diff --git a/.build.yml b/.build.yml new file mode 100644 index 0000000..eca9ae8 --- /dev/null +++ b/.build.yml @@ -0,0 +1,8 @@ +kind: pipeline +type: exec +name: XT Toolchain Pipeline + +steps: +- name: Compile XT Toolchain + commands: + - sh build-linux.sh diff --git a/README.md b/README.md index cd5f0d1..1151226 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,29 @@ ## XT Toolchain -This is an the XT toolchain based on MinGW-W64. It currently supports C and C++, and provides -a variety of tools. It can be used to build both Windows and XT software, including XT OS. +This is a LLVM/Clang/LLD based mingw-w64 toolchain. It currently supports C and C++, and provides +a variety of tools including IDL, message and resource compilers. The XT Toolchain is also the +official build environment for compiling XT software, including the XT OS. Currently, it is +targeted at Linux host only, however it should be possible to build it in MSYS2 as well. -This repository contains 2 branches: - * gnu-toolchain: This is the GCC/Binutils based mingw-w64 toolchain. - * llvm-toolchain: This is the LLVM/Clang/LLD based mingw-w64 toolchain. +Benefits of a LLVM based MinGW toolchain are: + * Single toolchain targeting all architectures (i686, x86_64, armv7 and aarch64), + * Support for generating debug info in PDB format, + * Support for targeting ARM/AARCH64 architectures and ability to produce Windows ARM binaries. + +This software includes: + * CMake + * LLVM + * Make + * Mingw-w64 + * NASM + * Ninja + * Wine + +This software is based on [LLVM MinGW Toolchain](https://github.com/mstorsjo/llvm-mingw). ## Licensing -The XTchain project includes the scripts for building and assembling a toolchain as well as other -scripts and wrappers. These are licensed under the GPLv3 license. It covers only mentioned -components that are provided by XTchain directly. For more information on that, refer to +The XTchain project includes the scripts for building and assembling a toolchain as well as wrappers +for LLVM tools and environmental shell. These are licensed under the GPLv3 license. It covers only +mentioned components that are provided by XTchain directly. For more information on that, refer to the COPYING.md file. The final pre-built toolchain is covered by the licenses of the individual, external projects. The full list of software incorporated into this toolchain is available in the -README.md file in the corresponding branch. +README.md file. diff --git a/build-linux.sh b/build-linux.sh new file mode 100755 index 0000000..255e8de --- /dev/null +++ b/build-linux.sh @@ -0,0 +1,709 @@ +#!/bin/bash +# PROJECT: XTchain +# LICENSE: See the COPYING.md in the top level directory +# FILE: build-linux.sh +# DESCRIPTION: Toolchain building and assembly script +# DEVELOPERS: Rafal Kupiec + + +# Working Directories +BINDIR="$(pwd)/binaries" +PCHDIR="$(pwd)/patches" +SRCDIR="$(pwd)/sources" +WRKDIR="$(pwd)" + +# Architecture Settings +ARCHS="aarch64 armv7 i686 x86_64" +GENERIC="generic-w64-mingw32" + +# CMake Settings +CMAKEDIR="${SRCDIR}/cmake" +CMAKETAG="v3.23.1" +CMAKEVCS="https://gitlab.kitware.com/cmake/cmake.git" + +# LLVM Settings +LLVMDIR="${SRCDIR}/llvm" +LLVMTAG="llvmorg-11.0.0" +LLVMVCS="https://github.com/llvm/llvm-project.git" + +# Make Settings +MAKEDIR="${SRCDIR}/make" +MAKETAG="4.3" +MAKEVCS="git://git.savannah.gnu.org/make" + +# Mingw-w64 Settings +MINGWDIR="${SRCDIR}/mingw-w64" +MINGWLIB="ucrt" +MINGWTAG="v8.0.0" +MINGWNTV="0x601" +MINGWVCS="https://github.com/mirror/mingw-w64.git" + +# NASM Settings +NASMDIR="${SRCDIR}/nasm" +NASMTAG="nasm-2.15.05" +NASMVCS="https://github.com/netwide-assembler/nasm.git" + +# Ninja Settings +NINJADIR="${SRCDIR}/ninja" +NINJATAG="v1.11.0" +NINJAVCS="https://github.com/ninja-build/ninja.git" + +# Wine Settings +WINEDIR="${SRCDIR}/wine" +WINETAG="wine-7.9" +WINEVCS="git://source.winehq.org/git/wine.git" + + +# This function applies a patches to the 3rd party project +apply_patches() +{ + local PACKAGE="${1}" + local VERSION="${2}" + if [ -d "${PCHDIR}/${PACKAGE}/${VERSION}" ]; then + PATCHES="$(find ${PCHDIR}/${PACKAGE}/${VERSION} -name '*.diff' -o -name '*.patch' | sort -n)" + echo ">>> Applying custom patches ..." + for PATCH in ${PATCHES}; do + if [ -f "${PATCH}" ] && [ -r "${PATCH}" ]; then + for PREFIX in {0..5}; do + if patch -i${PATCH} -p${PREFIX} --silent --dry-run >/dev/null; then + patch -i${PATCH} -p${PREFIX} --silent + echo ">>> Patch ${PATCH} applied ..." + break; + elif [ ${PREFIX} -ge 5 ]; then + echo "Patch ${PATCH} does not fit. Failed applying patch ..." + return 1 + fi + done + fi + done + fi +} + +# This function compiles and installs CMAKE +cmake_build() +{ + echo ">>> Building CMAKE ..." + [ -z ${CLEAN} ] || rm -rf ${CMAKEDIR}/build-${GENERIC} + mkdir -p ${CMAKEDIR}/build-${GENERIC} + cd ${CMAKEDIR}/build-${GENERIC} + ../bootstrap \ + --prefix=${BINDIR} \ + --parallel=${CORES} \ + -- -DCMAKE_USE_OPENSSL=OFF + make -j${CORES} + make install + cd ${WRKDIR} +} + +# This function downloads CMAKE from VCS +cmake_fetch() +{ + if [ ! -d ${CMAKEDIR} ]; then + echo ">>> Downloading CMAKE ..." + git clone ${CMAKEVCS} ${CMAKEDIR} + cd ${CMAKEDIR} + git checkout tags/${CMAKETAG} + apply_patches ${CMAKEDIR##*/} ${CMAKETAG} + cd ${WRKDIR} + fi +} + +# This function compiles and install LLVM +llvm_build() +{ + echo ">>> Building LLVM ..." + [ -z ${CLEAN} ] || rm -rf ${LLVMDIR}/llvm/build + LLVM_ARCHS=() + for ARCH in ${ARCHS}; do + case ${ARCH} in + "aarch64") + LLVM_ARCHS+=( "AArch64" ) + ;; + "armv7") + LLVM_ARCHS+=( "ARM" ) + ;; + "i686"|"x86_64") + LLVM_ARCHS+=( "X86" ) + ;; + esac + done + LLVM_ARCHS=( $(for ARCH in ${LLVM_ARCHS[@]}; do echo ${ARCH}; done | sort -u) ) + cd ${LLVMDIR}/llvm/tools + for UTIL in clang lld lldb; do + if [ ! -e ${UTIL} ]; then + ln -sf ../../${UTIL} . + fi + done + mkdir -p ${LLVMDIR}/llvm/build + cd ${LLVMDIR}/llvm/build + cmake \ + -DCMAKE_BUILD_TYPE="Release" \ + -DCMAKE_INSTALL_PREFIX=${BINDIR} \ + -DLLDB_ENABLE_CURSES=FALSE \ + -DLLDB_ENABLE_LIBEDIT=FALSE \ + -DLLDB_ENABLE_LUA=FALSE \ + -DLLDB_ENABLE_PYTHON=FALSE \ + -DLLDB_INCLUDE_TESTS=FALSE \ + -DLLVM_ENABLE_ASSERTIONS=FALSE \ + -DLLVM_INSTALL_TOOLCHAIN_ONLY=TRUE \ + -DLLVM_TARGETS_TO_BUILD="$(echo ${LLVM_ARCHS[@]} | tr ' ' ';')" \ + -DLLVM_TOOLCHAIN_TOOLS="clang;llvm-addr2line;llvm-ar;llvm-as;llvm-cov;llvm-cvtres;llvm-dlltool;llvm-nm;llvm-objdump;llvm-objcopy;llvm-pdbutil;llvm-profdata;llvm-ranlib;llvm-rc;llvm-readobj;llvm-strings;llvm-strip;llvm-symbolizer" \ + .. + make -j${CORES} install/strip + cd ${WRKDIR} +} + +# This function compiles and install LIBCXX +llvm_build_libcxx() +{ + echo ">>> Building LLVM libraries (libcxx) ..." + for ARCH in ${ARCHS}; do + [ -z ${CLEAN} ] || rm -rf ${LLVMDIR}/libcxx/build-${ARCH} + mkdir -p ${LLVMDIR}/libcxx/build-${ARCH} + cd ${LLVMDIR}/libcxx/build-${ARCH} + cmake \ + -DCMAKE_BUILD_TYPE="Release" \ + -DCMAKE_INSTALL_PREFIX=${BINDIR}/${ARCH}-w64-mingw32 \ + -DCMAKE_AR="${BINDIR}/bin/llvm-ar" \ + -DCMAKE_C_COMPILER="${BINDIR}/bin/${ARCH}-w64-mingw32-clang" \ + -DCMAKE_C_COMPILER_WORKS=1 \ + -DCMAKE_CXX_COMPILER="${BINDIR}/bin/${ARCH}-w64-mingw32-clang++" \ + -DCMAKE_CXX_COMPILER_WORKS=1 \ + -DCMAKE_CROSSCOMPILING=TRUE \ + -DCMAKE_RANLIB="${BINDIR}/bin/llvm-ranlib" \ + -DCMAKE_SHARED_LINKER_FLAGS="-lunwind" \ + -DCMAKE_SYSTEM_NAME="Windows" \ + -DLLVM_PATH="${LLVMDIR}/llvm" \ + -DLIBCXX_CXX_ABI="libcxxabi" \ + -DLIBCXX_CXX_ABI_INCLUDE_PATHS="../../libcxxabi/include" \ + -DLIBCXX_CXX_ABI_LIBRARY_PATH="../../libcxxabi/build-${ARCH}/lib" \ + -DLIBCXX_ENABLE_ABI_LINKER_SCRIPT=FALSE \ + -DLIBCXX_ENABLE_EXCEPTIONS=TRUE \ + -DLIBCXX_ENABLE_EXPERIMENTAL_LIBRARY=FALSE \ + -DLIBCXX_ENABLE_FILESYSTEM=FALSE \ + -DLIBCXX_ENABLE_MONOTONIC_CLOCK=TRUE \ + -DLIBCXX_ENABLE_SHARED=TRUE \ + -DLIBCXX_ENABLE_STATIC=TRUE \ + -DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=TRUE \ + -DLIBCXX_ENABLE_THREADS=TRUE \ + -DLIBCXX_HAS_WIN32_THREAD_API=TRUE \ + -DLIBCXX_HAVE_CXX_ATOMICS_WITHOUT_LIB=TRUE \ + -DLIBCXX_INCLUDE_TESTS=FALSE \ + -DLIBCXX_INSTALL_HEADERS=TRUE \ + -DLIBCXX_LIBDIR_SUFFIX="" \ + -DLIBCXX_USE_COMPILER_RT=TRUE \ + .. + make -j${CORES} + make install + ${BINDIR}/bin/llvm-ar qcsL \ + ${BINDIR}/${ARCH}-w64-mingw32/lib/libc++.dll.a \ + ${BINDIR}/${ARCH}-w64-mingw32/lib/libunwind.dll.a + ${BINDIR}/bin/llvm-ar qcsL \ + ${BINDIR}/${ARCH}-w64-mingw32/lib/libc++.a \ + ${BINDIR}/${ARCH}-w64-mingw32/lib/libunwind.a + if [ ! -e ${BINDIR}/${ARCH}-w64-mingw32/bin ]; then + mkdir -p ${BINDIR}/${ARCH}-w64-mingw32/bin + fi + cp lib/libc++.dll ${BINDIR}/${ARCH}-w64-mingw32/bin/ + done + cd ${WRKDIR} +} + +# This function compiles LIBCXXABI +llvm_build_libcxxabi() +{ + echo ">>> Building LLVM libraries (libcxxabi) ..." + for ARCH in ${ARCHS}; do + [ -z ${CLEAN} ] || rm -rf ${LLVMDIR}/libcxxabi/build-${ARCH} + mkdir -p ${LLVMDIR}/libcxxabi/build-${ARCH} + cd ${LLVMDIR}/libcxxabi/build-${ARCH} + cmake \ + -DCMAKE_BUILD_TYPE="Release" \ + -DCMAKE_INSTALL_PREFIX=${BINDIR}/${ARCH}-w64-mingw32 \ + -DCMAKE_AR="${BINDIR}/bin/llvm-ar" \ + -DCMAKE_C_COMPILER="${BINDIR}/bin/${ARCH}-w64-mingw32-clang" \ + -DCMAKE_C_COMPILER_WORKS=1 \ + -DCMAKE_CROSSCOMPILING=TRUE \ + -DCMAKE_CXX_COMPILER="${BINDIR}/bin/${ARCH}-w64-mingw32-clang++" \ + -DCMAKE_CXX_COMPILER_WORKS=1 \ + -DCMAKE_CXX_FLAGS="-D_LIBCPP_HAS_THREAD_API_WIN32 -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS" \ + -DCMAKE_RANLIB="${BINDIR}/bin/llvm-ranlib" \ + -DCMAKE_SYSTEM_NAME="Windows" \ + -DLLVM_PATH="${LLVMDIR}/llvm" \ + -DLIBCXXABI_ENABLE_EXCEPTIONS=TRUE \ + -DLIBCXXABI_ENABLE_NEW_DELETE_DEFINITIONS=FALSE \ + -DLIBCXXABI_ENABLE_SHARED=FALSE \ + -DLIBCXXABI_ENABLE_THREADS=TRUE \ + -DLIBCXXABI_LIBCXX_INCLUDES="../../libcxx/include" \ + -DLIBCXXABI_LIBDIR_SUFFIX="" \ + -DLIBCXXABI_TARGET_TRIPLE="${ARCH}-w64-mingw32" \ + -DLIBCXXABI_USE_COMPILER_RT=TRUE \ + .. + make -j${CORES} + done + cd ${WRKDIR} +} + +# This function compiles and installs LIBUNWIND +llvm_build_libunwind() +{ + echo ">>> Building LLVM libraries (libunwind) ..." + for ARCH in ${ARCHS}; do + [ -z ${CLEAN} ] || rm -rf ${LLVMDIR}/libunwind/build-${ARCH} + mkdir -p ${LLVMDIR}/libunwind/build-${ARCH} + cd ${LLVMDIR}/libunwind/build-${ARCH} + cmake \ + -DCMAKE_BUILD_TYPE="Release" \ + -DCMAKE_INSTALL_PREFIX=${BINDIR}/${ARCH}-w64-mingw32 \ + -DCMAKE_AR="${BINDIR}/bin/llvm-ar" \ + -DCMAKE_C_COMPILER="${BINDIR}/bin/${ARCH}-w64-mingw32-clang" \ + -DCMAKE_C_COMPILER_WORKS=1 \ + -DCMAKE_C_FLAGS="-Wno-dll-attribute-on-redeclaration" \ + -DCMAKE_CROSSCOMPILING=TRUE \ + -DCMAKE_CXX_COMPILER="${BINDIR}/bin/${ARCH}-w64-mingw32-clang++" \ + -DCMAKE_CXX_COMPILER_WORKS=1 \ + -DCMAKE_CXX_FLAGS="-Wno-dll-attribute-on-redeclaration" \ + -DCMAKE_SYSTEM_NAME="Windows" \ + -DCMAKE_RANLIB="${BINDIR}/bin/llvm-ranlib" \ + -DLLVM_COMPILER_CHECKED=TRUE \ + -DLLVM_PATH="${LLVMDIR}/llvm" \ + -DLIBUNWIND_ENABLE_CROSS_UNWINDING=FALSE \ + -DLIBUNWIND_ENABLE_SHARED=TRUE \ + -DLIBUNWIND_ENABLE_STATIC=TRUE \ + -DLIBUNWIND_ENABLE_THREADS=TRUE \ + -DLIBUNWIND_USE_COMPILER_RT=TRUE \ + .. + make -j${CORES} + make install + if [ ! -e ${BINDIR}/${ARCH}-w64-mingw32/bin ]; then + mkdir -p ${BINDIR}/${ARCH}-w64-mingw32/bin + fi + cp lib/libunwind.dll ${BINDIR}/${ARCH}-w64-mingw32/bin/ + done + cd ${WRKDIR} +} + +# This function compiles and install LLVM runtime +llvm_build_runtime() +{ + echo ">>> Building LLVM compiler runtime ..." + for ARCH in ${ARCHS}; do + [ -z ${CLEAN} ] || rm -rf ${LLVMDIR}/compiler-rt/build-${ARCH} + mkdir -p ${LLVMDIR}/compiler-rt/build-${ARCH} + cd ${LLVMDIR}/compiler-rt/build-${ARCH} + case ${ARCH} in + "armv7") + BARCH="armv7" + LARCH="arm" + ;; + "i686") + if [ ! -e ${BINDIR}/i386-w64-mingw32 ]; then + ln -sf i686-w64-mingw32 ${BINDIR}/i386-w64-mingw32 + fi + BARCH="i386" + LARCH="i386" + ;; + *) + BARCH="${ARCH}" + LARCH="${ARCH}" + ;; + esac + cmake \ + -DCMAKE_BUILD_TYPE="Release" \ + -DCMAKE_INSTALL_PREFIX=${BINDIR}/${ARCH}-w64-mingw32 \ + -DCMAKE_AR="${BINDIR}/bin/llvm-ar" \ + -DCMAKE_C_COMPILER="${BINDIR}/bin/${ARCH}-w64-mingw32-clang" \ + -DCMAKE_C_COMPILER_TARGET="${BARCH}-windows-gnu" \ + -DCMAKE_C_COMPILER_WORKS=1 \ + -DCMAKE_CXX_COMPILER="${BINDIR}/bin/${ARCH}-w64-mingw32-clang++" \ + -DCMAKE_CXX_COMPILER_WORKS=1 \ + -DCMAKE_RANLIB="${BINDIR}/bin/llvm-ranlib" \ + -DCMAKE_SYSTEM_NAME="Windows" \ + -DCOMPILER_RT_DEFAULT_TARGET_ONLY=TRUE \ + -DCOMPILER_RT_USE_BUILTINS_LIBRARY=TRUE \ + -DSANITIZER_CXX_ABI=libc++ \ + ../lib/builtins + make -j${CORES} + mkdir -p ${BINDIR}/lib/clang/${LLVMTAG#*-}/lib/windows + for LIB in lib/windows/libclang_rt.*.a; do + cp ${LIB} ${BINDIR}/lib/clang/${LLVMTAG#*-}/lib/windows/$(basename ${LIB} | sed s/${BARCH}/${LARCH}/) + done + done + cd ${WRKDIR} +} + +# This function downloads LLVM from VCS +llvm_fetch() +{ + if [ ! -d ${LLVMDIR} ]; then + echo ">>> Downloading LLVM ..." + git clone ${LLVMVCS} ${LLVMDIR} + cd ${LLVMDIR} + git checkout tags/${LLVMTAG} + apply_patches ${LLVMDIR##*/} ${LLVMTAG##*-} + cd ${WRKDIR} + fi +} + +# This function compiles and install MAKE +make_build() +{ + echo ">>> Building Make ..." + [ -z ${CLEAN} ] || rm -rf ${MAKEDIR}/build + cd ${MAKEDIR} + ./bootstrap + sed -i "s/-Werror//" maintMakefile + mkdir -p ${MAKEDIR}/build + cd ${MAKEDIR}/build + ../configure \ + --prefix=${BINDIR} \ + --disable-dependency-tracking \ + --disable-silent-rules \ + --program-prefix=g \ + --without-guile + make -j${CORES} + make install + if [ ! -e ${BINDIR}/bin/make ]; then + ln -sf gmake ${BINDIR}/bin/make + fi + cd ${WRKDIR} +} + +# This function downloads MAKE from VCS +make_fetch() +{ + if [ ! -d ${MAKEDIR} ]; then + echo ">>> Downloading Make ..." + git clone ${MAKEVCS} ${MAKEDIR} + cd ${MAKEDIR} + git checkout tags/${MAKETAG} + apply_patches ${MAKEDIR##*/} ${MAKETAG} + cd ${WRKDIR} + fi +} + +# This function compiles and installs MINGW CRT +mingw_build_crt() +{ + for ARCH in ${ARCHS}; do + echo ">>> Building Mingw-W64 (CRT) for ${ARCH} ..." + [ -z ${CLEAN} ] || rm -rf ${MINGWDIR}/mingw-w64-crt/build-${ARCH} + mkdir -p ${MINGWDIR}/mingw-w64-crt/build-${ARCH} + cd ${MINGWDIR}/mingw-w64-crt/build-${ARCH} + case ${ARCH} in + "aarch64") + FLAGS="--disable-lib32 --disable-lib64 --enable-libarm64" + ;; + "armv7") + FLAGS="--disable-lib32 --disable-lib64 --enable-libarm32" + ;; + "i686") + FLAGS="--enable-lib32 --disable-lib64" + ;; + "x86_64") + FLAGS="--disable-lib32 --enable-lib64" + ;; + esac + ORIGPATH="${PATH}" + PATH="${BINDIR}/bin:${PATH}" + ../configure \ + --host=${ARCH}-w64-mingw32 \ + --prefix=${BINDIR}/${ARCH}-w64-mingw32 \ + --with-sysroot=${BINDIR} \ + --with-default-msvcrt=${MINGWLIB} \ + ${FLAGS} + make -j${CORES} + make install + PATH="${ORIGPATH}" + done + cd ${WRKDIR} +} + +# This function compiles and installs MINGW headers +mingw_build_headers() +{ + echo ">>> Building Mingw-W64 (headers) ..." + [ -z ${CLEAN} ] || rm -rf ${MINGWDIR}/mingw-w64-headers/build-${GENERIC} + mkdir -p ${MINGWDIR}/mingw-w64-headers/build-${GENERIC} + cd ${MINGWDIR}/mingw-w64-headers/build-${GENERIC} + ../configure \ + --prefix=${BINDIR}/${GENERIC} \ + --enable-idl \ + --with-default-msvcrt=${MINGWLIB} \ + --with-default-win32-winnt=${MINGWNTV} + make -j${CORES} + make install + for ARCH in ${ARCHS}; do + mkdir -p ${BINDIR}/${ARCH}-w64-mingw32 + if [ ! -e ${BINDIR}/${ARCH}-w64-mingw32/include ]; then + ln -sfn ../${GENERIC}/include ${BINDIR}/${ARCH}-w64-mingw32/include + fi + done + cd ${WRKDIR} +} + +# This function compiles and install MINGW libraries +mingw_build_libs() +{ + for LIB in libmangle winpthreads winstorecompat; do + echo ">>> Building Mingw-w64 (libs) for ${ARCH} ..." + for ARCH in ${ARCHS}; do + [ -z ${CLEAN} ] || rm -rf ${MINGWDIR}/mingw-w64-libraries/${LIB}/build-${ARCH} + mkdir -p ${MINGWDIR}/mingw-w64-libraries/${LIB}/build-${ARCH} + cd ${MINGWDIR}/mingw-w64-libraries/${LIB}/build-${ARCH} + ORIGPATH="${PATH}" + PATH="${BINDIR}/bin:${PATH}" + ../configure \ + --host=${ARCH}-w64-mingw32 \ + --prefix=${BINDIR}/${ARCH}-w64-mingw32 \ + --libdir=${BINDIR}/${ARCH}-w64-mingw32/lib + make -j${CORES} + make install + PATH="${ORIGPATH}" + done + done + cd ${WRKDIR} +} + +# This function compiles and installs MINGW tools +mingw_build_tools() +{ + for TOOL in gendef genidl genlib genpeimg widl; do + for ARCH in ${ARCHS}; do + echo ">>> Building Mingw-w64 (tools) for ${ARCH} ..." + [ -z ${CLEAN} ] || rm -rf ${MINGWDIR}/mingw-w64-tools/${TOOL}/build-${ARCH} + mkdir -p ${MINGWDIR}/mingw-w64-tools/${TOOL}/build-${ARCH} + cd ${MINGWDIR}/mingw-w64-tools/${TOOL}/build-${ARCH} + ../configure \ + --target=${ARCH}-w64-mingw32 \ + --prefix=${BINDIR} + make -j${CORES} + make install + if [ -e ${BINDIR}/bin/${TOOL} ]; then + mv ${BINDIR}/bin/${TOOL} ${BINDIR}/bin/${ARCH}-w64-mingw32-${TOOL} + fi + done + done + cd ${WRKDIR} +} + +# This function downloads MINGW from VCS +mingw_fetch() +{ + if [ ! -d ${MINGWDIR} ]; then + echo ">>> Downloading MinGW-w64 ..." + git clone ${MINGWVCS} ${MINGWDIR} + cd ${MINGWDIR} + git checkout tags/${MINGWTAG} + apply_patches ${MINGWDIR##*/} ${MINGWTAG} + cd ${WRKDIR} + fi +} + +# This function compiles and installs NASM +nasm_build() +{ + cd ${NASMDIR} + ./autogen.sh + ./configure + make -j${CORES} + install nasm ndisasm ${BINDIR}/bin/ + cd ${WRKDIR} +} + +# This function downloads NASM from VCS +nasm_fetch() +{ + if [ ! -d ${NASMDIR} ]; then + echo ">>> Downloading NASM ..." + git clone ${NASMVCS} ${NASMDIR} + cd ${NASMDIR} + git checkout tags/${NASMTAG} + apply_patches ${NASMDIR##*/} ${NASMTAG##*-} + cd ${WRKDIR} + fi +} + +# This function compiles and installs NINJA +ninja_build() +{ + echo ">>> Building NINJA ..." + [ -z ${CLEAN} ] || rm -rf ${NINJADIR}/build-${GENERIC} + mkdir -p ${NINJADIR}/build-${GENERIC} + cd ${NINJADIR}/build-${GENERIC} + ../configure.py --bootstrap + install ninja ${BINDIR}/bin/ + cd ${WRKDIR} +} + +# This function downloads NINJA from VCS +ninja_fetch() +{ + if [ ! -d ${NINJADIR} ]; then + echo ">>> Downloading NINJA ..." + git clone ${NINJAVCS} ${NINJADIR} + cd ${NINJADIR} + git checkout tags/${NINJATAG} + apply_patches ${NINJADIR##*/} ${NINJATAG} + cd ${WRKDIR} + fi +} + +# This function compiles and install WINE tools +wine_build() +{ + echo ">>> Building Wine ..." + mkdir -p ${WINEDIR}/build + cd ${WINEDIR}/build + ../configure \ + -enable-win64 \ + --without-x + for TOOL in winedump wmc wrc; do + make -j${CORES} tools/${TOOL}/all + cp tools/${TOOL}/${TOOL} ${BINDIR}/bin/ + for ARCH in ${ARCHS}; do + if [ ! -e ${BINDIR}/bin/${ARCH}-w64-mingw32-${TOOL} ]; then + ln -sf ${TOOL} ${BINDIR}/bin/${ARCH}-w64-mingw32-${TOOL} + fi + done + done + cd ${WRKDIR} +} + +# This function downloads WINE from VCS +wine_fetch() +{ + if [ ! -d ${WINEDIR} ]; then + echo ">>> Downloading WINE ..." + git clone ${WINEVCS} ${WINEDIR} + cd ${WINEDIR} + git checkout tags/${WINETAG} + apply_patches ${WINEDIR##*/} ${WINETAG##*-} + cd ${WRKDIR} + fi +} + +# This function installs XTCHAIN scripts, wrappers and symlinks +xtchain_build() +{ + echo ">>> Building XTchain ..." + mkdir -p ${BINDIR}/bin + mkdir -p ${BINDIR}/lib/xtchain + mkdir -p ${BINDIR}/${GENERIC}/bin + cp ${WRKDIR}/scripts/*-wrapper ${BINDIR}/${GENERIC}/bin + for ARCH in ${ARCHS}; do + for EXEC in c++ c11 c99 cc clang clang++ g++ gcc; do + ln -sf ../${GENERIC}/bin/clang-target-wrapper ${BINDIR}/bin/${ARCH}-w64-mingw32-${EXEC} + done + for EXEC in addr2line ar as nm objcopy pdbutil ranlib rc strings strip; do + ln -sf llvm-${EXEC} ${BINDIR}/bin/${ARCH}-w64-mingw32-${EXEC} + done + for EXEC in dlltool ld objdump; do + ln -sf ../${GENERIC}/bin/${EXEC}-wrapper ${BINDIR}/bin/${ARCH}-w64-mingw32-${EXEC} + done + for EXEC in windres xtcspecc; do + if [ ! -e ${BINDIR}/bin/${EXEC} ]; then + gcc ${WRKDIR}/tools/${EXEC}.c -o ${BINDIR}/bin/${EXEC} + fi + ln -sf ${EXEC} ${BINDIR}/bin/${ARCH}-w64-mingw32-${EXEC} + done + done + cp ${WRKDIR}/scripts/xtclib ${BINDIR}/lib/xtchain/ + cp ${WRKDIR}/scripts/xtchain ${BINDIR}/ +} + + +# Exit immediately on any failure +set -e + +# Check if script launched as root +if [ "$(whoami)" = "root" ]; then + echo "This script cannot be run as root!" + exit 1 +fi + +# Check number of CPU cores available +: ${CORES:=$(sysctl -n hw.ncpu 2>/dev/null)} +: ${CORES:=$(nproc 2>/dev/null)} +: ${CORES:=1} + +# Create working directories +mkdir -p ${BINDIR} +mkdir -p ${SRCDIR} + +# XTchain +xtchain_build + +# Download LLVM +llvm_fetch + +# Build and install LLVM +llvm_build + +# Download NASM +nasm_fetch + +# Build and install NASM +nasm_build + +# Download Mingw-W64 +mingw_fetch + +# Build and install Mingw-W64 headers +mingw_build_headers + +# Build and install Mingw-W64 CRT +mingw_build_crt + +# Build and install LLVM compiler runtime +llvm_build_runtime + +# Build and install Mingw-W64 libraries +mingw_build_libs + +# Build and install Mingw-W64 tools +mingw_build_tools + +# Build LLVM libraries +llvm_build_libunwind +llvm_build_libcxxabi +llvm_build_libcxx + +# Download Wine +wine_fetch + +# Build and install Wine tools +wine_build + +# Download Make +make_fetch + +# Build and install Make +make_build + +# Download CMake +cmake_fetch + +# Build and install CMake +cmake_build + +# Download Ninja +ninja_fetch + +# Build and install Ninja +ninja_build + +# Remove unneeded files to save disk space +echo ">>> Removing unneeded files to save disk space ..." +rm -rf ${BINDIR}/{doc,include,share/{bash-completion,emacs,info,locale,man,vim}} +rm -rf ${BINDIR}/bin/{clang-{check,exdef-mapping,import-test,offload-*,rename,scan-deps},hmaptool,ld64.lld,wasm-ld} + +# Save XT Toolchain version +cd ${WRKDIR} +: ${XTCVER:=$(git describe --exact-match --tags 2>/dev/null)} +: ${XTCVER:=DEV} +echo "${XTCVER}" > ${BINDIR}/Version + +# Prepare archive +echo ">>> Creating toolchain archive ..." +tar -I 'zstd -19' -cpf xtchain-${XTCVER}-linux.tar.zst -C ${BINDIR} . diff --git a/patches/cmake/v3.23.1/001-add-xtc-version-suffix.patch b/patches/cmake/v3.23.1/001-add-xtc-version-suffix.patch new file mode 100644 index 0000000..7319cbf --- /dev/null +++ b/patches/cmake/v3.23.1/001-add-xtc-version-suffix.patch @@ -0,0 +1,13 @@ +diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake +index 818109f0b7..749377d193 100644 +--- a/Source/CMakeVersion.cmake ++++ b/Source/CMakeVersion.cmake +@@ -7,7 +7,7 @@ set(CMake_VERSION_IS_DIRTY 0) + + # Start with the full version number used in tags. It has no dev info. + set(CMake_VERSION +- "${CMake_VERSION_MAJOR}.${CMake_VERSION_MINOR}.${CMake_VERSION_PATCH}") ++ "${CMake_VERSION_MAJOR}.${CMake_VERSION_MINOR}.${CMake_VERSION_PATCH}-XTC") + if(DEFINED CMake_VERSION_RC) + set(CMake_VERSION "${CMake_VERSION}-rc${CMake_VERSION_RC}") + endif() diff --git a/patches/llvm/11.0.0/001-hardware-exception-handling.patch b/patches/llvm/11.0.0/001-hardware-exception-handling.patch new file mode 100644 index 0000000..3b0d37c --- /dev/null +++ b/patches/llvm/11.0.0/001-hardware-exception-handling.patch @@ -0,0 +1,1044 @@ +diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h +index d3fad58fcf5..77153f85a93 100644 +--- a/clang/include/clang/AST/Stmt.h ++++ b/clang/include/clang/AST/Stmt.h +@@ -1764,6 +1764,7 @@ public: + class LabelStmt : public ValueStmt { + LabelDecl *TheDecl; + Stmt *SubStmt; ++ bool SideEntry = false; + + public: + /// Build a label statement. +@@ -1799,6 +1800,9 @@ public: + static bool classof(const Stmt *T) { + return T->getStmtClass() == LabelStmtClass; + } ++ ++ bool isSideEntry() const { return SideEntry; } ++ void setSideEntry(bool SE) { SideEntry = SE; } + }; + + /// Represents an attribute applied to a statement. +diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def +index 70f68d664bb..1e87b455db8 100644 +--- a/clang/include/clang/Basic/LangOptions.def ++++ b/clang/include/clang/Basic/LangOptions.def +@@ -128,6 +128,7 @@ LANGOPT(ZVector , 1, 0, "System z vector extensions") + LANGOPT(Exceptions , 1, 0, "exception handling") + LANGOPT(ObjCExceptions , 1, 0, "Objective-C exceptions") + LANGOPT(CXXExceptions , 1, 0, "C++ exceptions") ++LANGOPT(EHAsynch , 1, 0, "C/C++ EH Asynch exceptions") + LANGOPT(DWARFExceptions , 1, 0, "dwarf exception handling") + LANGOPT(SjLjExceptions , 1, 0, "setjmp-longjump exception handling") + LANGOPT(SEHExceptions , 1, 0, "SEH .xdata exception handling") +diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td +index 966cb907b7e..dc7b38c254f 100644 +--- a/clang/include/clang/Driver/Options.td ++++ b/clang/include/clang/Driver/Options.td +@@ -889,6 +889,8 @@ def fno_crash_diagnostics : Flag<["-"], "fno-crash-diagnostics">, Group, Group; + defm cxx_exceptions: OptInFFlag<"cxx-exceptions", "Enable C++ exceptions">; ++def feh_asynch: Flag<["-"], "feh-asynch">, Group, ++ HelpText<"Enable EH Asynchronous exceptions">, Flags<[CC1Option]>; + def fcxx_modules : Flag <["-"], "fcxx-modules">, Group, + Flags<[DriverOption]>; + def fdebug_pass_arguments : Flag<["-"], "fdebug-pass-arguments">, Group; +@@ -1505,6 +1507,7 @@ def fno_common : Flag<["-"], "fno-common">, Group, Flags<[CC1Option]>, + def fno_constant_cfstrings : Flag<["-"], "fno-constant-cfstrings">, Group, + Flags<[CC1Option]>, + HelpText<"Disable creation of CodeFoundation-type constant strings">; ++def fno_eh_asynch: Flag<["-"], "fno-eh-asynch">, Group; + def fno_cxx_modules : Flag <["-"], "fno-cxx-modules">, Group, + Flags<[DriverOption]>; + def fno_diagnostics_fixit_info : Flag<["-"], "fno-diagnostics-fixit-info">, Group, +diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp +index 4bd00ece86a..7cc6089b711 100644 +--- a/clang/lib/AST/JSONNodeDumper.cpp ++++ b/clang/lib/AST/JSONNodeDumper.cpp +@@ -1444,6 +1444,7 @@ void JSONNodeDumper::VisitCaseStmt(const CaseStmt *CS) { + void JSONNodeDumper::VisitLabelStmt(const LabelStmt *LS) { + JOS.attribute("name", LS->getName()); + JOS.attribute("declId", createPointerRepresentation(LS->getDecl())); ++ attributeOnlyIfTrue("sideEntry", LS->isSideEntry()); + } + void JSONNodeDumper::VisitGotoStmt(const GotoStmt *GS) { + JOS.attribute("targetLabelDeclId", +diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp +index 5b0a0ac392c..0d5e9418fb7 100644 +--- a/clang/lib/AST/TextNodeDumper.cpp ++++ b/clang/lib/AST/TextNodeDumper.cpp +@@ -916,6 +916,8 @@ void TextNodeDumper::VisitWhileStmt(const WhileStmt *Node) { + + void TextNodeDumper::VisitLabelStmt(const LabelStmt *Node) { + OS << " '" << Node->getName() << "'"; ++ if (Node->isSideEntry()) ++ OS << " side_entry"; + } + + void TextNodeDumper::VisitGotoStmt(const GotoStmt *Node) { +diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp +index ad543ef86c1..3b69bb20b06 100644 +--- a/clang/lib/CodeGen/CGCleanup.cpp ++++ b/clang/lib/CodeGen/CGCleanup.cpp +@@ -194,6 +194,11 @@ void *EHScopeStack::pushCleanup(CleanupKind Kind, size_t Size) { + if (IsLifetimeMarker) + Scope->setLifetimeMarker(); + ++ // With Windows -EHa, Invoke llvm.seh.scope.begin() for EHCleanup ++ if (CGF->getLangOpts().EHAsynch && IsEHCleanup && ++ CGF->getTarget().getCXXABI().isMicrosoft()) ++ CGF->EmitSehCppScopeBegin(); ++ + return Scope->getCleanupBuffer(); + } + +@@ -759,14 +764,31 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { + if (Scope.isEHCleanup()) + cleanupFlags.setIsEHCleanupKind(); + ++ // Under -EHa, invoke seh.scope.end() to mark scope end before dtor ++ bool IsEHa = getLangOpts().EHAsynch && !Scope.isLifetimeMarker(); ++ const EHPersonality& Personality = EHPersonality::get(*this); + if (!RequiresNormalCleanup) { ++ // Mark CPP scope end for passed-by-value Arg temp ++ // per Windows ABI which is "normally" Cleanup in callee ++ if (IsEHa && getInvokeDest()) { ++ if (Personality.isMSVCXXPersonality()) ++ EmitSehCppScopeEnd(); ++ } + destroyOptimisticNormalEntry(*this, Scope); + EHStack.popCleanup(); + } else { + // If we have a fallthrough and no other need for the cleanup, + // emit it directly. +- if (HasFallthrough && !HasPrebranchedFallthrough && +- !HasFixups && !HasExistingBranches) { ++ if (HasFallthrough && !HasPrebranchedFallthrough && !HasFixups && ++ !HasExistingBranches) { ++ ++ // mark SEH scope end for fall-through flow ++ if (IsEHa && getInvokeDest()) { ++ if (Personality.isMSVCXXPersonality()) ++ EmitSehCppScopeEnd(); ++ else ++ EmitSehTryScopeEnd(); ++ } + + destroyOptimisticNormalEntry(*this, Scope); + EHStack.popCleanup(); +@@ -801,6 +823,14 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { + // should already be branched to it. + EmitBlock(NormalEntry); + ++ // intercept normal cleanup to mark SEH scope end ++ if (IsEHa) { ++ if (Personality.isMSVCXXPersonality()) ++ EmitSehCppScopeEnd(); ++ else ++ EmitSehTryScopeEnd(); ++ } ++ + // III. Figure out where we're going and build the cleanup + // epilogue. + +@@ -1248,11 +1278,18 @@ void CodeGenFunction::DeactivateCleanupBlock(EHScopeStack::stable_iterator C, + // to the current RunCleanupsScope. + if (C == EHStack.stable_begin() && + CurrentCleanupScopeDepth.strictlyEncloses(C)) { +- // If it's a normal cleanup, we need to pretend that the +- // fallthrough is unreachable. +- CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP(); +- PopCleanupBlock(); +- Builder.restoreIP(SavedIP); ++ // Per comment below, checking EHAsynch is not really necessary ++ // it's there to assure zero-impact w/o EHAsynch option ++ if (!Scope.isNormalCleanup() && getLangOpts().EHAsynch) ++ PopCleanupBlock(); ++ else ++ { ++ // If it's a normal cleanup, we need to pretend that the ++ // fallthrough is unreachable. ++ CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP(); ++ PopCleanupBlock(); ++ Builder.restoreIP(SavedIP); ++ } + return; + } + +@@ -1276,3 +1313,60 @@ void CodeGenFunction::EmitCXXTemporary(const CXXTemporary *Temporary, + pushDestroy(NormalAndEHCleanup, Ptr, TempType, destroyCXXObject, + /*useEHCleanup*/ true); + } ++ ++// Need to set "funclet" in OperandBundle properly for noThrow ++// intrinsic (see CGCall.cpp) ++static void EmitSehScope(CodeGenFunction &CGF, ++ llvm::FunctionCallee &SehCppScope) { ++ llvm::BasicBlock *InvokeDest = CGF.getInvokeDest(); ++ llvm::BasicBlock *BB = CGF.Builder.GetInsertBlock(); ++ assert(BB && InvokeDest); ++ llvm::BasicBlock *Cont = CGF.createBasicBlock("invoke.cont"); ++ SmallVector BundleList = ++ CGF.getBundlesForFunclet(SehCppScope.getCallee()); ++ if (CGF.CurrentFuncletPad) ++ BundleList.emplace_back("funclet", CGF.CurrentFuncletPad); ++ CGF.Builder.CreateInvoke(SehCppScope, Cont, InvokeDest, None, BundleList); ++ CGF.EmitBlock(Cont); ++} ++ ++// Invoke a llvm.seh.scope.begin at the beginning of a CPP scope for -EHa ++void CodeGenFunction::EmitSehCppScopeBegin() { ++ assert(getLangOpts().EHAsynch); ++ llvm::FunctionType *FTy = ++ llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false); ++ llvm::FunctionCallee SehCppScope = ++ CGM.CreateRuntimeFunction(FTy, "llvm.seh.scope.begin"); ++ EmitSehScope(*this, SehCppScope); ++} ++ ++// Invoke a llvm.seh.scope.end at the end of a CPP scope for -EHa ++// llvm.seh.scope.end is emitted before popCleanup, so it's "invoked" ++void CodeGenFunction::EmitSehCppScopeEnd() { ++ assert(getLangOpts().EHAsynch); ++ llvm::FunctionType *FTy = ++ llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false); ++ llvm::FunctionCallee SehCppScope = ++ CGM.CreateRuntimeFunction(FTy, "llvm.seh.scope.end"); ++ EmitSehScope(*this, SehCppScope); ++} ++ ++// Invoke a llvm.seh.try.begin at the beginning of a SEH scope for -EHa ++void CodeGenFunction::EmitSehTryScopeBegin() { ++ assert(getLangOpts().EHAsynch); ++ llvm::FunctionType* FTy = ++ llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false); ++ llvm::FunctionCallee SehCppScope = ++ CGM.CreateRuntimeFunction(FTy, "llvm.seh.try.begin"); ++ EmitSehScope(*this, SehCppScope); ++} ++ ++// Invoke a llvm.seh.try.end at the end of a SEH scope for -EHa ++void CodeGenFunction::EmitSehTryScopeEnd() { ++ assert(getLangOpts().EHAsynch); ++ llvm::FunctionType* FTy = ++ llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false); ++ llvm::FunctionCallee SehCppScope = ++ CGM.CreateRuntimeFunction(FTy, "llvm.seh.try.end"); ++ EmitSehScope(*this, SehCppScope); ++} +\ No newline at end of file +diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp +index bdf70252b5a..a4e4d7d8120 100644 +--- a/clang/lib/CodeGen/CGException.cpp ++++ b/clang/lib/CodeGen/CGException.cpp +@@ -39,6 +39,18 @@ static llvm::FunctionCallee getFreeExceptionFn(CodeGenModule &CGM) { + return CGM.CreateRuntimeFunction(FTy, "__cxa_free_exception"); + } + ++static llvm::FunctionCallee getSehTryBeginFn(CodeGenModule &CGM) { ++ llvm::FunctionType *FTy = ++ llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false); ++ return CGM.CreateRuntimeFunction(FTy, "llvm.seh.try.begin"); ++} ++ ++static llvm::FunctionCallee getSehTryEndFn(CodeGenModule &CGM) { ++ llvm::FunctionType *FTy = ++ llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false); ++ return CGM.CreateRuntimeFunction(FTy, "llvm.seh.try.end"); ++} ++ + static llvm::FunctionCallee getUnexpectedFn(CodeGenModule &CGM) { + // void __cxa_call_unexpected(void *thrown_exception); + +@@ -451,7 +463,7 @@ void CodeGenFunction::EmitStartEHSpec(const Decl *D) { + if (!FD) { + // Check if CapturedDecl is nothrow and create terminate scope for it. + if (const CapturedDecl* CD = dyn_cast_or_null(D)) { +- if (CD->isNothrow()) ++ if (CD->isNothrow() && !getLangOpts().EHAsynch /* !IsEHa */) + EHStack.pushTerminate(); + } + return; +@@ -463,7 +475,8 @@ void CodeGenFunction::EmitStartEHSpec(const Decl *D) { + ExceptionSpecificationType EST = Proto->getExceptionSpecType(); + if (isNoexceptExceptionSpec(EST) && Proto->canThrow() == CT_Cannot) { + // noexcept functions are simple terminate scopes. +- EHStack.pushTerminate(); ++ if (!getLangOpts().EHAsynch) // -EHa: HW exception still can occur ++ EHStack.pushTerminate(); + } else if (EST == EST_Dynamic || EST == EST_DynamicNone) { + // TODO: Revisit exception specifications for the MS ABI. There is a way to + // encode these in an object file but MSVC doesn't do anything with it. +@@ -540,7 +553,7 @@ void CodeGenFunction::EmitEndEHSpec(const Decl *D) { + if (!FD) { + // Check if CapturedDecl is nothrow and pop terminate scope for it. + if (const CapturedDecl* CD = dyn_cast_or_null(D)) { +- if (CD->isNothrow()) ++ if (CD->isNothrow() && !EHStack.empty()) + EHStack.popTerminate(); + } + return; +@@ -550,7 +563,8 @@ void CodeGenFunction::EmitEndEHSpec(const Decl *D) { + return; + + ExceptionSpecificationType EST = Proto->getExceptionSpecType(); +- if (isNoexceptExceptionSpec(EST) && Proto->canThrow() == CT_Cannot) { ++ if (isNoexceptExceptionSpec(EST) && Proto->canThrow() == CT_Cannot && ++ !EHStack.empty() /* possible empty when -EHa */) { + EHStack.popTerminate(); + } else if (EST == EST_Dynamic || EST == EST_DynamicNone) { + // TODO: Revisit exception specifications for the MS ABI. There is a way to +@@ -606,6 +620,9 @@ void CodeGenFunction::EnterCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) { + } else { + // No exception decl indicates '...', a catch-all. + CatchScope->setHandler(I, CGM.getCXXABI().getCatchAllTypeInfo(), Handler); ++ // Mark scope with SehTryBegin ++ if (getLangOpts().EHAsynch) ++ EmitRuntimeCallOrInvoke(getSehTryBeginFn(CGM)); + } + } + } +@@ -720,7 +737,7 @@ llvm::BasicBlock *CodeGenFunction::getInvokeDestImpl() { + // If exceptions are disabled/ignored and SEH is not in use, then there is no + // invoke destination. SEH "works" even if exceptions are off. In practice, + // this means that C++ destructors and other EH cleanups don't run, which is +- // consistent with MSVC's behavior. ++ // consistent with MSVC's behavior, except in the presence of -EHa + const LangOptions &LO = CGM.getLangOpts(); + if (!LO.Exceptions || LO.IgnoreExceptions) { + if (!LO.Borland && !LO.MicrosoftExt) +@@ -1610,7 +1627,23 @@ void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) { + JumpDest TryExit = getJumpDestInCurrentScope("__try.__leave"); + + SEHTryEpilogueStack.push_back(&TryExit); ++ ++ llvm::BasicBlock *TryBB = nullptr; ++ // IsEHa: emit an invoke to _seh_try_begin() runtime for -EHa ++ if (getLangOpts().EHAsynch) { ++ EmitRuntimeCallOrInvoke(getSehTryBeginFn(CGM)); ++ if (SEHTryEpilogueStack.size() == 1) // outermost only ++ TryBB = Builder.GetInsertBlock(); ++ } ++ + EmitStmt(S.getTryBlock()); ++ ++ // Volatilize all blocks in Try, till current insert point ++ if (TryBB) { ++ llvm::SmallPtrSet Visited; ++ VolatilizeTryBlocks(TryBB, Visited); ++ } ++ + SEHTryEpilogueStack.pop_back(); + + if (!TryExit.getBlock()->use_empty()) +@@ -1621,6 +1654,38 @@ void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) { + ExitSEHTryStmt(S); + } + ++// Recursively walk through blocks in a _try ++// and make all memory instructions volatile ++void CodeGenFunction::VolatilizeTryBlocks( ++ llvm::BasicBlock *BB, llvm::SmallPtrSet &V) { ++ if (BB == SEHTryEpilogueStack.back()->getBlock() /* end of Try */ || ++ !V.insert(BB).second /* already visited */ || ++ !BB->getParent() /* not emitted */ || BB->empty()) ++ return; ++ ++ if (!BB->isEHPad()) { ++ for (llvm::BasicBlock::iterator J = BB->begin(), JE = BB->end(); J != JE; ++ ++J) { ++ if (isa(J)) { ++ auto LI = cast(J); ++ LI->setVolatile(true); ++ } else if (isa(J)) { ++ auto SI = cast(J); ++ SI->setVolatile(true); ++ } else if (isa(J)) { ++ auto *MCI = cast(J); ++ MCI->setVolatile(llvm::ConstantInt::get(Builder.getInt1Ty(), 1)); ++ } ++ } ++ } ++ const llvm::Instruction *TI = BB->getTerminator(); ++ if (TI) { ++ unsigned N = TI->getNumSuccessors(); ++ for (unsigned I = 0; I < N; I++) ++ VolatilizeTryBlocks(TI->getSuccessor(I), V); ++ } ++} ++ + namespace { + struct PerformSEHFinally final : EHScopeStack::Cleanup { + llvm::Function *OutlinedFinally; +@@ -2101,6 +2166,12 @@ void CodeGenFunction::ExitSEHTryStmt(const SEHTryStmt &S) { + return; + } + ++ // IsEHa: emit an invoke _seh_try_end() to mark end of FT flow ++ if (getLangOpts().EHAsynch && Builder.GetInsertBlock()) { ++ llvm::FunctionCallee SehTryEnd = getSehTryEndFn(CGM); ++ EmitRuntimeCallOrInvoke(SehTryEnd); ++ } ++ + // Otherwise, we must have an __except block. + const SEHExceptStmt *Except = S.getExceptHandler(); + assert(Except && "__try must have __finally xor __except"); +diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp +index 672909849bb..32dca08b8cf 100644 +--- a/clang/lib/CodeGen/CGStmt.cpp ++++ b/clang/lib/CodeGen/CGStmt.cpp +@@ -606,6 +606,11 @@ void CodeGenFunction::LexicalScope::rescopeLabels() { + + void CodeGenFunction::EmitLabelStmt(const LabelStmt &S) { + EmitLabel(S.getDecl()); ++ ++ // IsEHa - emit eha.scope.begin if it's a side entry of a scope ++ if (getLangOpts().EHAsynch && S.isSideEntry()) ++ EmitSehCppScopeBegin(); ++ + EmitStmt(S.getSubStmt()); + } + +diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp +index 8ce488f35dd..4cc8304c455 100644 +--- a/clang/lib/CodeGen/CodeGenFunction.cpp ++++ b/clang/lib/CodeGen/CodeGenFunction.cpp +@@ -71,6 +71,7 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext) + shouldEmitLifetimeMarkers(CGM.getCodeGenOpts(), CGM.getLangOpts())) { + if (!suppressNewContext) + CGM.getCXXABI().getMangleContext().startNewFunction(); ++ EHStack.setCGF(this); + + SetFastMathFlags(CurFPFeatures); + SetFPModel(); +diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h +index d794f4f0fa8..1b5892c7459 100644 +--- a/clang/lib/CodeGen/CodeGenFunction.h ++++ b/clang/lib/CodeGen/CodeGenFunction.h +@@ -2784,6 +2784,11 @@ public: + void EmitCXXTemporary(const CXXTemporary *Temporary, QualType TempType, + Address Ptr); + ++ void EmitSehCppScopeBegin(); ++ void EmitSehCppScopeEnd(); ++ void EmitSehTryScopeBegin(); ++ void EmitSehTryScopeEnd(); ++ + llvm::Value *EmitLifetimeStart(uint64_t Size, llvm::Value *Addr); + void EmitLifetimeEnd(llvm::Value *Size, llvm::Value *Addr); + +@@ -3134,6 +3139,8 @@ public: + void EmitSEHLeaveStmt(const SEHLeaveStmt &S); + void EnterSEHTryStmt(const SEHTryStmt &S); + void ExitSEHTryStmt(const SEHTryStmt &S); ++ void VolatilizeTryBlocks(llvm::BasicBlock *BB, ++ llvm::SmallPtrSet &V); + + void pushSEHCleanup(CleanupKind kind, + llvm::Function *FinallyFunc); +diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp +index 4ae8ce7e5cc..5512c8fe88a 100644 +--- a/clang/lib/CodeGen/CodeGenModule.cpp ++++ b/clang/lib/CodeGen/CodeGenModule.cpp +@@ -593,6 +593,9 @@ void CodeGenModule::Release() { + llvm::DenormalMode::IEEE); + } + ++ if (LangOpts.EHAsynch) ++ getModule().addModuleFlag(llvm::Module::Warning, "eh-asynch", 1); ++ + // Emit OpenCL specific module metadata: OpenCL/SPIR version. + if (LangOpts.OpenCL) { + EmitOpenCLMetadata(); +diff --git a/clang/lib/CodeGen/EHScopeStack.h b/clang/lib/CodeGen/EHScopeStack.h +index 3a640d6117d..60fe9b3c9dd 100644 +--- a/clang/lib/CodeGen/EHScopeStack.h ++++ b/clang/lib/CodeGen/EHScopeStack.h +@@ -236,6 +236,9 @@ private: + /// The innermost EH scope on the stack. + stable_iterator InnermostEHScope; + ++ /// The CGF this Stack belong to ++ CodeGenFunction* CGF; ++ + /// The current set of branch fixups. A branch fixup is a jump to + /// an as-yet unemitted label, i.e. a label for which we don't yet + /// know the EH stack depth. Whenever we pop a cleanup, we have +@@ -263,7 +266,7 @@ private: + public: + EHScopeStack() : StartOfBuffer(nullptr), EndOfBuffer(nullptr), + StartOfData(nullptr), InnermostNormalCleanup(stable_end()), +- InnermostEHScope(stable_end()) {} ++ InnermostEHScope(stable_end()), CGF(nullptr) {} + ~EHScopeStack() { delete[] StartOfBuffer; } + + /// Push a lazily-created cleanup on the stack. +@@ -311,6 +314,8 @@ public: + std::memcpy(Buffer, Cleanup, Size); + } + ++ void setCGF(CodeGenFunction* inCGF) { CGF = inCGF; } ++ + /// Pops a cleanup scope off the stack. This is private to CGCleanup.cpp. + void popCleanup(); + +diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp +index 45c6cb6b2e0..a080c93356a 100644 +--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp ++++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp +@@ -131,7 +131,12 @@ public: + + /// MSVC needs an extra flag to indicate a catchall. + CatchTypeInfo getCatchAllTypeInfo() override { +- return CatchTypeInfo{nullptr, 0x40}; ++ // For -EHa catch(...) must handle HW exception ++ // Adjective = HT_IsStdDotDot (0x40), only catch C++ exceptions ++ if (getContext().getLangOpts().EHAsynch) ++ return CatchTypeInfo{nullptr, 0}; ++ else ++ return CatchTypeInfo{nullptr, 0x40}; + } + + bool shouldTypeidBeNullChecked(bool IsDeref, QualType SrcRecordTy) override; +diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp +index af4bcf951e6..71112783ac0 100644 +--- a/clang/lib/Driver/ToolChains/Clang.cpp ++++ b/clang/lib/Driver/ToolChains/Clang.cpp +@@ -415,6 +415,7 @@ static void addExceptionArgs(const ArgList &Args, types::ID InputType, + Args.ClaimAllArgs(options::OPT_fobjc_exceptions); + Args.ClaimAllArgs(options::OPT_fno_objc_exceptions); + Args.ClaimAllArgs(options::OPT_fcxx_exceptions); ++ Args.ClaimAllArgs(options::OPT_feh_asynch); + Args.ClaimAllArgs(options::OPT_fno_cxx_exceptions); + return; + } +@@ -423,6 +424,13 @@ static void addExceptionArgs(const ArgList &Args, types::ID InputType, + bool EH = Args.hasFlag(options::OPT_fexceptions, options::OPT_fno_exceptions, + false); + ++ bool EHa = ++ Args.hasFlag(options::OPT_feh_asynch, options::OPT_fno_eh_asynch, false); ++ if (EHa) { ++ CmdArgs.push_back("-feh-asynch"); ++ EH = true; ++ } ++ + // Obj-C exceptions are enabled by default, regardless of -fexceptions. This + // is not necessarily sensible, but follows GCC. + if (types::isObjC(InputType) && +@@ -6581,7 +6589,10 @@ void Clang::AddClangCLArgs(const ArgList &Args, types::ID InputType, + if (types::isCXX(InputType)) + CmdArgs.push_back("-fcxx-exceptions"); + CmdArgs.push_back("-fexceptions"); ++ if (EH.Asynch) ++ CmdArgs.push_back("-feh-asynch"); + } ++ + if (types::isCXX(InputType) && EH.Synch && EH.NoUnwindC) + CmdArgs.push_back("-fexternc-nounwind"); + +diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp +index 73114c6d76c..b58af0b68e7 100644 +--- a/clang/lib/Frontend/CompilerInvocation.cpp ++++ b/clang/lib/Frontend/CompilerInvocation.cpp +@@ -2819,6 +2819,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, + Opts.IgnoreExceptions = Args.hasArg(OPT_fignore_exceptions); + Opts.ObjCExceptions = Args.hasArg(OPT_fobjc_exceptions); + Opts.CXXExceptions = Args.hasArg(OPT_fcxx_exceptions); ++ Opts.EHAsynch = Args.hasArg(OPT_feh_asynch); + + // -ffixed-point + Opts.FixedPoint = +diff --git a/clang/lib/Sema/JumpDiagnostics.cpp b/clang/lib/Sema/JumpDiagnostics.cpp +index b34243edea3..304f059a032 100644 +--- a/clang/lib/Sema/JumpDiagnostics.cpp ++++ b/clang/lib/Sema/JumpDiagnostics.cpp +@@ -930,6 +930,9 @@ void JumpScopeChecker::CheckJump(Stmt *From, Stmt *To, SourceLocation DiagLoc, + if (!ToScopesWarning.empty()) { + S.Diag(DiagLoc, JumpDiagWarning); + NoteJumpIntoScopes(ToScopesWarning); ++ assert(isa(To)); ++ LabelStmt *Label = cast(To); ++ Label->setSideEntry(true); + } + + // Handle errors. +diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp +index a40c5499a6d..bfed22a1252 100644 +--- a/clang/lib/Serialization/ASTReaderStmt.cpp ++++ b/clang/lib/Serialization/ASTReaderStmt.cpp +@@ -185,11 +185,13 @@ void ASTStmtReader::VisitDefaultStmt(DefaultStmt *S) { + + void ASTStmtReader::VisitLabelStmt(LabelStmt *S) { + VisitStmt(S); ++ bool IsSideEntry = Record.readInt(); + auto *LD = readDeclAs(); + LD->setStmt(S); + S->setDecl(LD); + S->setSubStmt(Record.readSubStmt()); + S->setIdentLoc(readSourceLocation()); ++ S->setSideEntry(IsSideEntry); + } + + void ASTStmtReader::VisitAttributedStmt(AttributedStmt *S) { +diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp +index 0767b3a24bf..614fd45d193 100644 +--- a/clang/lib/Serialization/ASTWriterStmt.cpp ++++ b/clang/lib/Serialization/ASTWriterStmt.cpp +@@ -115,6 +115,7 @@ void ASTStmtWriter::VisitDefaultStmt(DefaultStmt *S) { + + void ASTStmtWriter::VisitLabelStmt(LabelStmt *S) { + VisitStmt(S); ++ Record.push_back(S->isSideEntry()); + Record.AddDeclRef(S->getDecl()); + Record.AddStmt(S->getSubStmt()); + Record.AddSourceLocation(S->getIdentLoc()); +diff --git a/clang/test/CodeGen/windows-seh-EHa-CppCatchDotDotDot.cpp b/clang/test/CodeGen/windows-seh-EHa-CppCatchDotDotDot.cpp +new file mode 100644 +index 00000000000..2928a610d3c +--- /dev/null ++++ b/clang/test/CodeGen/windows-seh-EHa-CppCatchDotDotDot.cpp +@@ -0,0 +1,58 @@ ++// RUN: %clang_cc1 -triple x86_64-windows -feh-asynch -fcxx-exceptions -fexceptions -fms-extensions -x c++ -Wno-implicit-function-declaration -S -emit-llvm %s -o - | FileCheck %s ++ ++// CHECK: define dso_local void @"?crash@@YAXH@Z ++// CHECK: invoke void @llvm.seh.try.begin() ++// CHECK: invoke void @llvm.seh.scope.begin() ++// CHECK: invoke void @llvm.seh.scope.end() ++ ++// CHECK: %[[dst:[0-9-]+]] = catchswitch within none [label %catch] unwind to caller ++// CHECK: %[[dst1:[0-9-]+]] = catchpad within %[[dst]] [i8* null, i32 0, i8* null] ++// CHECK: "funclet"(token %[[dst1]]) ++ ++// CHECK: invoke void @llvm.seh.try.begin() ++// CHECK: %[[src:[0-9-]+]] = load volatile i32, i32* %i ++// CHECK-NEXT: invoke void @"?crash@@YAXH@Z"(i32 %[[src]]) ++// CHECK: invoke void @llvm.seh.try.end() ++ ++// ***************************************************************************** ++// Abstract: Test CPP catch(...) under SEH -EHa option ++ ++void printf(...); ++int volatile *NullPtr = 0; ++void foo() { ++ *NullPtr = 0; ++} ++int *pt1, *pt2, *pt3; ++int g; ++void crash(int i) { ++ g = i; ++ try { ++ struct A { ++ A() { ++ printf(" in A ctor \n"); ++ if (g == 0) ++ *NullPtr = 0; ++ } ++ ~A() { ++ printf(" in A dtor \n"); ++ } ++ } ObjA; ++ if (i == 1) ++ *NullPtr = 0; ++ } catch (...) { ++ printf(" in catch(...) funclet \n"); ++ if (i == 1) ++ throw(i); ++ } ++} ++ ++int main() { ++ for (int i = 0; i < 2; i++) { ++ __try { ++ crash(i); ++ } __except (1) { ++ printf(" Test CPP unwind: in except handler i = %d \n", i); ++ } ++ } ++ return 0; ++} +diff --git a/clang/test/CodeGen/windows-seh-EHa-CppCondiTemps.cpp b/clang/test/CodeGen/windows-seh-EHa-CppCondiTemps.cpp +new file mode 100644 +index 00000000000..d588d6e2292 +--- /dev/null ++++ b/clang/test/CodeGen/windows-seh-EHa-CppCondiTemps.cpp +@@ -0,0 +1,129 @@ ++// RUN: %clang_cc1 -triple x86_64-windows -feh-asynch -fcxx-exceptions -fexceptions -fms-extensions -x c++ -Wno-implicit-function-declaration -S -emit-llvm %s -o - | FileCheck %s ++ ++// CHECK: define dso_local i32 @"?bar@@YAHHVB1@@VB2@@@Z" ++// CHECK: %coerce.dive1 = getelementptr inbounds %class.B2 ++// CHECK: %coerce.dive2 = getelementptr inbounds %class.B1 ++// ----- scope begin of two passed-by-value temps ++// CHECK: invoke void @llvm.seh.scope.begin() ++// CHECK: invoke void @llvm.seh.scope.begin() ++// CHECK: invoke void @llvm.seh.scope.end() ++// CHECK: call void @"??1B1@@QEAA@XZ" ++// CHECK: invoke void @llvm.seh.scope.end() ++// CHECK: call void @"??1B2@@QEAA@XZ" ++ ++// CHECK: define linkonce_odr dso_local void @"??1B2@@QEAA@XZ" ++// CHECK: %this.addr = alloca %class.B2* ++// ----- B1 scope begin without base ctor ++// CHECK: invoke void @llvm.seh.scope.begin() ++// CHECK: invoke void @llvm.seh.scope.end() ++// CHECK: call void @"??1B1@@QEAA@XZ" ++ ++// CHECK: define dso_local void @"?goo@@YA?AVB1@@H@Z" ++// CHECK: call %class.B2* @"??0B2@@QEAA@XZ"(%class.B2* %b2ingoo) ++// CHECK: invoke void @llvm.seh.scope.begin() ++// check: call void @llvm.memcpy ++// CHECK: invoke void @llvm.seh.scope.end() ++// CHECK: call void @"??1B2@@QEAA@XZ"(%class.B2* %b2ingoo) ++ ++// CHECK: define linkonce_odr dso_local %class.B2* @"??0B2@@QEAA@XZ" ++// CHECK: call %class.B1* @"??0B1@@QEAA@XZ"(%class.B1* ++// ----- scope begin of base ctor ++// CHECK: invoke void @llvm.seh.scope.begin() ++// CHECK: invoke void @llvm.seh.scope.end() ++// ----- B1 scope end without base dtor ++ ++// **************************************************************************** ++// Abstract: Test CPP Conditional-Expr & ABI Temps under SEH -EHa option ++ ++void printf(...); ++ ++int xxxx = 0; ++int* ptr; ++ ++int foo(int a) ++{ ++ return xxxx + a; ++} ++ ++class B1 { ++public: ++ int data = 90; ++ B1() { foo(data + 111); } ++ ~B1() { printf("in B1 Dtor \n"); } ++}; ++class B2 : public B1 { ++public: ++ B2() { foo(data + 222); } ++ ~B2() { printf("in B2 Dtor \n");; } ++}; ++class B3 : public B2 { ++public: ++ B3() { foo(data + 333); } ++ ~B3() { printf("in B3 Dtor \n");; } ++}; ++ ++int bar(int j, class B1 b1Bar, class B2 b2Bar) ++{ ++ int ww; ++ if ( j > 0) ++ ww = b1Bar.data; ++ else ++ ww = b2Bar.data; ++ return ww + *ptr; ++} ++ ++class B1 goo(int w) ++{ ++ class B2 b2ingoo; ++ b2ingoo.data += w; ++ return b2ingoo; ++} ++ ++// CHECK: define dso_local i32 @main() ++// CHECK: invoke void @llvm.seh.scope.begin() ++// --- beginning of conditional temp test ++// CHECK: invoke %class.B2* @"??0B2@@QEAA@XZ" ++// CHECK: invoke void @llvm.seh.scope.begin() ++// CHECK: invoke %class.B3* @"??0B3@@QEAA@XZ" ++// CHECK: invoke void @llvm.seh.scope.begin() ++// CHECK: invoke void @llvm.seh.scope.end() ++// CHECK: call void @"??1B3@@QEAA@XZ" ++// CHECK: invoke void @llvm.seh.scope.end() ++// CHECK: call void @"??1B2@@QEAA@XZ" ++// ----- end of conditional temp test ++ ++// ----- testing caller's passed-by-value temps ++// setting scope in case exception occurs before the call ++// check: invoke %class.B2* @"??0B2@@QEAA@XZ" ++// CHECK: invoke void @llvm.seh.scope.begin() ++// CHECK: invoke %class.B1* @"??0B1@@QEAA@XZ" ++// CHECK: invoke void @llvm.seh.scope.begin() ++// ----- end of temps' scope right before callee ++// CHECK: invoke void @llvm.seh.scope.end() ++// CHECK: invoke void @llvm.seh.scope.end() ++// CHECK: invoke i32 @"?bar@@YAHHVB1@@VB2@@@Z" ++ ++// ----- testing caller's return-by-value temp ++// scope begins right after callee which is the ctor of return temp ++// CHECK: void @"?goo@@YA?AVB1@@H@Z" ++// CHECK: invoke void @llvm.seh.scope.begin() ++// CHECK: invoke void @llvm.seh.scope.end() ++ ++int main() { ++ class B3 b3inmain; ++ ++ // Test conditional ctor and dtor ++ int m = (xxxx > 1) ? B2().data + foo(99) : ++ B3().data + foo(88); ++ ++ // Test: passed-in by value ++ // Per Windows ABI, ctored by caller, dtored by callee ++ int i = bar(foo(0), B1(), B2()); ++ ++ // Test: returned by value ++ // Per Windows ABI, caller allocate a temp in stack, then ctored by callee, ++ // finally dtored in caller after consumed ++ class B1 b1fromgoo = goo(i); ++ ++ return m + b1fromgoo.data + b3inmain.data; ++} +\ No newline at end of file +diff --git a/clang/test/CodeGen/windows-seh-EHa-CppDtors01.cpp b/clang/test/CodeGen/windows-seh-EHa-CppDtors01.cpp +new file mode 100644 +index 00000000000..8fdb1e889c4 +--- /dev/null ++++ b/clang/test/CodeGen/windows-seh-EHa-CppDtors01.cpp +@@ -0,0 +1,60 @@ ++// RUN: %clang_cc1 -triple x86_64-windows -feh-asynch -fcxx-exceptions -fexceptions -fms-extensions -x c++ -Wno-implicit-function-declaration -S -emit-llvm %s -o - | FileCheck %s ++ ++// CHECK: invoke void @llvm.seh.scope.begin() ++// CHECK: invoke void @llvm.seh.scope.begin() ++// CHECK: invoke void @llvm.seh.scope.begin() ++// CHECK: invoke void @llvm.seh.scope.end() ++// CHECK: invoke void @llvm.seh.scope.end() ++// CHECK: invoke void @llvm.seh.scope.end() ++ ++// CHECK: invoke void @llvm.seh.try.begin() ++// CHECK: %[[src:[0-9-]+]] = load volatile i32, i32* %i ++// CHECK-NEXT: invoke void @"?crash@@YAXH@Z"(i32 %[[src]]) ++// CHECK: invoke void @llvm.seh.try.end() ++ ++// **************************************************************************** ++// Abstract: Test CPP unwind Dtoring under SEH -EHa option ++ ++void printf(...); ++int volatile *NullPtr = 0; ++void crash(int i) { ++ struct A { ++ ~A() { ++ printf(" in A dtor \n"); ++ } ++ } ObjA; ++ if (i == 0) ++ *NullPtr = 0; ++ ++ struct B { ++ ~B() { ++ printf(" in B dtor \n"); ++ } ++ } ObjB; ++ if (i == 1) ++ *NullPtr = 0; ++ ++ struct C { ++ ~C() { ++ printf(" in C dtor \n"); ++ } ++ } ObjC; ++ if (i == 2) ++ *NullPtr = 0; ++} ++ ++#define TRY __try ++#define CATCH_ALL __except (1) ++ ++int g; ++int main() { ++ for (int i = 0; i < 3; i++) { ++ TRY { ++ crash(i); ++ } ++ CATCH_ALL { ++ printf(" Test CPP unwind: in catch handler i = %d \n", i); ++ } ++ } ++ return 0; ++} +diff --git a/clang/test/CodeGen/windows-seh-EHa-TryInFinally.cpp b/clang/test/CodeGen/windows-seh-EHa-TryInFinally.cpp +new file mode 100644 +index 00000000000..3efd7d6505f +--- /dev/null ++++ b/clang/test/CodeGen/windows-seh-EHa-TryInFinally.cpp +@@ -0,0 +1,42 @@ ++// RUN: %clang_cc1 -triple x86_64-windows -feh-asynch -fcxx-exceptions -fexceptions -fms-extensions -x c++ -Wno-implicit-function-declaration -S -emit-llvm %s -o - | FileCheck %s ++ ++// CHECK: invoke void @llvm.seh.try.begin() ++// CHECK: invoke void @llvm.seh.try.begin() ++// CHECK: %[[src:[0-9-]+]] = load volatile i32, i32* %i ++// CHECK-NEXT: i32 %[[src]] ++// CHECK: invoke void @llvm.seh.try.end() ++// CHECK: invoke void @llvm.seh.try.end() ++ ++// CHECK: define internal void @"?fin$0@0@main@@"(i8 %abnormal_termination ++// CHECK: invoke void @llvm.seh.try.begin() ++// CHECK: invoke void @llvm.seh.try.end() ++ ++// ***************************************************************************** ++// Abstract: Test __Try in __finally under SEH -EHa option ++void printf(...); ++int volatile *NullPtr = 0; ++int main() { ++ for (int i = 0; i < 3; i++) { ++ printf(" --- Test _Try in _finally --- i = %d \n", i); ++ __try { ++ __try { ++ printf(" In outer _try i = %d \n", i); ++ if (i == 0) ++ *NullPtr = 0; ++ } __finally { ++ __try { ++ printf(" In outer _finally i = %d \n", i); ++ if (i == 1) ++ *NullPtr = 0; ++ } __finally { ++ printf(" In Inner _finally i = %d \n", i); ++ if (i == 2) ++ *NullPtr = 0; ++ } ++ } ++ } __except (1) { ++ printf(" --- In outer except handler i = %d \n", i); ++ } ++ } ++ return 0; ++} +diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst +index 9b58b9dfb17..7ba418ab7d0 100644 +--- a/llvm/docs/LangRef.rst ++++ b/llvm/docs/LangRef.rst +@@ -11642,6 +11642,66 @@ The '``llvm.localescape``' intrinsic blocks inlining, as inlining changes where + the escaped allocas are allocated, which would break attempts to use + '``llvm.localrecover``'. + ++'``llvm.seh.try.begin``' and '``llvm.seh.try.end``' Intrinsics ++^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++Syntax: ++""""""" ++ ++:: ++ ++ declare void @llvm.seh.try.begin() ++ declare void @llvm.seh.try.end() ++ ++Overview: ++""""""""" ++ ++The '``llvm.seh.try.begin``' and '``llvm.seh.try.end``' intrinsics mark ++the boundary of a _try region for Windows SEH Asynchrous Exception Handling. ++ ++Semantics: ++"""""""""" ++ ++When a C-function is compiled with Windows SEH Asynchrous Exception option, ++-feh_asynch (aka MSVC -EHa), these two intrinsics are injected to mark _try ++boundary and to prevent from potential exceptions being moved across boundary. ++ ++'``llvm.seh.scope.begin``' and '``llvm.seh.scope.end``' Intrinsics ++^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++Syntax: ++""""""" ++ ++:: ++ ++ declare void @llvm.seh.scope.begin() ++ declare void @llvm.seh.scope.end() ++ ++Overview: ++""""""""" ++ ++The '``llvm.seh.scope.begin``' and '``llvm.seh.scope.end``' intrinsics mark ++the boundary of a CPP object lifetime for Windows SEH Asynchrous Exception ++Handling (MSVC option -EHa). ++ ++Semantics: ++"""""""""" ++ ++LLVM's ordinary exception-handling representation associates EH cleanups and ++handlers only with ``invoke``s, which normally correspond only to call sites. To ++support arbitrary faulting instructions, it must be possible to recover the current ++EH scope for any instruction. Turning every operation in LLVM that could fault ++into an ``invoke`` of a new, potentially-throwing intrinsic would require adding a ++large number of intrinsics, impede optimization of those operations, and make ++compilation slower by introducing many extra basic blocks. These intrinsics can ++be used instead to mark the region protected by a cleanup, such as for a local ++C++ object with a non-trivial destructor. ``llvm.seh.scope.begin`` is used to mark ++the start of the region; it is aways called with ``invoke``, with the unwind block ++being the desired unwind destination for any potentially-throwing instructions ++within the region. `llvm.seh.scope.end` is used to mark when the scope ends ++and the EH cleanup is no longer required (e.g. because the destructor is being ++called). ++ + .. _int_read_register: + .. _int_read_volatile_register: + .. _int_write_register: +diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td +index 4918ea876df..0d33b64998d 100644 +--- a/llvm/include/llvm/IR/Intrinsics.td ++++ b/llvm/include/llvm/IR/Intrinsics.td +@@ -481,6 +481,16 @@ def int_eh_recoverfp : Intrinsic<[llvm_ptr_ty], + [llvm_ptr_ty, llvm_ptr_ty], + [IntrNoMem]>; + ++// To mark the beginning/end of a try-scope for Windows SEH -EHa ++// calls/invokes to these intrinsics are placed to model control flows ++// caused by HW exceptions under option -EHa. ++// calls/invokes to these intrinsics will be discarded during a codegen pass ++// after EH tables are generated ++def int_seh_try_begin : Intrinsic<[], [], [IntrReadMem, IntrWriteMem, IntrWillReturn]>; ++def int_seh_try_end : Intrinsic<[], [], [IntrReadMem, IntrWriteMem, IntrWillReturn]>; ++def int_seh_scope_begin : Intrinsic<[], [], [IntrNoMem]>; ++def int_seh_scope_end : Intrinsic<[], [], [IntrNoMem]>; ++ + // Note: we treat stacksave/stackrestore as writemem because we don't otherwise + // model their dependencies on allocas. + def int_stacksave : Intrinsic<[llvm_ptr_ty]>, +diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +index d2930391f87..0e5a78f314b 100644 +--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp ++++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +@@ -2801,6 +2801,10 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) { + llvm_unreachable("Cannot invoke this intrinsic"); + case Intrinsic::donothing: + // Ignore invokes to @llvm.donothing: jump directly to the next BB. ++ case Intrinsic::seh_try_begin: ++ case Intrinsic::seh_scope_begin: ++ case Intrinsic::seh_try_end: ++ case Intrinsic::seh_scope_end: + break; + case Intrinsic::experimental_patchpoint_void: + case Intrinsic::experimental_patchpoint_i64: +@@ -6635,6 +6639,10 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, + lowerCallToExternalSymbol(I, FunctionName); + return; + case Intrinsic::donothing: ++ case Intrinsic::seh_try_begin: ++ case Intrinsic::seh_scope_begin: ++ case Intrinsic::seh_try_end: ++ case Intrinsic::seh_scope_end: + // ignore + return; + case Intrinsic::experimental_stackmap: +diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp +index c518ae87ea9..6fb5e60c34a 100644 +--- a/llvm/lib/IR/Verifier.cpp ++++ b/llvm/lib/IR/Verifier.cpp +@@ -4269,6 +4269,10 @@ void Verifier::visitInstruction(Instruction &I) { + Assert( + !F->isIntrinsic() || isa(I) || + F->getIntrinsicID() == Intrinsic::donothing || ++ F->getIntrinsicID() == Intrinsic::seh_try_begin || ++ F->getIntrinsicID() == Intrinsic::seh_try_end || ++ F->getIntrinsicID() == Intrinsic::seh_scope_begin || ++ F->getIntrinsicID() == Intrinsic::seh_scope_end || + F->getIntrinsicID() == Intrinsic::coro_resume || + F->getIntrinsicID() == Intrinsic::coro_destroy || + F->getIntrinsicID() == Intrinsic::experimental_patchpoint_void || diff --git a/patches/make/4.3/001-fix-find_in_given_path.patch b/patches/make/4.3/001-fix-find_in_given_path.patch new file mode 100644 index 0000000..9e88976 --- /dev/null +++ b/patches/make/4.3/001-fix-find_in_given_path.patch @@ -0,0 +1,13 @@ +diff --git a/src/job.c b/src/job.c +index d6e28d3..f8f6950 100644 +--- a/src/job.c ++++ b/src/job.c +@@ -2391,7 +2391,7 @@ child_execute_job (struct childbase *child, int good_stdin, char **argv) + } + } + +- cmd = (char *)find_in_given_path (argv[0], p, 0); ++ cmd = (char *)find_in_given_path (argv[0], p, 0, false); + } + + if (!cmd) diff --git a/patches/mingw-w64/v8.0.0/001-widl-sltg-support.patch b/patches/mingw-w64/v8.0.0/001-widl-sltg-support.patch new file mode 100644 index 0000000..c618bf5 --- /dev/null +++ b/patches/mingw-w64/v8.0.0/001-widl-sltg-support.patch @@ -0,0 +1,2013 @@ +diff -apurN a/mingw-w64-tools/widl/Makefile.am b/mingw-w64-tools/widl/Makefile.am +--- a/mingw-w64-tools/widl/Makefile.am 2020-10-13 16:23:12.944066396 +0200 ++++ b/mingw-w64-tools/widl/Makefile.am 2020-10-13 16:19:00.523221067 +0200 +@@ -29,6 +29,7 @@ widl_SOURCES = src/widl.h \ + src/utils.c \ + src/widl.c \ + src/write_msft.c \ ++ src/write_sltg.c \ + src/wpp/wpp_private.h \ + src/wpp/ppy.tab.h \ + src/wpp/ppl.yy.c \ +diff -apurN a/mingw-w64-tools/widl/Makefile.in b/mingw-w64-tools/widl/Makefile.in +--- a/mingw-w64-tools/widl/Makefile.in 2020-10-13 16:23:12.944066396 +0200 ++++ b/mingw-w64-tools/widl/Makefile.in 2020-10-13 16:21:19.069027341 +0200 +@@ -117,7 +117,8 @@ am_widl_OBJECTS = src/widl-client.$(OBJE + src/widl-utils.$(OBJEXT) src/widl-widl.$(OBJEXT) \ + src/widl-write_msft.$(OBJEXT) src/wpp/widl-ppl.yy.$(OBJEXT) \ + src/wpp/widl-ppy.tab.$(OBJEXT) src/wpp/widl-preproc.$(OBJEXT) \ +- src/wpp/widl-wpp.$(OBJEXT) src/widl-pathtools.$(OBJEXT) ++ src/wpp/widl-wpp.$(OBJEXT) src/widl-pathtools.$(OBJEXT) \ ++ src/widl-write_sltg.$(OBJEXT) + widl_OBJECTS = $(am_widl_OBJECTS) + widl_LDADD = $(LDADD) + widl_LINK = $(CCLD) $(widl_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ +@@ -356,6 +357,7 @@ widl_SOURCES = src/widl.h \ + src/utils.c \ + src/widl.c \ + src/write_msft.c \ ++ src/write_sltg.c \ + src/wpp/wpp_private.h \ + src/wpp/ppy.tab.h \ + src/wpp/ppl.yy.c \ +@@ -527,6 +529,8 @@ src/widl-widl.$(OBJEXT): src/$(am__dirst + src/$(DEPDIR)/$(am__dirstamp) + src/widl-write_msft.$(OBJEXT): src/$(am__dirstamp) \ + src/$(DEPDIR)/$(am__dirstamp) ++src/widl-write_sltg.$(OBJEXT): src/$(am__dirstamp) \ ++ src/$(DEPDIR)/$(am__dirstamp) + src/wpp/$(am__dirstamp): + @$(MKDIR_P) src/wpp + @: > src/wpp/$(am__dirstamp) +@@ -840,6 +845,20 @@ src/widl-write_msft.obj: src/write_msft. + @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(widl_CPPFLAGS) $(CPPFLAGS) $(widl_CFLAGS) $(CFLAGS) -c -o src/widl-write_msft.obj `if test -f 'src/write_msft.c'; then $(CYGPATH_W) 'src/write_msft.c'; else $(CYGPATH_W) '$(srcdir)/src/write_msft.c'; fi` + ++src/widl-write_sltg.o: src/write_sltg.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(widl_CPPFLAGS) $(CPPFLAGS) $(widl_CFLAGS) $(CFLAGS) -MT src/widl-write_sltg.o -MD -MP -MF src/$(DEPDIR)/widl-write_sltg.Tpo -c -o src/widl-write_sltg.o `test -f 'src/write_sltg.c' || echo '$(srcdir)/'`src/write_sltg.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/widl-write_sltg.Tpo src/$(DEPDIR)/widl-write_sltg.Po ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/write_sltg.c' object='src/widl-write_sltg.o' libtool=no @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(widl_CPPFLAGS) $(CPPFLAGS) $(widl_CFLAGS) $(CFLAGS) -c -o src/widl-write_sltg.o `test -f 'src/write_sltg.c' || echo '$(srcdir)/'`src/write_sltg.c ++ ++src/widl-write_sltg.obj: src/write_sltg.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(widl_CPPFLAGS) $(CPPFLAGS) $(widl_CFLAGS) $(CFLAGS) -MT src/widl-write_sltg.obj -MD -MP -MF src/$(DEPDIR)/widl-write_sltg.Tpo -c -o src/widl-write_sltg.obj `if test -f 'src/write_sltg.c'; then $(CYGPATH_W) 'src/write_sltg.c'; else $(CYGPATH_W) '$(srcdir)/src/write_sltg.c'; fi` ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/widl-write_sltg.Tpo src/$(DEPDIR)/widl-write_sltg.Po ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/write_sltg.c' object='src/widl-write_sltg.obj' libtool=no @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(widl_CPPFLAGS) $(CPPFLAGS) $(widl_CFLAGS) $(CFLAGS) -c -o src/widl-write_sltg.obj `if test -f 'src/write_sltg.c'; then $(CYGPATH_W) 'src/write_sltg.c'; else $(CYGPATH_W) '$(srcdir)/src/write_sltg.c'; fi` ++ + src/wpp/widl-ppl.yy.o: src/wpp/ppl.yy.c + @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(widl_CPPFLAGS) $(CPPFLAGS) $(widl_CFLAGS) $(CFLAGS) -MT src/wpp/widl-ppl.yy.o -MD -MP -MF src/wpp/$(DEPDIR)/widl-ppl.yy.Tpo -c -o src/wpp/widl-ppl.yy.o `test -f 'src/wpp/ppl.yy.c' || echo '$(srcdir)/'`src/wpp/ppl.yy.c + @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/wpp/$(DEPDIR)/widl-ppl.yy.Tpo src/wpp/$(DEPDIR)/widl-ppl.yy.Po +diff -apurN a/mingw-w64-tools/widl/src/register.c b/mingw-w64-tools/widl/src/register.c +--- a/mingw-w64-tools/widl/src/register.c 2020-10-13 16:23:12.950066345 +0200 ++++ b/mingw-w64-tools/widl/src/register.c 2020-10-13 16:19:00.523221067 +0200 +@@ -281,7 +281,12 @@ void write_typelib_regscript( const stat + if (count && !strendswith( typelib_name, ".res" )) + error( "Cannot store multiple typelibs into %s\n", typelib_name ); + else +- create_msft_typelib( stmt->u.lib ); ++ { ++ if (do_old_typelib) ++ create_sltg_typelib( stmt->u.lib ); ++ else ++ create_msft_typelib( stmt->u.lib ); ++ } + count++; + } + if (count && strendswith( typelib_name, ".res" )) flush_output_resources( typelib_name ); +diff -apurN a/mingw-w64-tools/widl/src/typelib.h b/mingw-w64-tools/widl/src/typelib.h +--- a/mingw-w64-tools/widl/src/typelib.h 2020-10-13 16:23:12.950066345 +0200 ++++ b/mingw-w64-tools/widl/src/typelib.h 2020-10-13 16:19:00.523221067 +0200 +@@ -83,4 +83,5 @@ enum VARENUM { + extern unsigned short get_type_vt(type_t *t); + + extern int create_msft_typelib(typelib_t *typelib); ++extern int create_sltg_typelib(typelib_t *typelib); + #endif +diff -apurN a/mingw-w64-tools/widl/src/widl.c b/mingw-w64-tools/widl/src/widl.c +--- a/mingw-w64-tools/widl/src/widl.c 2020-10-13 16:23:12.951066337 +0200 ++++ b/mingw-w64-tools/widl/src/widl.c 2020-10-13 16:22:05.493635687 +0200 +@@ -64,6 +64,7 @@ static const char usage[] = + " --nostdinc Do not search the standard include path\n" + " --ns_prefix Prefix namespaces with ABI namespace\n" + " --oldnames Use old naming conventions\n" ++" --oldtlb Use old typelib (SLTG) format\n" + " -o, --output=NAME Set the output file name\n" + " -Otype Type of stubs to generate (-Os, -Oi, -Oif)\n" + " -p Generate proxy\n" +@@ -116,6 +117,7 @@ int do_everything = 1; + static int preprocess_only = 0; + int do_header = 0; + int do_typelib = 0; ++int do_old_typelib = 0; + int do_proxies = 0; + int do_client = 0; + int do_server = 0; +@@ -168,6 +170,7 @@ enum { + DLLDATA_OPTION, + DLLDATA_ONLY_OPTION, + LOCAL_STUBS_OPTION, ++ OLD_TYPELIB_OPTION, + NOSTDINC_OPTION, + PREFIX_ALL_OPTION, + PREFIX_CLIENT_OPTION, +@@ -195,6 +198,7 @@ static const struct option long_options[ + { "nostdinc", 0, NULL, NOSTDINC_OPTION }, + { "ns_prefix", 0, NULL, RT_NS_PREFIX }, + { "oldnames", 0, NULL, OLDNAMES_OPTION }, ++ { "oldtlb", 0, NULL, OLD_TYPELIB_OPTION }, + { "output", 0, NULL, 'o' }, + { "prefix-all", 1, NULL, PREFIX_ALL_OPTION }, + { "prefix-client", 1, NULL, PREFIX_CLIENT_OPTION }, +@@ -333,6 +337,7 @@ static void set_everything(int x) + { + do_header = x; + do_typelib = x; ++ do_old_typelib = x; + do_proxies = x; + do_client = x; + do_server = x; +@@ -749,6 +754,9 @@ int main(int argc,char *argv[]) + do_everything = 0; + do_typelib = 1; + break; ++ case OLD_TYPELIB_OPTION: ++ do_old_typelib = 1; ++ break; + case 'T': + typelib_name = xstrdup(optarg); + break; +diff -apurN a/mingw-w64-tools/widl/src/widl.h b/mingw-w64-tools/widl/src/widl.h +--- a/mingw-w64-tools/widl/src/widl.h 2020-10-13 16:23:12.951066337 +0200 ++++ b/mingw-w64-tools/widl/src/widl.h 2020-10-13 16:19:00.524221058 +0200 +@@ -38,6 +38,7 @@ extern int pedantic; + extern int do_everything; + extern int do_header; + extern int do_typelib; ++extern int do_old_typelib; + extern int do_proxies; + extern int do_client; + extern int do_server; +diff -apurN a/mingw-w64-tools/widl/src/write_sltg.c b/mingw-w64-tools/widl/src/write_sltg.c +--- a/mingw-w64-tools/widl/src/write_sltg.c 1970-01-01 01:00:00.000000000 +0100 ++++ b/mingw-w64-tools/widl/src/write_sltg.c 2020-10-13 16:19:00.524221058 +0200 +@@ -0,0 +1,1857 @@ ++/* ++ * Typelib (SLTG) generation ++ * ++ * Copyright 2015,2016 Dmitry Timoshkov ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA ++ */ ++ ++#include "config.h" ++#include "wine/port.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define NONAMELESSUNION ++ ++#include "windef.h" ++#include "winbase.h" ++ ++#include "widl.h" ++#include "typelib.h" ++#include "typelib_struct.h" ++#include "utils.h" ++#include "header.h" ++#include "typetree.h" ++ ++static const GUID sltg_library_guid = { 0x204ff,0,0,{ 0xc0,0,0,0,0,0,0,0x46 } }; ++ ++struct sltg_data ++{ ++ int size, allocated; ++ char *data; ++}; ++ ++struct sltg_library ++{ ++ short name; ++ char *helpstring; ++ char *helpfile; ++ int helpcontext; ++ int syskind; ++ LCID lcid; ++ int libflags; ++ int version; ++ GUID uuid; ++}; ++ ++struct sltg_block ++{ ++ int length; ++ int index_string; ++ void *data; ++ struct sltg_block *next; ++}; ++ ++struct sltg_typelib ++{ ++ typelib_t *typelib; ++ struct sltg_data index; ++ struct sltg_data name_table; ++ struct sltg_library library; ++ struct sltg_block *blocks; ++ int n_file_blocks; ++ int first_block; ++ int typeinfo_count; ++ int typeinfo_size; ++ struct sltg_block *typeinfo; ++}; ++ ++struct sltg_hrefmap ++{ ++ int href_count; ++ int *href; ++}; ++ ++#include "pshpack1.h" ++struct sltg_typeinfo_header ++{ ++ short magic; ++ int href_offset; ++ int res06; ++ int member_offset; ++ int res0e; ++ int version; ++ int res16; ++ struct ++ { ++ unsigned unknown1 : 3; ++ unsigned flags : 13; ++ unsigned unknown2 : 8; ++ unsigned typekind : 8; ++ } misc; ++ int res1e; ++}; ++ ++struct sltg_member_header ++{ ++ short res00; ++ short res02; ++ char res04; ++ int extra; ++}; ++ ++struct sltg_variable ++{ ++ char magic; /* 0x0a */ ++ char flags; ++ short next; ++ short name; ++ short byte_offs; /* pos in struct, or offset to const type or const data (if flags & 0x08) */ ++ short type; /* if flags & 0x02 this is the type, else offset to type */ ++ int memid; ++ short helpcontext; ++ short helpstring; ++ short varflags; /* only present if magic & 0x02 */ ++}; ++ ++struct sltg_tail ++{ ++ short cFuncs; ++ short cVars; ++ short cImplTypes; ++ short res06; /* always 0000 */ ++ short funcs_off; /* offset to functions (starting from the member header) */ ++ short vars_off; /* offset to vars (starting from the member header) */ ++ short impls_off; /* offset to implemented types (starting from the member header) */ ++ short funcs_bytes; /* bytes used by function data */ ++ short vars_bytes; /* bytes used by var data */ ++ short impls_bytes; /* bytes used by implemented type data */ ++ short tdescalias_vt; /* for TKIND_ALIAS */ ++ short res16; /* always ffff */ ++ short res18; /* always 0000 */ ++ short res1a; /* always 0000 */ ++ short simple_alias; /* tdescalias_vt is a vt rather than an offset? */ ++ short res1e; /* always 0000 */ ++ short cbSizeInstance; ++ short cbAlignment; ++ short res24; ++ short res26; ++ short cbSizeVft; ++ short res2a; /* always ffff */ ++ short res2c; /* always ffff */ ++ short res2e; /* always ffff */ ++ short res30; /* always ffff */ ++ short res32; /* unknown */ ++ short type_bytes; /* bytes used by type descriptions */ ++}; ++ ++struct sltg_hrefinfo ++{ ++ char magic; /* 0xdf */ ++ char res01; /* 0x00 */ ++ int res02; /* 0xffffffff */ ++ int res06; /* 0xffffffff */ ++ int res0a; /* 0xffffffff */ ++ int res0e; /* 0xffffffff */ ++ int res12; /* 0xffffffff */ ++ int res16; /* 0xffffffff */ ++ int res1a; /* 0xffffffff */ ++ int res1e; /* 0xffffffff */ ++ int res22; /* 0xffffffff */ ++ int res26; /* 0xffffffff */ ++ int res2a; /* 0xffffffff */ ++ int res2e; /* 0xffffffff */ ++ int res32; /* 0xffffffff */ ++ int res36; /* 0xffffffff */ ++ int res3a; /* 0xffffffff */ ++ int res3e; /* 0xffffffff */ ++ short res42;/* 0xffff */ ++ int number; /* this is 8 times the number of refs */ ++ /* Now we have number bytes (8 for each ref) of SLTG_UnknownRefInfo */ ++ ++ short res50;/* 0xffff */ ++ char res52; /* 0x01 */ ++ int res53; /* 0x00000000 */ ++ /* Now we have number/8 SLTG_Names (first WORD is no of bytes in the ascii ++ * string). Strings look like "*\Rxxxx*#n". If xxxx == ffff then the ++ * ref refers to the nth type listed in this library (0 based). Else ++ * the xxxx (which maybe fewer than 4 digits) is the offset into the name ++ * table to a string "*\G{}#1.0#0#C:\WINNT\System32\stdole32.tlb#" ++ * The guid is the typelib guid; the ref again refers to the nth type of ++ * the imported typelib. ++ */ ++ ++ char resxx; /* 0xdf */ ++}; ++ ++struct sltg_function ++{ ++ char magic; /* 0x4c, 0xcb or 0x8b with optional SLTG_FUNCTION_FLAGS_PRESENT flag */ ++ char flags; /* high nibble is INVOKE_KIND, low nibble = 2 */ ++ short next; /* byte offset from beginning of group to next fn */ ++ short name; /* Offset within name table to name */ ++ int dispid; /* dispid */ ++ short helpcontext; /* helpcontext (again 1 is special) */ ++ short helpstring; /* helpstring offset to offset */ ++ short arg_off; /* offset to args from start of block */ ++ char nacc; /* lowest 3bits are CALLCONV, rest are no of args */ ++ char retnextopt; /* if 0x80 bit set ret type follows else next WORD ++ is offset to ret type. No of optional args is ++ middle 6 bits */ ++ short rettype; /* return type VT_?? or offset to ret type */ ++ short vtblpos; /* position in vtbl? */ ++ short funcflags; /* present if magic & 0x20 */ ++/* Param list starts, repeat next two as required */ ++#if 0 ++ WORD name; /* offset to 2nd letter of name */ ++ WORD+ type; /* VT_ of param */ ++#endif ++}; ++ ++struct sltg_impl_info ++{ ++ short res00; ++ short next; ++ short res04; ++ char impltypeflags; ++ char res07; ++ short res08; ++ short ref; ++ short res0c; ++ short res0e; ++ short res10; ++ short res12; ++ short pos; ++}; ++ ++#include "poppack.h" ++ ++static void add_structure_typeinfo(struct sltg_typelib *typelib, type_t *type); ++static void add_interface_typeinfo(struct sltg_typelib *typelib, type_t *type); ++static void add_enum_typeinfo(struct sltg_typelib *typelib, type_t *type); ++static void add_union_typeinfo(struct sltg_typelib *typelib, type_t *type); ++static void add_coclass_typeinfo(struct sltg_typelib *typelib, type_t *type); ++ ++static void init_sltg_data(struct sltg_data *data) ++{ ++ data->size = 0; ++ data->allocated = 0x10; ++ data->data = xmalloc(0x10); ++} ++ ++static int add_index(struct sltg_data *index, const char *name) ++{ ++ int name_offset = index->size; ++ int new_size = index->size + strlen(name) + 1; ++ ++ chat("add_index: name_offset %d, \"%s\"\n", name_offset, name); ++ ++ if (new_size > index->allocated) ++ { ++ index->allocated = max(index->allocated * 2, new_size); ++ index->data = xrealloc(index->data, index->allocated); ++ } ++ ++ strcpy(index->data + index->size, name); ++ index->size = new_size; ++ ++ return name_offset; ++} ++ ++static void init_index(struct sltg_data *index) ++{ ++ static const char compobj[] = { 1,'C','o','m','p','O','b','j',0 }; ++ ++ init_sltg_data(index); ++ ++ add_index(index, compobj); ++} ++ ++static int add_name(struct sltg_typelib *sltg, const char *name) ++{ ++ int name_offset = sltg->name_table.size; ++ int new_size = sltg->name_table.size + strlen(name) + 1 + 8; ++ int aligned_size; ++ ++ chat("add_name: %s\n", name); ++ ++ aligned_size = (new_size + 0x1f) & ~0x1f; ++ if (aligned_size - new_size < 4) ++ new_size = aligned_size; ++ else ++ new_size = (new_size + 1) & ~1; ++ ++ if (new_size > sltg->name_table.allocated) ++ { ++ sltg->name_table.allocated = max(sltg->name_table.allocated * 2, new_size); ++ sltg->name_table.data = xrealloc(sltg->name_table.data, sltg->name_table.allocated); ++ } ++ ++ memset(sltg->name_table.data + sltg->name_table.size, 0xff, 8); ++ strcpy(sltg->name_table.data + sltg->name_table.size + 8, name); ++ sltg->name_table.size = new_size; ++ sltg->name_table.data[sltg->name_table.size - 1] = 0; /* clear alignment */ ++ ++ return name_offset; ++} ++ ++static void init_name_table(struct sltg_typelib *sltg) ++{ ++ init_sltg_data(&sltg->name_table); ++} ++ ++static void init_library(struct sltg_typelib *sltg) ++{ ++ const attr_t *attr; ++ ++ sltg->library.name = add_name(sltg, sltg->typelib->name); ++ sltg->library.helpstring = NULL; ++ sltg->library.helpcontext = 0; ++ sltg->library.syskind = (pointer_size == 8) ? SYS_WIN64 : SYS_WIN32; ++ sltg->library.lcid = 0x0409; ++ sltg->library.libflags = 0; ++ sltg->library.version = 0; ++ sltg->library.helpfile = NULL; ++ memset(&sltg->library.uuid, 0, sizeof(sltg->library.uuid)); ++ ++ if (!sltg->typelib->attrs) return; ++ ++ LIST_FOR_EACH_ENTRY(attr, sltg->typelib->attrs, const attr_t, entry) ++ { ++ const expr_t *expr; ++ ++ switch (attr->type) ++ { ++ case ATTR_VERSION: ++ sltg->library.version = attr->u.ival; ++ break; ++ case ATTR_HELPSTRING: ++ sltg->library.helpstring = attr->u.pval; ++ break; ++ case ATTR_HELPFILE: ++ sltg->library.helpfile = attr->u.pval; ++ break; ++ case ATTR_UUID: ++ sltg->library.uuid = *(GUID *)attr->u.pval; ++ break; ++ case ATTR_HELPCONTEXT: ++ expr = attr->u.pval; ++ sltg->library.helpcontext = expr->cval; ++ break; ++ case ATTR_LIBLCID: ++ expr = attr->u.pval; ++ sltg->library.lcid = expr->cval; ++ break; ++ case ATTR_CONTROL: ++ sltg->library.libflags |= 0x02; /* LIBFLAG_FCONTROL */ ++ break; ++ case ATTR_HIDDEN: ++ sltg->library.libflags |= 0x04; /* LIBFLAG_FHIDDEN */ ++ break; ++ case ATTR_RESTRICTED: ++ sltg->library.libflags |= 0x01; /* LIBFLAG_FRESTRICTED */ ++ break; ++ default: ++ break; ++ } ++ } ++} ++ ++static void add_block_index(struct sltg_typelib *sltg, void *data, int size, int index) ++{ ++ struct sltg_block *block = xmalloc(sizeof(*block)); ++ ++ block->length = size; ++ block->data = data; ++ block->index_string = index; ++ block->next = NULL; ++ ++ if (sltg->blocks) ++ { ++ struct sltg_block *blocks = sltg->blocks; ++ ++ while (blocks->next) ++ blocks = blocks->next; ++ ++ blocks->next = block; ++ } ++ else ++ sltg->blocks = block; ++ ++ sltg->n_file_blocks++; ++} ++ ++static void add_block(struct sltg_typelib *sltg, void *data, int size, const char *name) ++{ ++ struct sltg_block *block = xmalloc(sizeof(*block)); ++ int index; ++ ++ chat("add_block: %p,%d,\"%s\"\n", data, size, name); ++ ++ index = add_index(&sltg->index, name); ++ ++ add_block_index(sltg, data, size, index); ++} ++ ++static void *create_library_block(struct sltg_typelib *typelib, int *size, int *index) ++{ ++ void *block; ++ short *p; ++ ++ *size = sizeof(short) * 9 + sizeof(int) * 3 + sizeof(GUID); ++ if (typelib->library.helpstring) *size += strlen(typelib->library.helpstring); ++ if (typelib->library.helpfile) *size += strlen(typelib->library.helpfile); ++ ++ block = xmalloc(*size); ++ p = block; ++ *p++ = 0x51cc; /* magic */ ++ *p++ = 3; /* res02 */ ++ *p++ = typelib->library.name; ++ *p++ = 0xffff; /* res06 */ ++ if (typelib->library.helpstring) ++ { ++ *p++ = strlen(typelib->library.helpstring); ++ strcpy((char *)p, typelib->library.helpstring); ++ p = (short *)((char *)p + strlen(typelib->library.helpstring)); ++ } ++ else ++ *p++ = 0xffff; ++ if (typelib->library.helpfile) ++ { ++ *p++ = strlen(typelib->library.helpfile); ++ strcpy((char *)p, typelib->library.helpfile); ++ p = (short *)((char *)p + strlen(typelib->library.helpfile)); ++ } ++ else ++ *p++ = 0xffff; ++ *(int *)p = typelib->library.helpcontext; ++ p += 2; ++ *p++ = typelib->library.syskind; ++ *p++ = typelib->library.lcid; ++ *(int *)p = 0; /* res12 */ ++ p += 2; ++ *p++ = typelib->library.libflags; ++ *(int *)p = typelib->library.version; ++ p += 2; ++ *(GUID *)p = typelib->library.uuid; ++ ++ *index = add_index(&typelib->index, "dir"); ++ ++ return block; ++} ++ ++static const char *new_index_name(void) ++{ ++ static char name[11] = "0000000000"; ++ static int pos = 0; ++ char *new_name; ++ ++ if (name[pos] == 'Z') ++ { ++ pos++; ++ if (pos > 9) ++ error("too many index names\n"); ++ } ++ ++ name[pos]++; ++ ++ new_name = xmalloc(sizeof(name)); ++ strcpy(new_name, name); ++ return new_name; ++} ++ ++static void sltg_add_typeinfo(struct sltg_typelib *sltg, void *data, int size, const char *name) ++{ ++ struct sltg_block *block = xmalloc(sizeof(*block)); ++ ++ chat("sltg_add_typeinfo: %p,%d,%s\n", data, size, name); ++ ++ block->length = size; ++ block->data = data; ++ block->index_string = 0; ++ block->next = NULL; ++ ++ if (sltg->typeinfo) ++ { ++ struct sltg_block *typeinfo = sltg->typeinfo; ++ ++ while (typeinfo->next) ++ typeinfo = typeinfo->next; ++ ++ typeinfo->next = block; ++ } ++ else ++ sltg->typeinfo = block; ++ ++ sltg->typeinfo_count++; ++ sltg->typeinfo_size += size; ++} ++ ++static void append_data(struct sltg_data *block, const void *data, int size) ++{ ++ int new_size = block->size + size; ++ ++ if (new_size > block->allocated) ++ { ++ block->allocated = max(block->allocated * 2, new_size); ++ block->data = xrealloc(block->data, block->allocated); ++ } ++ ++ memcpy(block->data + block->size, data, size); ++ block->size = new_size; ++} ++ ++static void add_module_typeinfo(struct sltg_typelib *typelib, type_t *type) ++{ ++ error("add_module_typeinfo: %s not implemented\n", type->name); ++} ++ ++static const char *add_typeinfo_block(struct sltg_typelib *typelib, const type_t *type, int kind) ++{ ++ const char *index_name, *other_name; ++ void *block; ++ short *p; ++ int size, helpcontext = 0; ++ GUID guid = { 0 }; ++ const expr_t *expr; ++ ++ index_name = new_index_name(); ++ other_name = new_index_name(); ++ ++ expr = get_attrp(type->attrs, ATTR_HELPCONTEXT); ++ if (expr) helpcontext = expr->cval; ++ ++ p = get_attrp(type->attrs, ATTR_UUID); ++ if (p) guid = *(GUID *)p; ++ ++ size = sizeof(short) * 8 + 10 /* index_name */ * 2 + sizeof(int) + sizeof(GUID); ++ ++ block = xmalloc(size); ++ p = block; ++ *p++ = strlen(index_name); ++ strcpy((char *)p, index_name); ++ p = (short *)((char *)p + strlen(index_name)); ++ *p++ = strlen(other_name); ++ strcpy((char *)p, other_name); ++ p = (short *)((char *)p + strlen(other_name)); ++ *p++ = -1; /* res1a */ ++ *p++ = add_name(typelib, type->name); /* name offset */ ++ *p++ = 0; /* FIXME: helpstring */ ++ *p++ = -1; /* res20 */ ++ *(int *)p = helpcontext; ++ p += 2; ++ *p++ = -1; /* res26 */ ++ *(GUID *)p = guid; ++ p += sizeof(GUID)/2; ++ *p = kind; ++ ++ sltg_add_typeinfo(typelib, block, size, index_name); ++ ++ return index_name; ++} ++ ++static void init_typeinfo(struct sltg_typeinfo_header *ti, const type_t *type, int kind, ++ const struct sltg_hrefmap *hrefmap) ++{ ++ ti->magic = 0x0501; ++ ti->href_offset = -1; ++ ti->res06 = -1; ++ ti->res0e = -1; ++ ti->version = get_attrv(type->attrs, ATTR_VERSION); ++ ti->res16 = 0xfffe0000; ++ ti->misc.unknown1 = 0x02; ++ ti->misc.flags = 0; /* FIXME */ ++ ti->misc.unknown2 = 0x02; ++ ti->misc.typekind = kind; ++ ti->res1e = 0; ++ ++ ti->member_offset = sizeof(*ti); ++ ++ if (hrefmap->href_count) ++ { ++ char name[64]; ++ int i, hrefinfo_size; ++ ++ hrefinfo_size = sizeof(struct sltg_hrefinfo); ++ ++ for (i = 0; i < hrefmap->href_count; i++) ++ { ++ sprintf(name, "*\\Rffff*#%x", hrefmap->href[i]); ++ hrefinfo_size += 8 + 2 + strlen(name); ++ } ++ ++ ti->href_offset = ti->member_offset; ++ ti->member_offset += hrefinfo_size; ++ } ++} ++ ++static void init_sltg_tail(struct sltg_tail *tail) ++{ ++ tail->cFuncs = 0; ++ tail->cVars = 0; ++ tail->cImplTypes = 0; ++ tail->res06 = 0; ++ tail->funcs_off = -1; ++ tail->vars_off = -1; ++ tail->impls_off = -1; ++ tail->funcs_bytes = -1; ++ tail->vars_bytes = -1; ++ tail->impls_bytes = -1; ++ tail->tdescalias_vt = -1; ++ tail->res16 = -1; ++ tail->res18 = 0; ++ tail->res1a = 0; ++ tail->simple_alias = 0; ++ tail->res1e = 0; ++ tail->cbSizeInstance = 0; ++ tail->cbAlignment = 4; ++ tail->res24 = -1; ++ tail->res26 = -1; ++ tail->cbSizeVft = 0; ++ tail->res2a = -1; ++ tail->res2c = -1; ++ tail->res2e = -1; ++ tail->res30 = -1; ++ tail->res32 = 0; ++ tail->type_bytes = 0; ++} ++ ++static void write_hrefmap(struct sltg_data *data, const struct sltg_hrefmap *hrefmap) ++{ ++ struct sltg_hrefinfo hrefinfo; ++ char name[64]; ++ int i; ++ ++ if (!hrefmap->href_count) return; ++ ++ hrefinfo.magic = 0xdf; ++ hrefinfo.res01 = 0; ++ hrefinfo.res02 = -1; ++ hrefinfo.res06 = -1; ++ hrefinfo.res0a = -1; ++ hrefinfo.res0e = -1; ++ hrefinfo.res12 = -1; ++ hrefinfo.res16 = -1; ++ hrefinfo.res1a = -1; ++ hrefinfo.res1e = -1; ++ hrefinfo.res22 = -1; ++ hrefinfo.res26 = -1; ++ hrefinfo.res2a = -1; ++ hrefinfo.res2e = -1; ++ hrefinfo.res32 = -1; ++ hrefinfo.res36 = -1; ++ hrefinfo.res3a = -1; ++ hrefinfo.res3e = -1; ++ hrefinfo.res42 = -1; ++ hrefinfo.number = hrefmap->href_count * 8; ++ hrefinfo.res50 = -1; ++ hrefinfo.res52 = 1; ++ hrefinfo.res53 = 0; ++ hrefinfo.resxx = 0xdf; ++ ++ append_data(data, &hrefinfo, offsetof(struct sltg_hrefinfo, res50)); ++ ++ for (i = 0; i < hrefmap->href_count; i++) ++ append_data(data, "\xff\xff\xff\xff\xff\xff\xff\xff", 8); ++ ++ append_data(data, &hrefinfo.res50, 7); ++ ++ for (i = 0; i < hrefmap->href_count; i++) ++ { ++ short len; ++ ++ sprintf(name, "*\\Rffff*#%x", hrefmap->href[i]); ++ len = strlen(name); ++ ++ append_data(data, &len, sizeof(len)); ++ append_data(data, name, len); ++ } ++ ++ append_data(data, &hrefinfo.resxx, sizeof(hrefinfo.resxx)); ++} ++ ++static void dump_var_desc(const char *data, int size) ++{ ++ const unsigned char *p = (const unsigned char *)data; ++ int i; ++ ++ if (!(debuglevel & (DEBUGLEVEL_TRACE | DEBUGLEVEL_CHAT))) return; ++ ++ chat("dump_var_desc: size %d bytes\n", size); ++ ++ for (i = 0; i < size; i++) ++ fprintf(stderr, " %02x", *p++); ++ ++ fprintf(stderr, "\n"); ++} ++ ++static int get_element_size(type_t *type) ++{ ++ int vt = get_type_vt(type); ++ ++ switch (vt) ++ { ++ case VT_I1: ++ case VT_UI1: ++ return 1; ++ ++ case VT_INT: ++ case VT_UINT: ++ return /* typelib_kind == SYS_WIN16 ? 2 : */ 4; ++ ++ case VT_UI2: ++ case VT_I2: ++ case VT_BOOL: ++ return 2; ++ ++ case VT_I4: ++ case VT_UI4: ++ case VT_R4: ++ case VT_ERROR: ++ case VT_HRESULT: ++ return 4; ++ ++ case VT_R8: ++ case VT_I8: ++ case VT_UI8: ++ case VT_CY: ++ case VT_DATE: ++ return 8; ++ ++ case VT_DECIMAL: ++ return 16; ++ ++ case VT_PTR: ++ case VT_UNKNOWN: ++ case VT_DISPATCH: ++ case VT_BSTR: ++ case VT_LPSTR: ++ case VT_LPWSTR: ++ return pointer_size; ++ ++ case VT_VOID: ++ return 0; ++ ++ case VT_VARIANT: ++ return pointer_size == 8 ? 24 : 16; ++ ++ case VT_USERDEFINED: ++ return 0; ++ ++ default: ++ error("get_element_size: unrecognized vt %d\n", vt); ++ break; ++ } ++ ++ return 0; ++} ++ ++static int local_href(struct sltg_hrefmap *hrefmap, int typelib_href) ++{ ++ int i, href = -1; ++ ++ for (i = 0; i < hrefmap->href_count; i++) ++ { ++ if (hrefmap->href[i] == typelib_href) ++ { ++ href = i; ++ break; ++ } ++ } ++ ++ if (href == -1) ++ { ++ href = hrefmap->href_count; ++ ++ if (hrefmap->href) ++ hrefmap->href = xrealloc(hrefmap->href, sizeof(*hrefmap->href) * (hrefmap->href_count + 1)); ++ else ++ hrefmap->href = xmalloc(sizeof(*hrefmap->href)); ++ ++ hrefmap->href[hrefmap->href_count] = typelib_href; ++ hrefmap->href_count++; ++ } ++ ++ chat("typelib href %d mapped to local href %d\n", typelib_href, href); ++ ++ return href << 2; ++} ++ ++static short write_var_desc(struct sltg_typelib *typelib, struct sltg_data *data, type_t *type, short param_flags, ++ short flags, short base_offset, int *size_instance, struct sltg_hrefmap *hrefmap) ++{ ++ short vt, vt_flags, desc_offset; ++ ++ chat("write_var_desc: type %p, type->name %s\n", ++ type, type->name ? type->name : "NULL"); ++ ++ if (is_array(type) && !type_array_is_decl_as_ptr(type)) ++ { ++ int num_dims, elements, array_start, size, array_size; ++ type_t *atype; ++ struct ++ { ++ short cDims; ++ short fFeatures; ++ int cbElements; ++ int cLocks; ++ void *pvData; ++ int bound[2]; ++ } *array; ++ int *bound; ++ short vt_off[2]; ++ ++ elements = 1; ++ num_dims = 0; ++ ++ atype = type; ++ ++ while (is_array(atype) && !type_array_is_decl_as_ptr(atype)) ++ { ++ num_dims++; ++ elements *= type_array_get_dim(atype); ++ ++ atype = type_array_get_element_type(atype); ++ } ++ ++ chat("write_var_desc: VT_CARRAY: %d dimensions, %d elements\n", num_dims, elements); ++ ++ array_start = data->size; ++ ++ size = sizeof(*array) + (num_dims - 1) * 8 /* sizeof(SAFEARRAYBOUND) */; ++ array = xmalloc(size); ++ ++ array->cDims = num_dims; ++ array->fFeatures = 0x0004; /* FADF_EMBEDDED */ ++ array->cbElements = get_element_size(atype); ++ array->cLocks = 0; ++ array->pvData = NULL; ++ ++ bound = array->bound; ++ ++ array_size = array->cbElements; ++ atype = type; ++ ++ while (is_array(atype) && !type_array_is_decl_as_ptr(atype)) ++ { ++ bound[0] = type_array_get_dim(atype); ++ array_size *= bound[0]; ++ bound[1] = 0; ++ bound += 2; ++ ++ atype = type_array_get_element_type(atype); ++ } ++ ++ if (size_instance) ++ { ++ *size_instance += array_size; ++ size_instance = NULL; /* don't account for element size */ ++ } ++ ++ append_data(data, array, size); ++ ++ desc_offset = data->size; ++ ++ vt_off[0] = VT_CARRAY; ++ vt_off[1] = array_start + base_offset; ++ append_data(data, vt_off, sizeof(vt_off)); ++ ++ /* fall through to write array element description */ ++ type = atype; ++ } ++ else ++ desc_offset = data->size; ++ ++ vt = get_type_vt(type); ++ ++ if (vt == VT_PTR) ++ { ++ type_t *ref = is_ptr(type) ? type_pointer_get_ref_type(type) : type_array_get_element_type(type); ++ ++ if (is_ptr(ref)) ++ { ++ chat("write_var_desc: vt VT_PTR | 0x0400 | %04x\n", param_flags); ++ vt = VT_PTR | 0x0400 | param_flags; ++ append_data(data, &vt, sizeof(vt)); ++ write_var_desc(typelib, data, ref, 0, 0, base_offset, size_instance, hrefmap); ++ } ++ else ++ write_var_desc(typelib, data, ref, param_flags, 0x0e00, base_offset, size_instance, hrefmap); ++ return desc_offset; ++ } ++ ++ chat("write_var_desc: vt %d, flags %04x\n", vt, flags); ++ ++ vt_flags = vt | flags | param_flags; ++ append_data(data, &vt_flags, sizeof(vt_flags)); ++ ++ if (vt == VT_USERDEFINED) ++ { ++ short href; ++ ++ while (type->typelib_idx < 0 && type_is_alias(type)) ++ type = type_alias_get_aliasee_type(type); ++ ++ chat("write_var_desc: VT_USERDEFINED, type %p, name %s, real type %d, href %d\n", ++ type, type->name, type_get_type(type), type->typelib_idx); ++ ++ if (type->typelib_idx == -1) ++ { ++ chat("write_var_desc: trying to ref not added type\n"); ++ ++ switch (type_get_type(type)) ++ { ++ case TYPE_STRUCT: ++ add_structure_typeinfo(typelib, type); ++ break; ++ case TYPE_INTERFACE: ++ add_interface_typeinfo(typelib, type); ++ break; ++ case TYPE_ENUM: ++ add_enum_typeinfo(typelib, type); ++ break; ++ case TYPE_UNION: ++ add_union_typeinfo(typelib, type); ++ break; ++ case TYPE_COCLASS: ++ add_coclass_typeinfo(typelib, type); ++ break; ++ default: ++ error("write_var_desc: VT_USERDEFINED - unhandled type %d\n", ++ type_get_type(type)); ++ } ++ } ++ ++ if (type->typelib_idx == -1) ++ error("write_var_desc: trying to ref not added type\n"); ++ ++ href = local_href(hrefmap, type->typelib_idx); ++ chat("write_var_desc: VT_USERDEFINED, local href %d\n", href); ++ ++ append_data(data, &href, sizeof(href)); ++ } ++ ++ if (size_instance) ++ *size_instance += get_element_size(type); ++ ++ return desc_offset; ++} ++ ++static void add_structure_typeinfo(struct sltg_typelib *typelib, type_t *type) ++{ ++ struct sltg_data data, *var_data = NULL; ++ struct sltg_hrefmap hrefmap; ++ const char *index_name; ++ struct sltg_typeinfo_header ti; ++ struct sltg_member_header member; ++ struct sltg_tail tail; ++ int member_offset, var_count = 0, var_data_size = 0, size_instance = 0; ++ short *type_desc_offset = NULL; ++ ++ if (type->typelib_idx != -1) return; ++ ++ chat("add_structure_typeinfo: type %p, type->name %s\n", type, type->name); ++ ++ type->typelib_idx = typelib->n_file_blocks; ++ ++ hrefmap.href_count = 0; ++ hrefmap.href = NULL; ++ ++ if (type_struct_get_fields(type)) ++ { ++ int i = 0; ++ var_t *var; ++ ++ var_count = list_count(type_struct_get_fields(type)); ++ ++ var_data = xmalloc(var_count * sizeof(*var_data)); ++ type_desc_offset = xmalloc(var_count * sizeof(*type_desc_offset)); ++ ++ LIST_FOR_EACH_ENTRY(var, type_struct_get_fields(type), var_t, entry) ++ { ++ short base_offset; ++ ++ chat("add_structure_typeinfo: var %p (%s), type %p (%s)\n", ++ var, var->name, var->declspec.type, var->declspec.type->name); ++ ++ init_sltg_data(&var_data[i]); ++ ++ base_offset = var_data_size + (i + 1) * sizeof(struct sltg_variable); ++ type_desc_offset[i] = write_var_desc(typelib, &var_data[i], var->declspec.type, 0, 0, ++ base_offset, &size_instance, &hrefmap); ++ dump_var_desc(var_data[i].data, var_data[i].size); ++ ++ if (var_data[i].size > sizeof(short)) ++ var_data_size += var_data[i].size; ++ i++; ++ } ++ } ++ ++ init_sltg_data(&data); ++ ++ index_name = add_typeinfo_block(typelib, type, TKIND_RECORD); ++ ++ init_typeinfo(&ti, type, TKIND_RECORD, &hrefmap); ++ append_data(&data, &ti, sizeof(ti)); ++ ++ write_hrefmap(&data, &hrefmap); ++ ++ member_offset = data.size; ++ ++ member.res00 = 0x0001; ++ member.res02 = 0xffff; ++ member.res04 = 0x01; ++ member.extra = var_data_size + var_count * sizeof(struct sltg_variable); ++ append_data(&data, &member, sizeof(member)); ++ ++ var_data_size = 0; ++ ++ if (type_struct_get_fields(type)) ++ { ++ int i = 0; ++ short next = member_offset; ++ var_t *var; ++ ++ LIST_FOR_EACH_ENTRY(var, type_struct_get_fields(type), var_t, entry) ++ { ++ struct sltg_variable variable; ++ ++ next += sizeof(variable); ++ ++ variable.magic = 0x2a; /* always write flags to simplify calculations */ ++ variable.name = add_name(typelib, var->name); ++ variable.byte_offs = 0; ++ if (var_data[i].size > sizeof(short)) ++ { ++ variable.flags = 0; ++ var_data_size = next - member_offset + type_desc_offset[i]; ++ variable.type = var_data_size; ++ next += var_data[i].size; ++ } ++ else ++ { ++ variable.flags = 0x02; ++ variable.type = *(short *)var_data[i].data; ++ } ++ variable.next = i < var_count - 1 ? next - member_offset : -1; ++ variable.memid = 0x40000000 + i; ++ variable.helpcontext = -2; /* 0xfffe */ ++ variable.helpstring = -1; ++ variable.varflags = 0; ++ ++ append_data(&data, &variable, sizeof(variable)); ++ if (var_data[i].size > sizeof(short)) ++ append_data(&data, var_data[i].data, var_data[i].size); ++ ++ i++; ++ } ++ } ++ ++ init_sltg_tail(&tail); ++ ++ tail.cVars = var_count; ++ tail.vars_off = 0; ++ tail.vars_bytes = var_data_size; ++ tail.cbSizeInstance = size_instance; ++ tail.type_bytes = data.size - member_offset - sizeof(member); ++ append_data(&data, &tail, sizeof(tail)); ++ ++ add_block(typelib, data.data, data.size, index_name); ++} ++ ++static importinfo_t *find_importinfo(typelib_t *typelib, const char *name) ++{ ++ importlib_t *importlib; ++ ++ LIST_FOR_EACH_ENTRY(importlib, &typelib->importlibs, importlib_t, entry) ++ { ++ int i; ++ ++ for (i = 0; i < importlib->ntypeinfos; i++) ++ { ++ if (!strcmp(name, importlib->importinfos[i].name)) ++ { ++ chat("Found %s in importlib list\n", name); ++ return &importlib->importinfos[i]; ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++static int get_func_flags(const var_t *func, int *dispid, int *invokekind, int *helpcontext, const char **helpstring) ++{ ++ const attr_t *attr; ++ int flags; ++ ++ *invokekind = 1 /* INVOKE_FUNC */; ++ *helpcontext = -2; ++ *helpstring = NULL; ++ ++ if (!func->attrs) return 0; ++ ++ flags = 0; ++ ++ LIST_FOR_EACH_ENTRY(attr, func->attrs, const attr_t, entry) ++ { ++ expr_t *expr = attr->u.pval; ++ switch(attr->type) ++ { ++ case ATTR_BINDABLE: ++ flags |= 0x4; /* FUNCFLAG_FBINDABLE */ ++ break; ++ case ATTR_DEFAULTBIND: ++ flags |= 0x20; /* FUNCFLAG_FDEFAULTBIND */ ++ break; ++ case ATTR_DEFAULTCOLLELEM: ++ flags |= 0x100; /* FUNCFLAG_FDEFAULTCOLLELEM */ ++ break; ++ case ATTR_DISPLAYBIND: ++ flags |= 0x10; /* FUNCFLAG_FDISPLAYBIND */ ++ break; ++ case ATTR_HELPCONTEXT: ++ *helpcontext = expr->u.lval; ++ break; ++ case ATTR_HELPSTRING: ++ *helpstring = attr->u.pval; ++ break; ++ case ATTR_HIDDEN: ++ flags |= 0x40; /* FUNCFLAG_FHIDDEN */ ++ break; ++ case ATTR_ID: ++ *dispid = expr->cval; ++ break; ++ case ATTR_IMMEDIATEBIND: ++ flags |= 0x1000; /* FUNCFLAG_FIMMEDIATEBIND */ ++ break; ++ case ATTR_NONBROWSABLE: ++ flags |= 0x400; /* FUNCFLAG_FNONBROWSABLE */ ++ break; ++ case ATTR_PROPGET: ++ *invokekind = 0x2; /* INVOKE_PROPERTYGET */ ++ break; ++ case ATTR_PROPPUT: ++ *invokekind = 0x4; /* INVOKE_PROPERTYPUT */ ++ break; ++ case ATTR_PROPPUTREF: ++ *invokekind = 0x8; /* INVOKE_PROPERTYPUTREF */ ++ break; ++ /* FIXME: FUNCFLAG_FREPLACEABLE */ ++ case ATTR_REQUESTEDIT: ++ flags |= 0x8; /* FUNCFLAG_FREQUESTEDIT */ ++ break; ++ case ATTR_RESTRICTED: ++ flags |= 0x1; /* FUNCFLAG_FRESTRICTED */ ++ break; ++ case ATTR_SOURCE: ++ flags |= 0x2; /* FUNCFLAG_FSOURCE */ ++ break; ++ case ATTR_UIDEFAULT: ++ flags |= 0x200; /* FUNCFLAG_FUIDEFAULT */ ++ break; ++ case ATTR_USESGETLASTERROR: ++ flags |= 0x80; /* FUNCFLAG_FUSESGETLASTERROR */ ++ break; ++ default: ++ break; ++ } ++ } ++ ++ return flags; ++} ++ ++static int get_param_flags(const var_t *param) ++{ ++ const attr_t *attr; ++ int flags, in, out; ++ ++ if (!param->attrs) return 0; ++ ++ flags = 0; ++ in = out = 0; ++ ++ LIST_FOR_EACH_ENTRY(attr, param->attrs, const attr_t, entry) ++ { ++ switch(attr->type) ++ { ++ case ATTR_IN: ++ in++; ++ break; ++ case ATTR_OUT: ++ out++; ++ break; ++ case ATTR_PARAMLCID: ++ flags |= 0x2000; ++ break; ++ case ATTR_RETVAL: ++ flags |= 0x80; ++ break; ++ default: ++ chat("unhandled param attr %d\n", attr->type); ++ break; ++ } ++ } ++ ++ if (out) ++ { ++ if (in) ++ flags |= 0x8000; ++ else ++ flags |= 0x4000; ++ } ++ else if (!in) ++ flags |= 0xc000; ++ ++ return flags; ++} ++ ++ ++static int add_func_desc(struct sltg_typelib *typelib, struct sltg_data *data, var_t *func, ++ int idx, int dispid, short base_offset, struct sltg_hrefmap *hrefmap) ++{ ++ struct sltg_data ret_data, *arg_data; ++ int arg_count = 0, arg_data_size, optional = 0, defaults = 0, old_size; ++ int funcflags = 0, invokekind = 1 /* INVOKE_FUNC */, helpcontext; ++ const char *helpstring; ++ const var_t *arg; ++ short ret_desc_offset, *arg_desc_offset, arg_offset; ++ struct sltg_function func_desc; ++ ++ chat("add_func_desc: %s, idx %#x, dispid %#x\n", func->name, idx, dispid); ++ ++ old_size = data->size; ++ ++ init_sltg_data(&ret_data); ++ ret_desc_offset = write_var_desc(typelib, &ret_data, type_function_get_rettype(func->declspec.type), ++ 0, 0, base_offset, NULL, hrefmap); ++ dump_var_desc(ret_data.data, ret_data.size); ++ ++ arg_data_size = 0; ++ arg_offset = base_offset + sizeof(struct sltg_function); ++ ++ if (ret_data.size > sizeof(short)) ++ { ++ arg_data_size += ret_data.size; ++ arg_offset += ret_data.size; ++ } ++ ++ if (type_function_get_args(func->declspec.type)) ++ { ++ int i = 0; ++ ++ arg_count = list_count(type_function_get_args(func->declspec.type)); ++ ++ arg_data = xmalloc(arg_count * sizeof(*arg_data)); ++ arg_desc_offset = xmalloc(arg_count * sizeof(*arg_desc_offset)); ++ ++ arg_offset += arg_count * 2 * sizeof(short); ++ ++ LIST_FOR_EACH_ENTRY(arg, type_function_get_args(func->declspec.type), const var_t, entry) ++ { ++ const attr_t *attr; ++ short param_flags = get_param_flags(arg); ++ ++ chat("add_func_desc: arg[%d] %p (%s), type %p (%s)\n", ++ i, arg, arg->name, arg->declspec.type, arg->declspec.type->name); ++ ++ init_sltg_data(&arg_data[i]); ++ ++ ++ arg_desc_offset[i] = write_var_desc(typelib, &arg_data[i], arg->declspec.type, param_flags, 0, ++ arg_offset, NULL, hrefmap); ++ dump_var_desc(arg_data[i].data, arg_data[i].size); ++ ++ if (arg_data[i].size > sizeof(short)) ++ { ++ arg_data_size += arg_data[i].size; ++ arg_offset += arg_data[i].size;; ++ } ++ ++ i++; ++ ++ if (!arg->attrs) continue; ++ ++ LIST_FOR_EACH_ENTRY(attr, arg->attrs, const attr_t, entry) ++ { ++ if (attr->type == ATTR_DEFAULTVALUE) ++ defaults++; ++ else if(attr->type == ATTR_OPTIONAL) ++ optional++; ++ } ++ } ++ } ++ ++ funcflags = get_func_flags(func, &dispid, &invokekind, &helpcontext, &helpstring); ++ ++ if (base_offset != -1) ++ chat("add_func_desc: flags %#x, dispid %#x, invokekind %d, helpcontext %#x, helpstring %s\n", ++ funcflags, dispid, invokekind, helpcontext, helpstring); ++ ++ func_desc.magic = 0x6c; /* always write flags to simplify calculations */ ++ func_desc.flags = (invokekind << 4) | 0x02; ++ if (idx & 0x80000000) ++ { ++ func_desc.next = -1; ++ idx &= ~0x80000000; ++ } ++ else ++ func_desc.next = base_offset + sizeof(func_desc) + arg_data_size + arg_count * 2 * sizeof(short); ++ func_desc.name = base_offset != -1 ? add_name(typelib, func->name) : -1; ++ func_desc.dispid = dispid; ++ func_desc.helpcontext = helpcontext; ++ func_desc.helpstring = (helpstring && base_offset != -1) ? add_name(typelib, helpstring) : -1; ++ func_desc.arg_off = arg_count ? base_offset + sizeof(func_desc) : -1; ++ func_desc.nacc = (arg_count << 3) | 4 /* CC_STDCALL */; ++ func_desc.retnextopt = (optional << 1); ++ if (ret_data.size > sizeof(short)) ++ { ++ func_desc.rettype = base_offset + sizeof(func_desc) + ret_desc_offset; ++ if (arg_count) ++ func_desc.arg_off += ret_data.size; ++ } ++ else ++ { ++ func_desc.retnextopt |= 0x80; ++ func_desc.rettype = *(short *)ret_data.data; ++ } ++ func_desc.vtblpos = idx * pointer_size; ++ func_desc.funcflags = funcflags; ++ ++ append_data(data, &func_desc, sizeof(func_desc)); ++ ++ arg_offset = base_offset + sizeof(struct sltg_function); ++ ++ if (ret_data.size > sizeof(short)) ++ { ++ append_data(data, ret_data.data, ret_data.size); ++ func_desc.arg_off += ret_data.size; ++ arg_offset += ret_data.size; ++ } ++ ++ if (arg_count) ++ { ++ int i = 0; ++ ++ arg_offset += arg_count * 2 * sizeof(short); ++ ++ LIST_FOR_EACH_ENTRY(arg, type_function_get_args(func->declspec.type), const var_t, entry) ++ { ++ short name, type_offset; ++ ++ name = base_offset != -1 ? add_name(typelib, arg->name) : -1; ++ ++ if (arg_data[i].size > sizeof(short)) ++ { ++ type_offset = (arg_offset + arg_desc_offset[i]); ++ arg_offset += arg_data[i].size; ++ } ++ else ++ { ++ name |= 1; ++ type_offset = *(short *)arg_data[i].data; ++ } ++ ++ append_data(data, &name, sizeof(name)); ++ append_data(data, &type_offset, sizeof(type_offset)); ++ ++ if (base_offset != -1) ++ chat("add_func_desc: arg[%d] - name %s (%#x), type_offset %#x\n", ++ i, arg->name, name, type_offset); ++ ++ i++; ++ } ++ ++ for (i = 0; i < arg_count; i++) ++ { ++ if (arg_data[i].size > sizeof(short)) ++ append_data(data, arg_data[i].data, arg_data[i].size); ++ } ++ } ++ ++ return data->size - old_size; ++} ++ ++static void write_impl_href(struct sltg_data *data, short href) ++{ ++ struct sltg_impl_info impl_info; ++ ++ impl_info.res00 = 0x004a; ++ impl_info.next = -1; ++ impl_info.res04 = -1; ++ impl_info.impltypeflags = 0; ++ impl_info.res07 = 0x80; ++ impl_info.res08 = 0x0012; ++ impl_info.ref = href; ++ impl_info.res0c = 0x4001; ++ impl_info.res0e = -2; /* 0xfffe */ ++ impl_info.res10 = -1; ++ impl_info.res12 = 0x001d; ++ impl_info.pos = 0; ++ ++ append_data(data, &impl_info, sizeof(impl_info)); ++} ++ ++static void add_interface_typeinfo(struct sltg_typelib *typelib, type_t *iface) ++{ ++ const statement_t *stmt_func; ++ importinfo_t *ref_importinfo = NULL; ++ short inherit_href = -1; ++ struct sltg_data data; ++ struct sltg_hrefmap hrefmap; ++ const char *index_name; ++ struct sltg_typeinfo_header ti; ++ struct sltg_member_header member; ++ struct sltg_tail tail; ++ int member_offset, base_offset, func_data_size, i; ++ int func_count, inherited_func_count = 0; ++ int dispid, inherit_level = 0; ++ ++ if (iface->typelib_idx != -1) return; ++ ++ chat("add_interface_typeinfo: type %p, type->name %s\n", iface, iface->name); ++ ++ if (!iface->details.iface) ++ { ++ error("interface %s is referenced but not defined\n", iface->name); ++ return; ++ } ++ ++ if (is_attr(iface->attrs, ATTR_DISPINTERFACE)) ++ { ++ error("support for dispinterface %s is not implemented\n", iface->name); ++ return; ++ } ++ ++ hrefmap.href_count = 0; ++ hrefmap.href = NULL; ++ ++ if (type_iface_get_inherit(iface)) ++ { ++ type_t *inherit; ++ ++ inherit = type_iface_get_inherit(iface); ++ ++ chat("add_interface_typeinfo: inheriting from base interface %s\n", inherit->name); ++ ++ ref_importinfo = find_importinfo(typelib->typelib, inherit->name); ++ ++ if (!ref_importinfo && type_iface_get_inherit(inherit)) ++ add_interface_typeinfo(typelib, inherit); ++ ++ if (ref_importinfo) ++ error("support for imported interfaces is not implemented\n"); ++ ++ inherit_href = local_href(&hrefmap, inherit->typelib_idx); ++ ++ while (inherit) ++ { ++ inherit_level++; ++ inherited_func_count += list_count(type_iface_get_stmts(inherit)); ++ inherit = type_iface_get_inherit(inherit); ++ } ++ } ++ ++ /* check typelib_idx again, it could have been added while resolving the parent interface */ ++ if (iface->typelib_idx != -1) return; ++ ++ iface->typelib_idx = typelib->n_file_blocks; ++ ++ /* pass 1: calculate function descriptions data size */ ++ init_sltg_data(&data); ++ ++ STATEMENTS_FOR_EACH_FUNC(stmt_func, type_iface_get_stmts(iface)) ++ { ++ add_func_desc(typelib, &data, stmt_func->u.var, -1, -1, -1, &hrefmap); ++ } ++ ++ func_data_size = data.size; ++ ++ /* pass 2: write function descriptions */ ++ init_sltg_data(&data); ++ ++ func_count = list_count(type_iface_get_stmts(iface)); ++ ++ index_name = add_typeinfo_block(typelib, iface, TKIND_INTERFACE); ++ ++ init_typeinfo(&ti, iface, TKIND_INTERFACE, &hrefmap); ++ append_data(&data, &ti, sizeof(ti)); ++ ++ write_hrefmap(&data, &hrefmap); ++ ++ member_offset = data.size; ++ base_offset = 0; ++ ++ member.res00 = 0x0001; ++ member.res02 = 0xffff; ++ member.res04 = 0x01; ++ member.extra = func_data_size; ++ if (inherit_href != -1) ++ { ++ member.extra += sizeof(struct sltg_impl_info); ++ base_offset += sizeof(struct sltg_impl_info); ++ } ++ append_data(&data, &member, sizeof(member)); ++ ++ if (inherit_href != -1) ++ write_impl_href(&data, inherit_href); ++ ++ i = 0; ++ dispid = 0x60000000 | (inherit_level << 16); ++ ++ STATEMENTS_FOR_EACH_FUNC(stmt_func, type_iface_get_stmts(iface)) ++ { ++ int idx = inherited_func_count + i; ++ ++ if (i == func_count - 1) idx |= 0x80000000; ++ ++ base_offset += add_func_desc(typelib, &data, stmt_func->u.var, ++ idx, dispid + i, base_offset, &hrefmap); ++ i++; ++ } ++ ++ init_sltg_tail(&tail); ++ ++ tail.cFuncs = func_count; ++ tail.funcs_off = 0; ++ tail.funcs_bytes = func_data_size; ++ tail.cbSizeInstance = pointer_size; ++ tail.cbAlignment = pointer_size; ++ tail.cbSizeVft = (inherited_func_count + func_count) * pointer_size; ++ tail.type_bytes = data.size - member_offset - sizeof(member); ++ tail.res24 = 0; ++ tail.res26 = 0; ++ if (inherit_href != -1) ++ { ++ tail.cImplTypes++; ++ tail.impls_off = 0; ++ tail.impls_bytes = 0; ++ ++ tail.funcs_off += sizeof(struct sltg_impl_info); ++ } ++ append_data(&data, &tail, sizeof(tail)); ++ ++ add_block(typelib, data.data, data.size, index_name); ++} ++ ++static void add_enum_typeinfo(struct sltg_typelib *typelib, type_t *type) ++{ ++ error("add_enum_typeinfo: %s not implemented\n", type->name); ++} ++ ++static void add_union_typeinfo(struct sltg_typelib *typelib, type_t *type) ++{ ++ error("add_union_typeinfo: %s not implemented\n", type->name); ++} ++ ++static void add_coclass_typeinfo(struct sltg_typelib *typelib, type_t *type) ++{ ++ error("add_coclass_typeinfo: %s not implemented\n", type->name); ++} ++ ++static void add_type_typeinfo(struct sltg_typelib *typelib, type_t *type) ++{ ++ chat("add_type_typeinfo: adding %s, type %d\n", type->name, type_get_type(type)); ++ ++ switch (type_get_type(type)) ++ { ++ case TYPE_INTERFACE: ++ add_interface_typeinfo(typelib, type); ++ break; ++ case TYPE_STRUCT: ++ add_structure_typeinfo(typelib, type); ++ break; ++ case TYPE_ENUM: ++ add_enum_typeinfo(typelib, type); ++ break; ++ case TYPE_UNION: ++ add_union_typeinfo(typelib, type); ++ break; ++ case TYPE_COCLASS: ++ add_coclass_typeinfo(typelib, type); ++ break; ++ case TYPE_BASIC: ++ case TYPE_POINTER: ++ break; ++ default: ++ error("add_type_typeinfo: unhandled type %d for %s\n", type_get_type(type), type->name); ++ break; ++ } ++} ++ ++static void add_statement(struct sltg_typelib *typelib, const statement_t *stmt) ++{ ++ switch(stmt->type) ++ { ++ case STMT_LIBRARY: ++ case STMT_IMPORT: ++ case STMT_PRAGMA: ++ case STMT_CPPQUOTE: ++ case STMT_DECLARATION: ++ /* not included in typelib */ ++ break; ++ case STMT_IMPORTLIB: ++ /* not processed here */ ++ break; ++ ++ case STMT_TYPEDEF: ++ { ++ const type_list_t *type_entry = stmt->u.type_list; ++ for (; type_entry; type_entry = type_entry->next) ++ { ++ /* in old style typelibs all types are public */ ++ add_type_typeinfo(typelib, type_entry->type); ++ } ++ break; ++ } ++ ++ case STMT_MODULE: ++ add_module_typeinfo(typelib, stmt->u.type); ++ break; ++ ++ case STMT_TYPE: ++ case STMT_TYPEREF: ++ { ++ type_t *type = stmt->u.type; ++ add_type_typeinfo(typelib, type); ++ break; ++ } ++ ++ default: ++ error("add_statement: unhandled statement type %d\n", stmt->type); ++ break; ++ } ++} ++ ++static void sltg_write_header(struct sltg_typelib *sltg, int *library_block_start) ++{ ++ char pad[0x40]; ++ struct sltg_header ++ { ++ int magic; ++ short n_file_blocks; ++ short res06; ++ short size_of_index; ++ short first_blk; ++ GUID uuid; ++ int res1c; ++ int res20; ++ } header; ++ struct sltg_block_entry ++ { ++ int length; ++ short index_string; ++ short next; ++ } entry; ++ struct sltg_block *block; ++ int i; ++ ++ header.magic = 0x47544c53; ++ header.n_file_blocks = sltg->n_file_blocks + 1; ++ header.res06 = 9; ++ header.size_of_index = sltg->index.size; ++ header.first_blk = 1; ++ header.uuid = sltg_library_guid; ++ header.res1c = 0x00000044; ++ header.res20 = 0xffff0000; ++ ++ put_data(&header, sizeof(header)); ++ ++ block = sltg->blocks; ++ for (i = 0; i < sltg->n_file_blocks - 1; i++) ++ { ++ assert(block->next != NULL); ++ ++ entry.length = block->length; ++ entry.index_string = block->index_string; ++ entry.next = header.first_blk + i + 1; ++ chat("sltg_write_header: writing block entry %d: length %#x, index_string %#x, next %#x\n", ++ i, entry.length, entry.index_string, entry.next); ++ put_data(&entry, sizeof(entry)); ++ ++ block = block->next; ++ } ++ ++ assert(block->next == NULL); ++ ++ /* library block length includes helpstrings and name table */ ++ entry.length = block->length + 0x40 + 2 + sltg->typeinfo_size + 4 + 6 + 12 + 0x200 + sltg->name_table.size + 12; ++ entry.index_string = block->index_string; ++ entry.next = 0; ++ chat("sltg_write_header: writing library block entry %d: length %#x, index_string %#x, next %#x\n", ++ i, entry.length, entry.index_string, entry.next); ++ put_data(&entry, sizeof(entry)); ++ ++ chat("sltg_write_header: writing index: %d bytes\n", sltg->index.size); ++ put_data(sltg->index.data, sltg->index.size); ++ memset(pad, 0, 9); ++ put_data(pad, 9); ++ ++ block = sltg->blocks; ++ for (i = 0; i < sltg->n_file_blocks - 1; i++) ++ { ++ chat("sltg_write_header: writing block %d: %d bytes\n", i, block->length); ++ ++ put_data(block->data, block->length); ++ block = block->next; ++ } ++ ++ assert(block->next == NULL); ++ ++ /* library block */ ++ chat("library_block_start = %#lx\n", (SIZE_T)output_buffer_pos); ++ *library_block_start = output_buffer_pos; ++ chat("sltg_write_header: writing library block %d: %d bytes\n", i, block->length); ++ put_data(block->data, block->length); ++ ++ chat("sltg_write_header: writing pad 0x40 bytes\n"); ++ memset(pad, 0xff, 0x40); ++ put_data(pad, 0x40); ++} ++ ++static void sltg_write_typeinfo(struct sltg_typelib *typelib) ++{ ++ int i; ++ struct sltg_block *block; ++ short count = typelib->typeinfo_count; ++ ++ put_data(&count, sizeof(count)); ++ ++ block = typelib->typeinfo; ++ for (i = 0; i < typelib->typeinfo_count; i++) ++ { ++ chat("sltg_write_typeinfo: writing block %d: %d bytes\n", i, block->length); ++ ++ put_data(block->data, block->length); ++ block = block->next; ++ } ++ assert(block == NULL); ++} ++ ++static void sltg_write_helpstrings(struct sltg_typelib *typelib) ++{ ++ static const char dummy[6]; ++ ++ chat("sltg_write_helpstrings: writing dummy 6 bytes\n"); ++ ++ put_data(dummy, sizeof(dummy)); ++} ++ ++static void sltg_write_nametable(struct sltg_typelib *typelib) ++{ ++ static const short dummy[6] = { 0xffff,1,2,0xff00,0xffff,0xffff }; ++ char pad[0x200]; ++ ++ chat("sltg_write_nametable: writing 12+0x200+%d bytes\n", typelib->name_table.size); ++ ++ put_data(dummy, sizeof(dummy)); ++ memset(pad, 0xff, 0x200); ++ put_data(pad, 0x200); ++ put_data(&typelib->name_table.size, sizeof(typelib->name_table.size)); ++ put_data(typelib->name_table.data, typelib->name_table.size); ++} ++ ++static void sltg_write_remainder(void) ++{ ++ static const short dummy1[] = { 1,0xfffe,0x0a03,0,0xffff,0xffff }; ++ static const short dummy2[] = { 0xffff,0xffff,0x0200,0,0,0 }; ++ static const char dummy3[] = { 0xf4,0x39,0xb2,0x71,0,0,0,0,0,0,0,0,0,0,0,0 }; ++ static const char TYPELIB[] = { 8,0,0,0,'T','Y','P','E','L','I','B',0 }; ++ int pad; ++ ++ pad = 0x01ffff01; ++ put_data(&pad, sizeof(pad)); ++ pad = 0; ++ put_data(&pad, sizeof(pad)); ++ ++ put_data(dummy1, sizeof(dummy1)); ++ ++ put_data(&sltg_library_guid, sizeof(sltg_library_guid)); ++ ++ put_data(TYPELIB, sizeof(TYPELIB)); ++ ++ put_data(dummy2, sizeof(dummy2)); ++ put_data(dummy3, sizeof(dummy3)); ++} ++ ++static void save_all_changes(struct sltg_typelib *typelib) ++{ ++ int library_block_start; ++ int *name_table_offset; ++ ++ sltg_write_header(typelib, &library_block_start); ++ sltg_write_typeinfo(typelib); ++ ++ name_table_offset = (int *)(output_buffer + output_buffer_pos); ++ chat("name_table_offset = %#lx\n", (SIZE_T)output_buffer_pos); ++ put_data(&library_block_start, sizeof(library_block_start)); ++ ++ sltg_write_helpstrings(typelib); ++ ++ *name_table_offset = output_buffer_pos - library_block_start; ++ chat("*name_table_offset = %#x\n", *name_table_offset); ++ ++ sltg_write_nametable(typelib); ++ sltg_write_remainder(); ++ ++ if (strendswith(typelib_name, ".res")) /* create a binary resource file */ ++ { ++ char typelib_id[13] = "#1"; ++ ++ expr_t *expr = get_attrp(typelib->typelib->attrs, ATTR_ID); ++ if (expr) ++ sprintf(typelib_id, "#%d", expr->cval); ++ add_output_to_resources("TYPELIB", typelib_id); ++ output_typelib_regscript(typelib->typelib); ++ } ++ else flush_output_buffer(typelib_name); ++} ++ ++int create_sltg_typelib(typelib_t *typelib) ++{ ++ struct sltg_typelib sltg; ++ const statement_t *stmt; ++ void *library_block; ++ int library_block_size, library_block_index; ++ ++ sltg.typelib = typelib; ++ sltg.typeinfo_count = 0; ++ sltg.typeinfo_size = 0; ++ sltg.typeinfo = NULL; ++ sltg.blocks = NULL; ++ sltg.n_file_blocks = 0; ++ sltg.first_block = 1; ++ ++ init_index(&sltg.index); ++ init_name_table(&sltg); ++ init_library(&sltg); ++ ++ library_block = create_library_block(&sltg, &library_block_size, &library_block_index); ++ ++ if (typelib->stmts) ++ LIST_FOR_EACH_ENTRY(stmt, typelib->stmts, const statement_t, entry) ++ add_statement(&sltg, stmt); ++ ++ add_block_index(&sltg, library_block, library_block_size, library_block_index); ++ ++ save_all_changes(&sltg); ++ ++ return 1; ++} diff --git a/scripts/clang-target-wrapper b/scripts/clang-target-wrapper new file mode 100755 index 0000000..58043e1 --- /dev/null +++ b/scripts/clang-target-wrapper @@ -0,0 +1,42 @@ +#!/bin/sh +# PROJECT: XTchain +# LICENSE: See the COPYING.md in the top level directory +# FILE: scripts/clang-target-wrapper +# DESCRIPTION: CLANG Wrapper +# DEVELOPERS: Martin Storsjo +# Rafal Kupiec + + +# Set basic variables +DIR="$(cd $(dirname $0) && pwd)" +CLANG="$DIR/clang" +BASENAME="$(basename $0)" +TARGET="${BASENAME%-*}" +EXECUTABLE="${BASENAME##*-}" +DEFAULT_TARGET="x86_64-w64-mingw32" +ARCH="${TARGET%%-*}" + +# Set proper target +if [ "${TARGET}" = "${BASENAME}" ]; then + TARGET="${DEFAULT_TARGET}" +fi + +# Set lang-specific flags +case ${EXECUTABLE} in + "clang++"|"g++"|"c++") + FLAGS="$FLAGS --driver-mode=g++" + ;; + *) + FLAGS="" + ;; +esac + +# Set compiler flags +FLAGS="${FLAGS} -target ${TARGET}" +FLAGS="${FLAGS} -rtlib=compiler-rt" +FLAGS="${FLAGS} -stdlib=libc++" +FLAGS="${FLAGS} -fuse-ld=lld" +FLAGS="${FLAGS} -Qunused-arguments" + +# Launch the compiler +$CLANG $FLAGS "$@" diff --git a/scripts/dlltool-wrapper b/scripts/dlltool-wrapper new file mode 100755 index 0000000..09db3a3 --- /dev/null +++ b/scripts/dlltool-wrapper @@ -0,0 +1,42 @@ +#!/bin/sh +# PROJECT: XTchain +# LICENSE: See the COPYING.md in the top level directory +# FILE: scripts/dlltool-wrapper +# DESCRIPTION: DLLTOOL Wrapper +# DEVELOPERS: Martin Storsjo +# Rafal Kupiec + + +# Set basic variables +DIR="$(cd $(dirname $0) && pwd)" +BASENAME="$(basename $0)" +TARGET="${BASENAME%-*}" +DEFAULT_TARGET="x86_64-w64-mingw32" + +# Update PATH +export PATH="$DIR":"$PATH" + +# Set proper target +if [ "${TARGET}" = "${BASENAME}" ]; then + TARGET="${DEFAULT_TARGET}" +fi + +# Set target machine +ARCH="${TARGET%%-*}" +case $ARCH in + aarch64) + M="arm64" + ;; + armv7) + M="arm" + ;; + i686) + M="i386" + ;; + x86_64) + M="i386:x86-64" + ;; +esac + +# Launch the utility +llvm-dlltool -m ${M} "$@" diff --git a/scripts/ld-wrapper b/scripts/ld-wrapper new file mode 100755 index 0000000..6fa9d39 --- /dev/null +++ b/scripts/ld-wrapper @@ -0,0 +1,42 @@ +#!/bin/sh +# PROJECT: XTchain +# LICENSE: See the COPYING.md in the top level directory +# FILE: scripts/ld-wrapper +# DESCRIPTION: LLD Wrapper +# DEVELOPERS: Martin Storsjo +# Rafal Kupiec + + +# Set basic variables +DIR="$(cd $(dirname $0) && pwd)" +BASENAME="$(basename $0)" +TARGET="${BASENAME%-*}" +DEFAULT_TARGET="x86_64-w64-mingw32" + +# Update PATH +export PATH="${DIR}":"${PATH}" + +# Set proper target +if [ "${TARGET}" = "${BASENAME}" ]; then + TARGET="${DEFAULT_TARGET}" +fi + +# Set target machine +ARCH="${TARGET%%-*}" +case ${ARCH} in + aarch64) + M="arm64pe" + ;; + armv7) + M="thumb2pe" + ;; + i686) + M="i386pe" + ;; + x86_64) + M="i386pep" + ;; +esac + +# Launch the linker +ld.lld -m ${M} "$@" diff --git a/scripts/objdump-wrapper b/scripts/objdump-wrapper new file mode 100755 index 0000000..977551c --- /dev/null +++ b/scripts/objdump-wrapper @@ -0,0 +1,46 @@ +#!/bin/sh +# PROJECT: XTchain +# LICENSE: See the COPYING.md in the top level directory +# FILE: scripts/objdump-wrapper +# DESCRIPTION: OBJDUMP Wrapper +# DEVELOPERS: Martin Storsjo +# Rafal Kupiec + + +# Set basic variables +DIR="$(cd $(dirname $0) && pwd)" + +# Update PATH +export PATH="$DIR":"$PATH" + +# Libtool can try to run objdump -f and wants to see certain strings in +# the output, to accept it being a valid windows (import) library +if [ "$1" = "-f" ]; then + llvm-readobj $2 | while read -r line; do + case $line in + File:*) + file=$(echo $line | awk '{print $2}') + ;; + Format:*) + format=$(echo $line | awk '{print $2}') + case $format in + COFF-i386) + format=pe-i386 + ;; + COFF-x86-64) + format=pe-x86-64 + ;; + COFF-ARM*) + # This is wrong; modern COFF armv7 isn't pe-arm-wince, and + # arm64 definitely isn't, but libtool wants to see this + # string (or some of the others) in order to accept it. + format=pe-arm-wince + ;; + esac + echo $file: file format $format + ;; + esac + done +else + llvm-objdump "$@" +fi diff --git a/scripts/xtchain b/scripts/xtchain new file mode 100755 index 0000000..63f6015 --- /dev/null +++ b/scripts/xtchain @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# PROJECT: XTchain +# LICENSE: See the COPYING.md in the top level directory +# FILE: scripts/xtchain +# DESCRIPTION: XTchain Entry Script +# DEVELOPERS: Rafal Kupiec + + +# Check if script launched as root +if [ "$(whoami)" = "root" ]; then + echo "This script cannot be run as root!" + exit 1 +fi + +# Get the absolute path to the XTchain +export XTCDIR="$(realpath $(dirname ${0}))" + +# Read the XTchain version +export XTCVER="$(cat ${XTCDIR}/Version)" + +# Load the library +source ${XTCDIR}/lib/xtchain/xtclib + +# Set the target architecture +: ${TARGET:=${1}} +: ${TARGET:=amd64} + +# Save the source directory +export SRCDIR="${2:-${PWD}}" + +# Make sure the compiler flags are clean +export HOST= +export CFLAGS= +export CXXFLAGS= +export LDFLAGS= + +# Update PATH +export PATH="${XTCDIR}/bin:${PATH}" + +# Display banner +version + +# Invoke shell with fancy prompt +export PFMAT1="\[\033[0;1;97;44m\]" +export PFMAT2="\[\033[0;34;104m\]" +export PFMAT3="\[\033[0;1;97;104m\]" +export PFMAT4="\[\033[0;94;49m\]" +export PFMAT5="\[\033[1;38;5;74m\]" +export PROMPT="\n${PFMAT1} XT Toolchain ${PFMAT2}${PFMAT3} \w ${PFMAT4}${PFMAT5} " +bash --rcfile <(echo 'source ~/.bashrc && export PS1="${PROMPT}" && cd ${SRCDIR}') diff --git a/scripts/xtclib b/scripts/xtclib new file mode 100755 index 0000000..b61f489 --- /dev/null +++ b/scripts/xtclib @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# PROJECT: XTchain +# LICENSE: See the COPYING.md in the top level directory +# FILE: scripts/xtclib +# DESCRIPTION: XTchain library +# DEVELOPERS: Rafal Kupiec + + +# Sets the target architecture +charch() +{ + if [ "x${1}" == "x" ]; then + echo "Syntax: charch [architecture]" + return + fi + case ${1} in + "aarch64"|"arm64") + export TARGET="aarch64" + ;; + "arm"|"armv7") + export TARGET="armv7" + ;; + "i386"|"i486"|"i586"|"i686"|"x86") + export TARGET="i686" + ;; + "amd64"|"x64"|"x86_64") + export TARGET="amd64" + ;; + *) + export TARGET="UNKNOWN" + esac + echo "Target Architecture: ${TARGET}" +} +export -f charch + +# Sets the build type +chbuild() +{ + if [ "x${1}" == "x" ]; then + echo "Syntax: chbuild [DEBUG|RELEASE]" + return + fi + case ${1} in + [Rr][Ee][Ll][Ee][Aa][Ss][Ee]) + export BUILD_TYPE="RELEASE" + ;; + *) + export BUILD_TYPE="DEBUG" + esac + echo "Target build type: ${BUILD_TYPE}" +} +export -f chbuild + +# Displays version banner +version() +{ + echo "###############################################################################" + echo "# XT Toolchain v${XTCVER} for Linux #" + echo "# by Rafal Kupiec #" + echo "###############################################################################" + echo + echo + echo "LLVM Compiler Version: $(${XTCDIR}/bin/clang --version | grep 'clang version' | cut -d' ' -f3)" + echo "LLVM Windres Utility Version: $(${XTCDIR}/bin/i686-w64-mingw32-windres -V | cut -d' ' -f6)" + echo "Mingw IDL Compiler Version: $(${XTCDIR}/bin/i686-w64-mingw32-widl -V | grep 'version' | cut -d' ' -f5)" + echo "Wine Message Compiler Version: $(${XTCDIR}/bin/wmc -V | grep 'version' | cut -d' ' -f5)" + echo "Wine Resource Compiler Version: $(${XTCDIR}/bin/wrc --version | grep 'version' | cut -d' ' -f5)" + echo + charch ${TARGET} + chbuild DEBUG + echo + echo +} +export -f version diff --git a/tools/windres.c b/tools/windres.c new file mode 100644 index 0000000..7a07360 --- /dev/null +++ b/tools/windres.c @@ -0,0 +1,558 @@ +/** + * PROJECT: XTchain + * LICENSE: See COPYING.md in the top level directory + * FILE: tools/windres.c + * DESCRIPTION: WINDRES compatible interface to LLVM + * DEVELOPERS: Josh de Kock + * Martin Storsjo + * Rafal Kupiec + */ + +#include "xtchain.h" + +#define WINDRES_VERSION "1.0" + +#ifndef DEFAULT_TARGET +#define DEFAULT_TARGET "x86_64-w64-mingw32" +#endif + +#include + +#define _tspawnvp_escape _spawnvp + +#include +#include + +#define _P_WAIT 0 + +static +int +_spawnvp(int mode, + const char *filename, + const char * const *argv) +{ + pid_t pid; + + if(!(pid = fork())) + { + execvp(filename, (char **) argv); + perror(filename); + exit(1); + } + + int stat = 0; + + if(waitpid(pid, &stat, 0) == -1) + { + return -1; + } + + if(WIFEXITED(stat)) + { + return WEXITSTATUS(stat); + } + errno = EIO; + + return -1; +} + +static +const +char *unescape_cpp(const char *str) +{ + char *out = strdup(str); + int len = strlen(str); + int i, outpos = 0; + + for(i = 0; i < len - 1; i++) + { + if(str[i] == '\\' && str[i + 1] == '"') + { + continue; + } + out[outpos++] = str[i]; + } + + while(i < len) + { + out[outpos++] = str[i++]; + } + + out[outpos++] = '\0'; + + return out; +} + +static +void print_version(void) +{ + printf("XTchain windres (GNU windres compatible) %s\n", WINDRES_VERSION); + exit(0); +} + +static +void print_help(void) +{ + printf( + "usage: llvm-windres