diff --git a/configure b/configure index 2c8d78598b2d3..53d1cc97a61de 100755 --- a/configure +++ b/configure @@ -582,6 +582,7 @@ opt rpath 0 "build rpaths into rustc itself" opt dist-host-only 0 "only install bins for the host architecture" opt inject-std-version 1 "inject the current compiler version of libstd into programs" opt llvm-version-check 1 "check if the LLVM version is supported, build anyway" +valopt nacl-cross-path "" "NaCl SDK path (must be Pepper 43 or higher). Must be absolute!" # Optimization and debugging options. These may be overridden by the release channel, etc. opt_nosave optimize 1 "build optimized rust code" @@ -1131,7 +1132,12 @@ do fi done ;; - + *-unknown-nacl) + if [ -z "$CFG_NACL_CROSS_PATH" ] + then + err "I need the NaCl SDK path! (use --nacl-cross-path)" + fi + ;; arm-apple-darwin) if [ $CFG_OSTYPE != apple-darwin ] then @@ -1390,12 +1396,158 @@ for t in $CFG_HOST do do_reconfigure=1 is_msvc=0 + + # LLVM's configure doesn't recognize the new Windows triples yet + gnu_t=$(to_gnu_triple $t) + if [ "$t" != "$CFG_BUILD" ] + then + LLVM_PREFIX="$gnu_t-" + else + LLVM_PREFIX="" + fi + + LLVM_AR="ar" + LLVM_RANLIB="ranlib" + + case "$CFG_CC" in + ("ccache clang") + LLVM_CXX_32="ccache ${LLVM_PREFIX}clang++ -Qunused-arguments" + LLVM_CC_32="ccache ${LLVM_PREFIX}clang -Qunused-arguments" + + LLVM_CXX_64="ccache ${LLVM_PREFIX}clang++ -Qunused-arguments" + LLVM_CC_64="ccache ${LLVM_PREFIX}clang -Qunused-arguments" + ;; + ("clang") + LLVM_CXX_32="${LLVM_PREFIX}clang++ -Qunused-arguments" + LLVM_CC_32="${LLVM_PREFIX}clang -Qunused-arguments" + + LLVM_CXX_64="${LLVM_PREFIX}clang++ -Qunused-arguments" + LLVM_CC_64="${LLVM_PREFIX}clang -Qunused-arguments" + ;; + ("ccache gcc") + LLVM_CXX_32="ccache ${LLVM_PREFIX}g++" + LLVM_CC_32="ccache ${LLVM_PREFIX}gcc" + + LLVM_CXX_64="ccache ${LLVM_PREFIX}g++" + LLVM_CC_64="ccache ${LLVM_PREFIX}gcc" + ;; + ("gcc") + LLVM_CXX_32="${LLVM_PREFIX}g++" + LLVM_CC_32="${LLVM_PREFIX}gcc" + + LLVM_CXX_64="${LLVM_PREFIX}g++" + LLVM_CC_64="${LLVM_PREFIX}gcc" + ;; + + (*) + msg "inferring LLVM_CXX/CC from CXX/CC = $CXX/$CC" + if [ -n "$CFG_ENABLE_CCACHE" ] + then + if [ -z "$CFG_CCACHE" ] + then + err "ccache requested but not found" + fi + + LLVM_CXX_32="ccache $CXX" + LLVM_CC_32="ccache $CC" + + LLVM_CXX_64="ccache $CXX" + LLVM_CC_64="ccache $CC" + else + LLVM_CXX_32="$CXX" + LLVM_CC_32="$CC" + + LLVM_CXX_64="$CXX" + LLVM_CC_64="$CC" + fi + + ;; + esac + + case "$CFG_CPUTYPE" in + (x86*) + LLVM_CFLAGS_32="-m32" + LLVM_CXXFLAGS_32="-m32" + LLVM_LDFLAGS_32="-m32" + + LLVM_CFLAGS_64="" + LLVM_CXXFLAGS_64="" + LLVM_LDFLAGS_64="" + ;; + + (*) + LLVM_CFLAGS_32="" + LLVM_CXXFLAGS_32="" + LLVM_LDFLAGS_32="" + + LLVM_CFLAGS_64="" + LLVM_CXXFLAGS_64="" + LLVM_LDFLAGS_64="" + ;; + esac + + if echo $t | grep -q x86_64 + then + LLVM_CXX=$LLVM_CXX_64 + LLVM_CC=$LLVM_CC_64 + LLVM_CFLAGS=$LLVM_CFLAGS_64 + LLVM_CXXFLAGS=$LLVM_CXXFLAGS_64 + LLVM_LDFLAGS=$LLVM_LDFLAGS_64 + else + LLVM_CXX=$LLVM_CXX_32 + LLVM_CC=$LLVM_CC_32 + LLVM_CFLAGS=$LLVM_CFLAGS_32 + LLVM_CXXFLAGS=$LLVM_CXXFLAGS_32 + LLVM_LDFLAGS=$LLVM_LDFLAGS_32 + fi + + # Toolchain overrides for specific hosts case "$t" in (*-msvc) - is_msvc=1 + is_msvc=1 + ;; + (le32-unknown-nacl) + BUILD_CXX=clang++ + BUILD_CC=clang + BUILD_CXXFLAGS=$LLVM_CXXFLAGS + BUILD_CFLAGS=$LLVM_CFLAGS + + export BUILD_CXX + export BUILD_CC + export BUILD_CXXFLAGS + export BUILD_CFLAGS + + LLVM_CXX="`$CFG_NACL_CROSS_PATH/tools/nacl_config.py -t pnacl --tool=c++`" + LLVM_CC="`$CFG_NACL_CROSS_PATH/tools/nacl_config.py -t pnacl --tool=cc`" + LLVM_RANLIB="`$CFG_NACL_CROSS_PATH/tools/nacl_config.py -t pnacl --tool=ranlib`" + LLVM_CFLAGS="-I$NACL_SDK_ROOT/toolchain/$($NACL_SDK_ROOT/tools/getos.py)_pnacl/le32-nacl/usr/include/glibc-compat" + LLVM_CXXFLAGS=$LLVM_CFLAGS + LLVM_AR="`$CFG_NACL_CROSS_PATH/tools/nacl_config.py -t pnacl --tool=ar`" + ;; + (*) + unset BUILD_CXX + unset BUILD_CC + unset BUILD_CXXFLAGS + unset BUILD_CFLAGS ;; esac + CXX=$LLVM_CXX + CC=$LLVM_CC + AR=$LLVM_AR + RANLIB=$LLVM_RANLIB + CFLAGS=$LLVM_CFLAGS + CXXFLAGS=$LLVM_CXXFLAGS + LDFLAGS=$LLVM_LDFLAGS + + export CXX + export CC + export AR + export RANLIB + export CFLAGS + export CXXFLAGS + export LDFLAGS + if [ -z $CFG_LLVM_ROOT ] then LLVM_BUILD_DIR=${CFG_BUILD_DIR}$t/llvm @@ -1404,10 +1556,10 @@ do LLVM_DBG_OPTS="--enable-debug-symbols --disable-optimized" # Just use LLVM straight from its build directory to # avoid 'make install' time - LLVM_INST_DIR=$LLVM_BUILD_DIR/Debug + LLVM_BUILD_SUBDIR=Debug else LLVM_DBG_OPTS="--enable-optimized" - LLVM_INST_DIR=$LLVM_BUILD_DIR/Release + LLVM_BUILD_SUBDIR=Release fi if [ -z "$CFG_ENABLE_LLVM_ASSERTIONS" ] then @@ -1419,9 +1571,13 @@ do # LLVM's CMake build system ignore this and outputs in `Release` # anyway. if [ ${is_msvc} -eq 0 ]; then - LLVM_INST_DIR=${LLVM_INST_DIR}+Asserts + LLVM_BUILD_SUBDIR=${LLVM_BUILD_SUBDIR}+Asserts + else + LLVM_BUILD_SUBDIR=${LLVM_BUILD_SUBDIR} fi fi + + LLVM_INST_DIR=$LLVM_BUILD_DIR/$LLVM_BUILD_SUBDIR else msg "not reconfiguring LLVM, external LLVM root" # The user is using their own LLVM @@ -1498,18 +1654,27 @@ do if [ ${do_reconfigure} -ne 0 ] && [ ${is_msvc} -eq 0 ] then - # LLVM's configure doesn't recognize the new Windows triples yet - gnu_t=$(to_gnu_triple $t) - msg "configuring LLVM for $gnu_t" - LLVM_TARGETS="--enable-targets=x86,x86_64,arm,aarch64,mips,powerpc" - LLVM_BUILD="--build=$gnu_t" + LLVM_BUILD="--build=$(to_gnu_triple $CFG_BUILD)" LLVM_HOST="--host=$gnu_t" LLVM_TARGET="--target=$gnu_t" # Disable unused LLVM features LLVM_OPTS="$LLVM_DBG_OPTS $LLVM_ASSERTION_OPTS --disable-docs --enable-bindings=none" + + case "$t" in + (le32-unknown-nacl) + # Enable only the arches that NaCl can sandbox. + LLVM_TARGETS="--enable-targets=x86,arm,mips" + # Use libc++ to match Rust. + LLVM_OPTS="$LLVM_OPTS --enable-libcpp" + ;; + (*) + LLVM_TARGETS="--enable-targets=x86,x86_64,arm,aarch64,mips,powerpc" + ;; + esac + # Disable term-info, linkage of which comes in multiple forms, # making our snapshots incompatible (#9334) LLVM_OPTS="$LLVM_OPTS --disable-terminfo" @@ -1525,110 +1690,6 @@ do ;; esac - case "$CFG_CC" in - ("ccache clang") - LLVM_CXX_32="ccache clang++ -Qunused-arguments" - LLVM_CC_32="ccache clang -Qunused-arguments" - - LLVM_CXX_64="ccache clang++ -Qunused-arguments" - LLVM_CC_64="ccache clang -Qunused-arguments" - ;; - ("clang") - LLVM_CXX_32="clang++ -Qunused-arguments" - LLVM_CC_32="clang -Qunused-arguments" - - LLVM_CXX_64="clang++ -Qunused-arguments" - LLVM_CC_64="clang -Qunused-arguments" - ;; - ("ccache gcc") - LLVM_CXX_32="ccache g++" - LLVM_CC_32="ccache gcc" - - LLVM_CXX_64="ccache g++" - LLVM_CC_64="ccache gcc" - ;; - ("gcc") - LLVM_CXX_32="g++" - LLVM_CC_32="gcc" - - LLVM_CXX_64="g++" - LLVM_CC_64="gcc" - ;; - - (*) - msg "inferring LLVM_CXX/CC from CXX/CC = $CXX/$CC" - if [ -n "$CFG_ENABLE_CCACHE" ] - then - if [ -z "$CFG_CCACHE" ] - then - err "ccache requested but not found" - fi - - LLVM_CXX_32="ccache $CXX" - LLVM_CC_32="ccache $CC" - - LLVM_CXX_64="ccache $CXX" - LLVM_CC_64="ccache $CC" - else - LLVM_CXX_32="$CXX" - LLVM_CC_32="$CC" - - LLVM_CXX_64="$CXX" - LLVM_CC_64="$CC" - fi - - ;; - esac - - case "$CFG_CPUTYPE" in - (x86*) - LLVM_CXX_32="$LLVM_CXX_32 -m32" - LLVM_CC_32="$LLVM_CC_32 -m32" - - LLVM_CFLAGS_32="-m32" - LLVM_CXXFLAGS_32="-m32" - LLVM_LDFLAGS_32="-m32" - - LLVM_CFLAGS_64="" - LLVM_CXXFLAGS_64="" - LLVM_LDFLAGS_64="" - - LLVM_CXX_32="$LLVM_CXX_32 -m32" - LLVM_CC_32="$LLVM_CC_32 -m32" - ;; - - (*) - LLVM_CFLAGS_32="" - LLVM_CXXFLAGS_32="" - LLVM_LDFLAGS_32="" - - LLVM_CFLAGS_64="" - LLVM_CXXFLAGS_64="" - LLVM_LDFLAGS_64="" - ;; - esac - - if echo $t | grep -q x86_64 - then - LLVM_CXX=$LLVM_CXX_64 - LLVM_CC=$LLVM_CC_64 - LLVM_CFLAGS=$LLVM_CFLAGS_64 - LLVM_CXXFLAGS=$LLVM_CXXFLAGS_64 - LLVM_LDFLAGS=$LLVM_LDFLAGS_64 - else - LLVM_CXX=$LLVM_CXX_32 - LLVM_CC=$LLVM_CC_32 - LLVM_CFLAGS=$LLVM_CFLAGS_32 - LLVM_CXXFLAGS=$LLVM_CXXFLAGS_32 - LLVM_LDFLAGS=$LLVM_LDFLAGS_32 - fi - - CXX=$LLVM_CXX - CC=$LLVM_CC - CFLAGS=$LLVM_CFLAGS - CXXFLAGS=$LLVM_CXXFLAGS - LDFLAGS=$LLVM_LDFLAGS - if [ -z "$CFG_DISABLE_LIBCPP" ] && [ -n "$CFG_USING_CLANG" ]; then LLVM_OPTS="$LLVM_OPTS --enable-libcpp" fi @@ -1639,12 +1700,6 @@ do msg "configuring LLVM with:" msg "$LLVM_FLAGS" - export CXX - export CC - export CFLAGS - export CXXFLAGS - export LDFLAGS - cd $LLVM_BUILD_DIR case $CFG_SRC_DIR in /* | [a-z]:* | [A-Z]:*) @@ -1667,8 +1722,10 @@ do # variables can't contain hyphens. The makefile will then have to # convert back. CFG_LLVM_BUILD_DIR=$(echo CFG_LLVM_BUILD_DIR_${t} | tr - _) + CFG_LLVM_BUILD_SUBDIR=$(echo CFG_LLVM_BUILD_SUBDIR_${t} | tr - _) CFG_LLVM_INST_DIR=$(echo CFG_LLVM_INST_DIR_${t} | tr - _) eval ${CFG_LLVM_BUILD_DIR}="'$LLVM_BUILD_DIR'" + eval ${CFG_LLVM_BUILD_SUBDIR}="'$LLVM_BUILD_SUBDIR'" eval ${CFG_LLVM_INST_DIR}="'$LLVM_INST_DIR'" done @@ -1688,6 +1745,7 @@ putvar CFG_LIBDIR_RELATIVE putvar CFG_DISABLE_MANAGE_SUBMODULES putvar CFG_AARCH64_LINUX_ANDROID_NDK putvar CFG_ARM_LINUX_ANDROIDEABI_NDK +putvar CFG_NACL_CROSS_PATH putvar CFG_MANDIR # Avoid spurious warnings from clang by feeding it original source on @@ -1716,8 +1774,10 @@ putvar CFG_LLVM_SRC_DIR for t in $CFG_HOST do CFG_LLVM_BUILD_DIR=$(echo CFG_LLVM_BUILD_DIR_${t} | tr - _) + CFG_LLVM_BUILD_SUBDIR=$(echo CFG_LLVM_BUILD_SUBDIR_${t} | tr - _) CFG_LLVM_INST_DIR=$(echo CFG_LLVM_INST_DIR_${t} | tr - _) putvar $CFG_LLVM_BUILD_DIR + putvar $CFG_LLVM_BUILD_SUBDIR putvar $CFG_LLVM_INST_DIR done diff --git a/mk/cfg/le32-unknown-nacl.mk b/mk/cfg/le32-unknown-nacl.mk new file mode 100644 index 0000000000000..33a59575cff28 --- /dev/null +++ b/mk/cfg/le32-unknown-nacl.mk @@ -0,0 +1,42 @@ +# le32-unknown-nacl (portable, PNaCl) +CROSS_PREFIX_le32-unknown-nacl:=$(CFG_PNACL_TOOLCHAIN)/bin/pnacl- +CC_le32-unknown-nacl=clang +CXX_le32-unknown-nacl=clang++ +CPP_le32-unknown-nacl=$(CXX_le32-unknown-nacl) -E +AR_le32-unknown-nacl=ar + +# Note: pso's aren't supported by PNaCl or Rust yet. +CFG_LIB_NAME_le32-unknown-nacl=lib$(1).pso +CFG_STATIC_LIB_NAME_le32-unknown-nacl=lib$(1).a +CFG_LIB_GLOB_le32-unknown-nacl=lib$(1)-*.pso +CFG_LIB_DSYM_GLOB_le32-unknown-nacl=lib$(1)-*.dylib.dSYM +CFG_CFLAGS_le32-unknown-nacl := -Wall -Wno-unused-variable -Wno-unused-value -I$(CFG_NACL_CROSS_PATH)/include -I$(CFG_NACL_CROSS_PATH)/include/pnacl -D_YUGA_LITTLE_ENDIAN=1 -D_YUGA_BIG_ENDIAN=0 -Os +CFG_CXXFLAGS_le32-unknown-nacl := -stdlib=libc++ $(CFG_CFLAGS_le32-unknown-nacl) +CFG_GCCISH_CFLAGS_le32-unknown-nacl := $(CFG_CFLAGS_le32-unknown-nacl) +CFG_GCCISH_CXXFLAGS_le32-unknown-nacl := $(CFG_CXXFLAGS_le32-unknown-nacl) +CFG_GCCISH_LINK_FLAGS_le32-unknown-nacl := -static -pthread -lm +CFG_GCCISH_DEF_FLAG_le32-unknown-nacl := -Wl,--export-dynamic,--dynamic-list= +CFG_GCCISH_PRE_LIB_FLAGS_le32-unknown-nacl := -Wl,-no-whole-archive +CFG_GCCISH_POST_LIB_FLAGS_le32-unknown-nacl := +CFG_DEF_SUFFIX_le32-unknown-nacl := .le32.nacl.def +CFG_INSTALL_NAME_le32-unknown-nacl = +CFG_LIBUV_LINK_FLAGS_le32-unknown-nacl = +CFG_DISABLE_LIBUV_le32-unknown-nacl := 1 +CFG_EXE_SUFFIX_le32-unknown-nacl = .pexe +CFG_WINDOWSY_le32-unknown-nacl := +CFG_UNIXY_le32-unknown-nacl := 1 +CFG_NACLY_le32-unknown-nacl := 1 +CFG_PATH_MUNGE_le32-unknown-nacl := true +CFG_LDPATH_le32-unknown-nacl := +CFG_RUN_le32-unknown-nacl=$(2) +CFG_RUN_TARG_le32-unknown-nacl=$(call CFG_RUN_le32-unknown-nacl,,$(2)) +SHARED_LIBS_DISABLED_le32-unknown-nacl := 1 +RUNTIME_CFLAGS_le32-unknown-nacl:= -I$(CFG_NACL_CROSS_PATH)/include/pnacl +RUNTIME_DISABLE_ASM_le32-unknown-nacl := 1 +RUSTC_FLAGS_le32-unknown-nacl:= +RUSTC_CROSS_FLAGS_le32-unknown-nacl=-C cross-path=$(CFG_NACL_CROSS_PATH) -L $(CFG_NACL_CROSS_PATH)/lib/pnacl/Release -L $(CFG_PNACL_TOOLCHAIN)/lib/clang/3.7.0/lib/le32-nacl -L $(CFG_PNACL_TOOLCHAIN)/le32-nacl/usr/lib -L $(CFG_PNACL_TOOLCHAIN)/le32-nacl/lib +CFG_GNU_TRIPLE_le32-unknown-nacl := le32-unknown-nacl + +# strdup isn't defined unless -std=gnu++11 is used +LLVM_FILTER_CXXFLAGS_le32-unknown-nacl := -std=c++11 +LLVM_EXTRA_CXXFLAGS_le32-unknown-nacl := -std=gnu++11 diff --git a/mk/llvm.mk b/mk/llvm.mk index d5b608e88daf8..e89d31f06c58a 100644 --- a/mk/llvm.mk +++ b/mk/llvm.mk @@ -82,7 +82,7 @@ endif # LLVM linkage: LLVM_LINKAGE_PATH_$(1):=$$(abspath $$(RT_OUTPUT_DIR_$(1))/llvmdeps.rs) $$(LLVM_LINKAGE_PATH_$(1)): $(S)src/etc/mklldeps.py $$(LLVM_CONFIG_$(1)) - $(Q)$(CFG_PYTHON) "$$<" "$$@" "$$(LLVM_COMPONENTS)" "$$(CFG_ENABLE_LLVM_STATIC_STDCPP)" \ + $(Q)$(CFG_PYTHON) "$$<" "$$@" "$$(filter-out $$(LLVM_DISABLED_TARGETS_$(1)),$$(LLVM_COMPONENTS))" "$$(CFG_ENABLE_LLVM_STATIC_STDCPP)" "$$(CFG_ENABLE_LLVM_LIBCPP)" \ $$(LLVM_CONFIG_$(1)) endef @@ -95,6 +95,8 @@ $(foreach host,$(CFG_HOST), \ # This can't be done in target.mk because it's included before this file. define LLVM_LINKAGE_DEPS $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.rustc_llvm: $$(LLVM_LINKAGE_PATH_$(2)) +RUSTFLAGS_rustc_llvm_T_$(3) += $$(shell $$(LLVM_CONFIG_$(3)) --components | tr '-' '_' | \ + sed 's/ \([^ ]*\)/\-\-cfg have_component_\1 /g') endef $(foreach source,$(CFG_HOST), \ diff --git a/mk/main.mk b/mk/main.mk index a9dcaaf9655ea..d214b27e52b9d 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -273,6 +273,12 @@ endif LLVM_COMPONENTS=x86 arm aarch64 mips powerpc ipo bitreader bitwriter linker asmparser mcjit \ interpreter instrumentation +ifneq ($(CFG_LLVM_ROOT),) +# Ensure we only try to link components that the installed LLVM actually has: +LLVM_COMPONENTS:=$(filter $(shell $(CFG_LLVM_ROOT)/bin/llvm-config$(X_$(CFG_BUILD)) --components),\ + $(LLVM_COMPONENTS)) +endif + # Only build these LLVM tools LLVM_TOOLS=bugpoint llc llvm-ar llvm-as llvm-dis llvm-mc opt llvm-extract @@ -282,12 +288,22 @@ define DEF_LLVM_VARS ifeq ($$(CFG_LLVM_ROOT),) CFG_LLVM_BUILD_DIR_$(1):=$$(CFG_LLVM_BUILD_DIR_$(subst -,_,$(1))) CFG_LLVM_INST_DIR_$(1):=$$(CFG_LLVM_INST_DIR_$(subst -,_,$(1))) + +# We need to use a copy of llvm-config that can run on the build machine: +ifneq ($$(CFG_BUILD),$(1)) +LLVM_CONFIG_$(1):=$$(CFG_LLVM_BUILD_DIR_$(subst -,_,$(1)))/BuildTools/$$(CFG_LLVM_BUILD_SUBDIR_$(subst -,_,$(1)))/bin/llvm-config$$(X_$$(CFG_BUILD)) else -CFG_LLVM_INST_DIR_$(1):=$$(CFG_LLVM_ROOT) +# Any rules that depend on LLVM should depend on LLVM_CONFIG +LLVM_CONFIG_$(1):=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-config$$(X_$(1)) endif +else +CFG_LLVM_INST_DIR_$(1):=$$(CFG_LLVM_ROOT) # Any rules that depend on LLVM should depend on LLVM_CONFIG LLVM_CONFIG_$(1):=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-config$$(X_$(1)) +endif + + LLVM_MC_$(1):=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-mc$$(X_$(1)) LLVM_AR_$(1):=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-ar$$(X_$(1)) LLVM_VERSION_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --version) @@ -295,13 +311,16 @@ LLVM_BINDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --bindir) LLVM_INCDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --includedir) LLVM_LIBDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --libdir) LLVM_LIBDIR_RUSTFLAGS_$(1)=-L "$$(LLVM_LIBDIR_$(1))" +LLVM_LIBS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --libs $$(LLVM_COMPONENTS)) LLVM_LDFLAGS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --ldflags) ifeq ($$(findstring freebsd,$(1)),freebsd) # On FreeBSD, it may search wrong headers (that are for pre-installed LLVM), # so we replace -I with -iquote to ensure that it searches bundled LLVM first. -LLVM_CXXFLAGS_$(1)=$$(subst -I, -iquote , $$(shell "$$(LLVM_CONFIG_$(1))" --cxxflags)) +LLVM_CXXFLAGS_$(1)=$$(subst -I, -iquote , $$(shell "$$(LLVM_CONFIG_$(1))" --cxxflags) $$(CXXFLAGS)) else -LLVM_CXXFLAGS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --cxxflags) +LLVM_CXXFLAGS_$(1)=$$(filter-out $$(LLVM_FILTER_CXXFLAGS_$(1)), \ + $$(shell "$$(LLVM_CONFIG_$(1))" --cxxflags) $$(CXXFLAGS)) \ + $$(LLVM_EXTRA_CXXFLAGS_$(1)) endif LLVM_HOST_TRIPLE_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --host-target) @@ -458,9 +477,9 @@ endif endif LD_LIBRARY_PATH_ENV_HOSTDIR$(1)_T_$(2)_H_$(3) := \ - $$(CURDIR)/$$(HLIB$(1)_H_$(3)) + $$(CURDIR)/$$(HLIB$(1)_H_$(3)):$$(CFG_LLVM_INST_DIR_$(3))/lib LD_LIBRARY_PATH_ENV_TARGETDIR$(1)_T_$(2)_H_$(3) := \ - $$(CURDIR)/$$(TLIB1_T_$(2)_H_$(CFG_BUILD)) + $$(CURDIR)/$$(TLIB1_T_$(2)_H_$(CFG_BUILD)):$$(CFG_LLVM_INST_DIR_$(CFG_BUILD))/lib HOST_RPATH_VAR$(1)_T_$(2)_H_$(3) := \ $$(LD_LIBRARY_PATH_ENV_NAME$(1)_T_$(2)_H_$(3))=$$(LD_LIBRARY_PATH_ENV_HOSTDIR$(1)_T_$(2)_H_$(3)):$$$$$$(LD_LIBRARY_PATH_ENV_NAME$(1)_T_$(2)_H_$(3)) diff --git a/mk/platform.mk b/mk/platform.mk index 60fe22cb32ee6..69e4f48526934 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -139,7 +139,9 @@ define FILTER_FLAGS endif endef -$(foreach target,$(CFG_TARGET), \ +# pnacl-clang doesn't accept `-Qunused-arguments` despite using clang under the +# hood. +$(foreach target,$(filter-out le32-unknown-nacl,$(CFG_TARGET)), \ $(eval $(call FILTER_FLAGS,$(target)))) # Configure various macros to pass gcc or cl.exe style arguments diff --git a/mk/prepare.mk b/mk/prepare.mk index fe619cc7caec7..bceb5335bd2f5 100644 --- a/mk/prepare.mk +++ b/mk/prepare.mk @@ -149,8 +149,9 @@ prepare-target-$(2)-host-$(3)-$(1)-$(4): prepare-maybe-clean-$(4) \ $$(call PREPARE_DIR,$$(PREPARE_WORKING_DEST_LIB_DIR)) \ $$(call PREPARE_DIR,$$(PREPARE_DEST_BIN_DIR)) \ $$(foreach crate,$$(TARGET_CRATES), \ - $$(if $$(or $$(findstring 1, $$(ONLY_RLIB_$$(crate))),$$(findstring 1,$$(CFG_INSTALL_ONLY_RLIB_$(2)))),, \ - $$(call PREPARE_LIB,$$(call CFG_LIB_GLOB_$(2),$$(crate)))) \ + $$(if $$(SHARED_LIBS_DISABLED_$(2)),,\ + $$(if $$(or $$(findstring 1, $$(ONLY_RLIB_$$(crate))),$$(findstring 1,$$(CFG_INSTALL_ONLY_RLIB_$(2)))),, \ + $$(call PREPARE_LIB,$$(call CFG_LIB_GLOB_$(2),$$(crate))))) \ $$(call PREPARE_LIB,$$(call CFG_RLIB_GLOB,$$(crate)))) \ $$(if $$(findstring $(2),$$(CFG_HOST)), \ $$(foreach crate,$$(HOST_CRATES), \ diff --git a/mk/rt.mk b/mk/rt.mk index 69277e774e43b..db820132dadb5 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -53,10 +53,14 @@ NATIVE_DEPS_hoedown_$(1) := hoedown/src/autolink.c \ NATIVE_DEPS_miniz_$(1) = miniz.c NATIVE_DEPS_rust_builtin_$(1) := rust_builtin.c \ rust_android_dummy.c -NATIVE_DEPS_rustrt_native_$(1) := arch/$$(HOST_$(1))/record_sp.S +NATIVE_DEPS_rustrt_native_$(1) := + NATIVE_DEPS_rust_test_helpers_$(1) := rust_test_helpers.c -NATIVE_DEPS_morestack_$(1) := arch/$$(HOST_$(1))/morestack.S +ifneq ($$(RUNTIME_DISABLE_ASM_$(1)), 1) +NATIVE_DEPS_rustrt_native_$(1) += arch/$$(HOST_$(1))/record_sp.S +NATIVE_DEPS_morestack_$(1) := arch/$$(HOST_$(1))/morestack.S +endif ################################################################################ # You shouldn't find it that necessary to edit anything below this line. @@ -289,6 +293,12 @@ $$(BACKTRACE_LIB_$(1)): touch $$@ else +ifeq ($$(CFG_NACLY_$(1)),1) +# See comment above +$$(BACKTRACE_LIB_$(1)): + touch $$@ +else + ifdef CFG_ENABLE_FAST_MAKE BACKTRACE_DEPS := $(S)/.gitmodules else @@ -327,6 +337,7 @@ $$(BACKTRACE_LIB_$(1)): $$(BACKTRACE_BUILD_DIR_$(1))/Makefile $$(MKFILE_DEPS) INCDIR=$(S)src/libbacktrace $$(Q)cp $$(BACKTRACE_BUILD_DIR_$(1))/.libs/libbacktrace.a $$@ +endif # endif for nacly endif # endif for windowsy endif # endif for ios endif # endif for darwin diff --git a/mk/target.mk b/mk/target.mk index c2de9af39c764..37a158f1c6682 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -127,7 +127,7 @@ $$(TBIN$(1)_T_$(2)_H_$(3))/$(4)$$(X_$(2)): \ $$(TSREQ$(1)_T_$(2)_H_$(3)) \ | $$(TBIN$(1)_T_$(2)_H_$(3))/ @$$(call E, rustc: $$@) - $$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --cfg $(4) + $$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --cfg $(4) $$(LLVM_LIBDIR_RUSTFLAGS_$(2)) endef diff --git a/mk/tests.mk b/mk/tests.mk index 9341166beb0b6..4b4dd90d730cf 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -437,6 +437,18 @@ $$(call TEST_OK_FILE,$(1),$(2),$(3),$(4)): \ exit 101; \ fi endef +define DEF_TEST_CRATE_RULES_le32-unknown-nacl +check-stage$(1)-T-$(2)-H-$(3)-$(4)-exec: $$(call TEST_OK_FILE,$(1),$(2),$(3),$(4)) +$$(call TEST_OK_FILE,$(1),$(2),$(3),$(4)): \ + $(3)/stage$(1)/test/$(4)test-$(2)$$(X_$(2)) + @$$(call E, run: pnacl-translate $$<) + $$(Q)$$(RPATH_VAR$(1)_T_$(2)_H_$(3)) \ + $$(CFG_PNACL_TOOLCHAIN)/bin/pnacl-translate \ + --allow-llvm-bitcode-input $$< \ + -o $$<.nexe -arch x86-64 + @$$(call E, run: $$<.nexe) + $$(Q)$$(CFG_NACL_CROSS_PATH)/tools/sel_ldr.py -- $$<.nexe +endef define DEF_TEST_CRATE_RULES_null check-stage$(1)-T-$(2)-H-$(3)-$(4)-exec: $$(call TEST_OK_FILE,$(1),$(2),$(3),$(4)) @@ -458,8 +470,10 @@ $(foreach host,$(CFG_HOST), \ $(eval $(call DEF_TEST_CRATE_RULES_android,$(stage),$(target),$(host),$(crate))), \ $(eval $(call DEF_TEST_CRATE_RULES_null,$(stage),$(target),$(host),$(crate))) \ ), \ + $(if $(findstring $(target),"le32-unknown-nacl"), \ + $(eval $(call DEF_TEST_CRATE_RULES_le32-unknown-nacl,$(stage),$(target),$(host),$(crate))), \ $(eval $(call DEF_TEST_CRATE_RULES,$(stage),$(target),$(host),$(crate))) \ - )))))) + ))))))) ###################################################################### # Rules for the compiletest tests (rpass, rfail, etc.) @@ -644,6 +658,7 @@ CTEST_COMMON_ARGS$(1)-T-$(2)-H-$(3) := \ --rustc-path $$(HBIN$(1)_H_$(3))/rustc$$(X_$(3)) \ --rustdoc-path $$(HBIN$(1)_H_$(3))/rustdoc$$(X_$(3)) \ --llvm-bin-path $(CFG_LLVM_INST_DIR_$(CFG_BUILD))/bin \ + --nacl-cross-path=$(CFG_NACL_CROSS_PATH) \ --aux-base $$(S)src/test/auxiliary/ \ --stage-id stage$(1)-$(2) \ --target $(2) \ @@ -654,10 +669,10 @@ CTEST_COMMON_ARGS$(1)-T-$(2)-H-$(3) := \ --android-cross-path=$(CFG_ANDROID_CROSS_PATH) \ --adb-path=$(CFG_ADB) \ --adb-test-dir=$(CFG_ADB_TEST_DIR) \ - --host-rustcflags "$(RUSTC_FLAGS_$(3)) $$(CTEST_RUSTC_FLAGS) -L $$(RT_OUTPUT_DIR_$(3))" \ + --host-rustcflags "$(RUSTC_FLAGS_$(3)) $$(CTEST_RUSTC_FLAGS) -L $$(RT_OUTPUT_DIR_$(3)) $$(LLVM_LIBDIR_RUSTFLAGS_$(3))" \ --lldb-python-dir=$(CFG_LLDB_PYTHON_DIR) \ - --target-rustcflags "$(RUSTC_FLAGS_$(2)) $$(CTEST_RUSTC_FLAGS) -L $$(RT_OUTPUT_DIR_$(2))" \ - $$(CTEST_TESTARGS) + --target-rustcflags "$(RUSTC_FLAGS_$(2)) $$(CTEST_RUSTC_FLAGS) -L $$(RT_OUTPUT_DIR_$(2)) $$(LLVM_LIBDIR_RUSTFLAGS_$(2))" \ + $$(CTEST_TESTARGS) $$(CTEST_TARGETARGS_$(2)) ifdef CFG_VALGRIND_RPASS ifdef GOOD_VALGRIND_$(2) @@ -1056,7 +1071,8 @@ $(3)/test/run-make/%-$(1)-T-$(2)-H-$(3).ok: \ "$$(LD_LIBRARY_PATH_ENV_HOSTDIR$(1)_T_$(2)_H_$(3))" \ "$$(LD_LIBRARY_PATH_ENV_TARGETDIR$(1)_T_$(2)_H_$(3))" \ $(1) \ - $$(S) + $$(S) \ + "$$(LLVM_LIBDIR_RUSTFLAGS_$(3))" @touch -r $$@.start_time $$@ && rm $$@.start_time else # FIXME #11094 - The above rule doesn't work right for multiple targets diff --git a/mk/util.mk b/mk/util.mk index 918484ac46352..5a8528bd9bd36 100644 --- a/mk/util.mk +++ b/mk/util.mk @@ -21,3 +21,19 @@ print-%: S := $(CFG_SRC_DIR) SREL := $(CFG_SRC_DIR_RELATIVE) + +ifeq ($(CFG_OSTYPE),pc-windows-gnu) + NACL_TOOLCHAIN_OS_PATH:=win +else ifeq ($(CFG_OSTYPE),apple-darwin) + NACL_TOOLCHAIN_OS_PATH:=mac +else + NACL_TOOLCHAIN_OS_PATH:=linux +endif + +ifneq ("$(wildcard $(CFG_NACL_CROSS_PATH)/REV)","") +# The user is using a toolchain built from source, or otherwise pointed +# CFG_NACL_CROSS_PATH directly at the PNaCl toolchain root. +CFG_PNACL_TOOLCHAIN:=$(CFG_NACL_CROSS_PATH) +else +CFG_PNACL_TOOLCHAIN:=$(CFG_NACL_CROSS_PATH)/toolchain/$(NACL_TOOLCHAIN_OS_PATH)_pnacl +endif diff --git a/src/compiletest/common.rs b/src/compiletest/common.rs index eb6c29eefbe78..cafc3c5df0ae8 100644 --- a/src/compiletest/common.rs +++ b/src/compiletest/common.rs @@ -151,6 +151,15 @@ pub struct Config { // the path containing LLDB's Python module pub lldb_python_dir: Option, + // NaCl Pepper SDK path + pub nacl_cross_path: Option, + // Explain what's going on pub verbose: bool } + +impl Config { + pub fn targeting_pnacl(&self) -> bool { + "le32-unknown-nacl" == &self.target[..] + } +} diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index 36c676391019b..11811b8355e3c 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -88,6 +88,7 @@ pub fn parse_config(args: Vec ) -> Config { optopt("", "adb-path", "path to the android debugger", "PATH"), optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"), optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH"), + optopt("", "nacl-cross-path", "path to NaCl Pepper sdk", "PATH"), optflag("h", "help", "show this message")); let (argv0, args_) = args.split_first().unwrap(); @@ -149,6 +150,7 @@ pub fn parse_config(args: Vec ) -> Config { gdb_version: extract_gdb_version(matches.opt_str("gdb-version")), lldb_version: extract_lldb_version(matches.opt_str("lldb-version")), android_cross_path: opt_path(matches, "android-cross-path"), + nacl_cross_path: matches.opt_str("nacl-cross-path").map(|s| Path::new(&s).to_path_buf() ), adb_path: opt_str2(matches.opt_str("adb-path")), adb_test_dir: format!("{}/{}", opt_str2(matches.opt_str("adb-test-dir")), @@ -225,6 +227,11 @@ pub fn run_tests(config: &Config) { env::set_var("RUST_TEST_THREADS","1"); } + if config.targeting_pnacl() && config.mode == DebugInfoGdb { + // Like android, PNaCl/NaCl also uses a remote debugger. + env::set_var("RUST_TEST_TASKS","1"); + } + match config.mode { DebugInfoLldb => { // Some older versions of LLDB seem to have problems with multiple diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index a648e51497e79..e772ea653de24 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -235,6 +235,7 @@ pub fn is_test_ignored(config: &Config, testfile: &Path) -> bool { !parse_name_directive(ln, &ignore_architecture(config)) && !parse_name_directive(ln, &ignore_stage(config)) && !parse_name_directive(ln, &ignore_env(config)) && + !(config.targeting_pnacl() && parse_name_directive(ln, "ignore-pnacl")) && !(config.mode == common::Pretty && parse_name_directive(ln, "ignore-pretty")) && !(config.target != config.host && parse_name_directive(ln, "ignore-cross-compile")) && !ignore_gdb(config, ln) && diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 5b62f29b82423..66bbdbea29e62 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -26,7 +26,7 @@ use std::io::BufReader; use std::io::prelude::*; use std::net::TcpStream; use std::path::{Path, PathBuf}; -use std::process::{Command, Output, ExitStatus}; +use std::process::{Command, Output, ExitStatus, Child}; pub fn run(config: Config, testfile: &Path) { match &*config.target { @@ -482,6 +482,147 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) { } } + _ if config.targeting_pnacl() => { + use std::env::consts::ARCH; + fn make_absolute(p: &Path) -> PathBuf { + use std::env; + env::current_dir() + .map(|cwd| cwd.join(p) ) + .unwrap() + } + + let arch_prefix = match ARCH { + "x86" => "i686", + _ => ARCH, + }; + #[cfg(windows)] + fn gdb_suffix() -> &'static str { ".exe" } + #[cfg(unix)] + fn gdb_suffix() -> &'static str { "" } + + let gdb = format!("{}-nacl-gdb{}", arch_prefix, gdb_suffix()); + + let _sel_ldr_process = match pnacl_exec_compiled_test(config, + props, + testfile, + props.exec_env.clone(), + true) { + ProcResOrProcessResult::ProcResResult(_) => unreachable!(), + ProcResOrProcessResult::ProcessResult(p) => p, + }; + + cmds = cmds.replace("run", "continue").to_string(); + + let pexe_path = make_absolute(&output_base_name(config, testfile)); + let nexe_path = + // add an extension, don't replace it: + format!("{:?}.nexe", pexe_path); + + // write debugger script + let rust_src_root = find_rust_src_root(config) + .expect("Could not find Rust source root"); + let rust_pp_module_rel_path = Path::new("./src/etc"); + let rust_pp_module_abs_path = rust_src_root + .join(rust_pp_module_rel_path); + let rust_pp_module_abs_path = format!("{:?}", rust_pp_module_abs_path); + + // write debugger script + let mut script_str = String::with_capacity(2048); + + script_str.push_str("set charset UTF-8\n"); + script_str.push_str("target remote :4014\n"); + + + // Add the directory containing the pretty printers to + // GDB's script auto loading safe path + script_str.push_str( + &format!("add-auto-load-safe-path {}\n", + rust_pp_module_abs_path.replace("\\", "\\\\"))[..]); + + // The following line actually doesn't have to do anything with + // pretty printing, it just tells GDB to print values on one line: + script_str.push_str("set print pretty off\n"); + + // Add the pretty printer directory to GDB's source-file search path + script_str.push_str(&format!("directory {}\n", rust_pp_module_abs_path)[..]); + + // Load the target executable + script_str.push_str(&format!("file {}\n", + nexe_path.replace("\\", "\\\\"))[..]); + + // Add line breakpoints + for line in breakpoint_lines.iter() { + script_str.push_str(&format!("break '{:?}':{}\n", + testfile.file_name().unwrap(), + *line)[..]); + } + + script_str.push_str(&cmds[..]); + script_str.push_str("quit\n"); + + debug!("script_str = {}", script_str); + dump_output_file(config, + testfile, + &script_str[..], + "debugger.script"); + + let cross_path = config.nacl_cross_path + .clone() + .expect("need the NaCl SDK path!"); + let mut gdb_path = cross_path.clone(); + gdb_path.push("toolchain"); + gdb_path.push(&({ + let mut s = pnacl_toolchain_prefix(); + s.push_str("_x86_newlib"); + s + })); + gdb_path.push("bin"); + gdb_path.push(&gdb); + let gdb_path = gdb_path; + + loop { + // wait for a quarter second for sel_ldr to start + ::std::thread::sleep_ms(250); + if TcpStream::connect("127.0.0.1:4014").is_ok() { + break; + } + } + + let debugger_script = make_out_name(config, testfile, "debugger.script"); + // FIXME (#9639): This needs to handle non-utf8 paths + let debugger_opts = + vec!("-quiet".to_string(), + "-batch".to_string(), + "-nx".to_string(), + format!("-command={}", debugger_script.display())); + + let procsrv::Result { + out, + err, + status + } = procsrv::run("", + &format!("{}", gdb_path.display())[..], + None, + &debugger_opts[..], + vec!(("".to_string(), "".to_string())), + None) + .expect(&format!("failed to exec `{:?}`", gdb_path)[..]); + let cmdline = { + let cmdline = make_cmdline("", + &gdb[..], + &debugger_opts[..]); + logv(config, format!("executing {}", cmdline)); + cmdline + }; + + debugger_run_result = ProcRes { + status: Status::Normal(status), + stdout: out, + stderr: err, + cmdline: cmdline + }; + } + _=> { let rust_src_root = find_rust_src_root(config) .expect("Could not find Rust source root"); @@ -1167,6 +1308,14 @@ fn exec_compiled_test(config: &Config, props: &TestProps, _arm_exec_compiled_test(config, props, testfile, env) } + "le32-unknown-nacl" => { + match pnacl_exec_compiled_test(config, props, + testfile, env, false) { + ProcResOrProcessResult::ProcResResult(p) => p, + ProcResOrProcessResult::ProcessResult(_) => unreachable!(), + } + } + _=> { let aux_dir = aux_output_dir_name(config, testfile); compose_and_run(config, @@ -1197,6 +1346,8 @@ fn compose_and_run_compiler(config: &Config, props: &TestProps, let aux_props = header::load_props(&abs_ab); let mut crate_type = if aux_props.no_prefer_dynamic { Vec::new() + } else if config.targeting_pnacl() { + vec!("--crate-type=rlib".to_string()) } else { // We primarily compile all auxiliary libraries as dynamic libraries // to avoid code size bloat and large binaries as much as possible @@ -1295,7 +1446,7 @@ fn make_compile_args(config: &Config, config.build_base.to_str().unwrap().to_string(), format!("--target={}", target)); args.push_all(&extras); - if !props.no_prefer_dynamic { + if !props.no_prefer_dynamic && !config.targeting_pnacl() { args.push("-C".to_string()); args.push("prefer-dynamic".to_string()); } @@ -1452,9 +1603,12 @@ fn output_testname(testfile: &Path) -> PathBuf { } fn output_base_name(config: &Config, testfile: &Path) -> PathBuf { - config.build_base + let p = config.build_base .join(&output_testname(testfile)) - .with_extension(&config.stage_id) + .with_extension(&config.stage_id); + if config.targeting_pnacl() && config.mode == DebugInfoGdb { + p.with_extension("debug.pexe") + } else { p } } fn maybe_dump_to_stdout(config: &Config, out: &str, err: &str) { @@ -1650,6 +1804,173 @@ fn _arm_push_aux_shared_library(config: &Config, testfile: &Path) { } } +enum ProcResOrProcessResult { + ProcResResult(ProcRes), + ProcessResult(Child), +} +fn pnacl_exec_compiled_test(config: &Config, props: &TestProps, + testfile: &Path, env: Vec<(String, String)>, + run_background: bool) -> ProcResOrProcessResult { + fn make_absolute(p: &Path) -> PathBuf { + use std::env; + env::current_dir() + .map(|cwd| cwd.join(p) ) + .unwrap() + } + + let cross_path = config.nacl_cross_path + .clone() + .expect("need the NaCl SDK path!"); + + let pexe_path = make_absolute(&output_base_name(config, testfile)); + let nexe_path = + // add an extension, don't replace it: + format!("{}.nexe", pexe_path.display()); + let nexe_path = Path::new(&nexe_path); + + #[cfg(target_arch = "x86")] + fn get_nacl_arch() -> &'static str { + "x86" + } + #[cfg(target_arch = "x86_64")] + fn get_nacl_arch() -> &'static str { + "x86-64" + } + #[cfg(target_arch = "arm")] + fn get_nacl_arch() -> &'static str { + "armv7" + } + #[cfg(target_arch = "mips")] + fn get_nacl_arch() -> &'static str { + "mipsel" + } + #[cfg(not(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "mips")))] + fn get_nacl_arch() -> &'static str { + unreachable!("unknown host arch for NaCl"); + } + + let pnacl_trans_args = vec!(format!("-o{}", nexe_path.display()), + format!("{}", pexe_path.display()), + "-arch".to_string(), get_nacl_arch().to_string(), + "-O0".to_string(), // FIXME: not good for benchmarks. + "-threads=seq".to_string(), + "--allow-llvm-bitcode-input".to_string(), + ); + let pnacl_trans = { + let mut pnacl_trans = cross_path.clone(); + pnacl_trans.push("toolchain"); + pnacl_trans.push(&({ + let mut s = pnacl_toolchain_prefix(); + s.push_str("_pnacl"); + s + })); + pnacl_trans.push("bin"); + pnacl_trans.push("pnacl-translate"); + if let Some(str) = config.rustc_path.extension() { + pnacl_trans.with_extension(str) + } else { + pnacl_trans + } + }; + match program_output(config, + testfile, + &config.compile_lib_path[..], + pnacl_trans.display().to_string(), + None, + pnacl_trans_args, + env.clone(), + None) { + ProcRes { ref status, .. } if status.success() => { } + res => { + return ProcResOrProcessResult::ProcResResult(res); + } + } + + let _ = fs::remove_file(&pexe_path); + + let tools = cross_path.join("tools"); + let nacl_helper_bootstrap = tools.join("nacl_helper_bootstrap_x86_64"); + let sel_ldr_bin = tools.join("sel_ldr_x86_64"); + let irt_core = tools.join("irt_core_x86_64.nexe"); + + let mut sel_ldr_args = vec!(sel_ldr_bin.display().to_string(), + "--r_debug=0xXXXXXXXXXXXXXXXX".to_string(), + "--reserved_at_zero=0xXXXXXXXXXXXXXXXX".to_string(), + "-a".to_string(), + "-B".to_string(), + irt_core.display().to_string()); + if run_background && config.mode == DebugInfoGdb { + sel_ldr_args.push("-g".to_string()); + } + sel_ldr_args.push(nexe_path.display().to_string()); + + let ProcArgs { + args: run_args, + .. + } = make_run_args(config, props, testfile); + sel_ldr_args.extend(run_args.into_iter()); + + let sel_ldr_dsp = nacl_helper_bootstrap.display().to_string(); + + let mut process = procsrv::run_background("", + &sel_ldr_dsp[..], + None, + &sel_ldr_args[..], + env, + None) + .expect(&format!("failed to exec `{}`", sel_ldr_dsp)[..]); + + if !run_background { + let status = process.wait() + .unwrap(); + let mut stdout = String::new(); + process.stdout + .as_mut() + .unwrap() + .read_to_string(&mut stdout) + .unwrap(); + + let mut stderr = String::new(); + process.stderr + .as_mut() + .unwrap() + .read_to_string(&mut stderr) + .unwrap(); + return ProcResOrProcessResult::ProcResResult(ProcRes { + status: Status::Normal(status), + stdout: stdout, + stderr: stderr, + cmdline: make_cmdline("", + &sel_ldr_dsp[..], + &sel_ldr_args[..]), + }); + } else { + return ProcResOrProcessResult::ProcessResult(process); + } +} + +#[cfg(target_os = "linux")] +fn pnacl_toolchain_prefix() -> String { + "linux".to_string() +} +#[cfg(target_os = "macos")] +fn pnacl_toolchain_prefix() -> String { + "mac".to_string() +} +#[cfg(windows)] +fn pnacl_toolchain_prefix() -> String { + "win".to_string() +} +#[cfg(all(not(windows), + not(target_os = "linux"), + not(target_os = "macos")))] +fn pnacl_toolchain_prefix() -> String { + unimplemented!(); +} + // codegen tests (using FileCheck) fn compile_test_and_save_ir(config: &Config, props: &TestProps, diff --git a/src/compiletest/util.rs b/src/compiletest/util.rs index 13d6c029ff584..f607896da27db 100644 --- a/src/compiletest/util.rs +++ b/src/compiletest/util.rs @@ -21,6 +21,7 @@ const OS_TABLE: &'static [(&'static str, &'static str)] = &[ ("ios", "ios"), ("linux", "linux"), ("mingw32", "windows"), + ("nacl", "nacl"), ("netbsd", "netbsd"), ("openbsd", "openbsd"), ("win32", "windows"), @@ -35,6 +36,7 @@ const ARCH_TABLE: &'static [(&'static str, &'static str)] = &[ ("hexagon", "hexagon"), ("i386", "x86"), ("i686", "x86"), + ("le32", "le32"), ("mips", "mips"), ("msp430", "msp430"), ("powerpc", "powerpc"), diff --git a/src/doc/trpl/SUMMARY.md b/src/doc/trpl/SUMMARY.md index 24686e772e3c3..589e314763d69 100644 --- a/src/doc/trpl/SUMMARY.md +++ b/src/doc/trpl/SUMMARY.md @@ -20,6 +20,7 @@ * [FFI](ffi.md) * [Borrow and AsRef](borrow-and-asref.md) * [Release Channels](release-channels.md) + * [Cross Compilers](cross-compilers.md) * [Syntax and Semantics](syntax-and-semantics.md) * [Variable Bindings](variable-bindings.md) * [Functions](functions.md) diff --git a/src/doc/trpl/cross-compilers.md b/src/doc/trpl/cross-compilers.md new file mode 100644 index 0000000000000..310b24345fff0 --- /dev/null +++ b/src/doc/trpl/cross-compilers.md @@ -0,0 +1,421 @@ +% Cross Compilers + +Rust is designed to be run anywhere. Anywhere! To do this, however, you're have +to build a cross compiler for the desired target yourself: the binary distributions +don't come equipped with this ability. + +## General + +In general, to create a cross compiler one has to add `--target=` to `configure`. However a few platforms use special toolchains for +linking or have other idiosyncrasies and thus require more involved steps to +create. If you write a port, and want to incorporate the port in Rust proper, +please add a section on this page! :) + +## PNaCl/JS + +To build a PNaCl/JS cross compiler, you'll need to first abtain the +`pepper_canary` toolchain from the [Native Client SDK][nacl-sdk]. Then configure +Rust, where `NACL_SDK_ROOT` points to where `pepper_canary` is installed, with: + + $ CC="$NACL_SDK_ROOT/toolchain/$($NACL_SDK_ROOT/tools/getos.py)_pnacl/bin/clang" + CXX="$NACL_SDK_ROOT/toolchain/$($NACL_SDK_ROOT/tools/getos.py)_pnacl/bin/clang++" + /path/to/rust/configure --target=le32-unknown-nacl --nacl-cross-path=$NACL_SDK_ROOT + --llvm-root=$NACL_SDK_ROOT/toolchain/$($NACL_SDK_ROOT/tools/getos.py)_pnacl + --enable-libcpp --enable-clang + +Then build: `make -j $(nproc)`. After the build completes, install! + +To use Cargo with your cross compiler, you'll first need to set up a few env vars: + + $ export PNACL_TOOLCHAIN=$NACL_SDK_ROOT/toolchain/$($NACL_SDK_ROOT/tools/getos.py)_pnacl; + export CC_le32_unknown_nacl=$($NACL_SDK_ROOT/tools/nacl_config.py -t pnacl --tool=cc); + export CXX_le32_unknown_nacl=$($NACL_SDK_ROOT/tools/nacl_config.py -t pnacl --tool=cxx); + export AR_le32_unknown_nacl=$($NACL_SDK_ROOT/tools/nacl_config.py -t pnacl --tool=ar); + export LD_LIBRARY_PATH=$PNACL_TOOLCHAIN/lib:$LD_LIBRARY_PATH; + export PATH=$PNACL_TOOLCHAIN/bin:$PATH; + +Then you can build with Cargo like: `cargo build --target=le32-unknown-nacl` + +Once you have a `pexe` from Cargo, you can do one of three things: + +1. Produce a stable `pexe`; +2. Translate the bitcode into a Native Client module (ie for debugging); or +3. Send the bitcode to `emcc` (append `bc` to the filename first!) for + Javascript codegening. + +If you are going to use the either 1 or 2, there are some additional resources +available: +* [`pexe-runner`][pexe-runner]: Run non-PPAPI PNaCl modules like one would any other native + binary (on Linux you can also install a binfmt handler). +* [`rust-ppapi`][rust-ppapi]: Rust bindings to parts C Pepper API. + + +[nacl-sdk]: https://developer.chrome.com/native-client/sdk/download + +[pexe-runner]: https://github.com/DiamondLovesYou/pexe-runner + +[rust-ppapi]: https://github.com/DiamondLovesYou/rust-ppapi + +## Android + +Rust has been ported to Android OS running on ARM architecture. Android on other architectures is not yet supported. + +**Important**: Right now, this only works correctly on linux. It can be made to work on OSX, but if you're on OSX, make things easy by setting up a VM. It has been successfully tested on ubuntu 14.04.2 in virtualbox, if you want something super safe, but most any debian-based thing will almost certainly work. + +In the issue tracker, [Android-related issues](https://github.com/rust-lang/rust/issues?labels=A-android) are labelled `A-android`, and [ARM-related issues](https://github.com/rust-lang/rust/issues?labels=A-ARM) are labelled `A-ARM`. + +Instructions to build a cross compiler to Android on ARM, to cross compile Rust sources, and to run resulting binaries follow. + +1. Setup Android NDK standalone tool chain with your specific [API Level](http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels) + + Download android NDK version _r9b_ or later (you might not be able to build jemalloc with previous versions) from http://developer.android.com/tools/sdk/ndk/index.html. for example: + + $ wget http://dl.google.com/android/ndk/android-ndk-r10d-linux-x86_64.bin + $ chmod +x android-ndk-r10d-linux-x86_64.bin + $ ./android-ndk-r10d-linux-x86_64.bin + $ ls android-ndk-r10d -d + android-ndk-r10d + + example command to setup standalone tool chain: + + ~/android-ndk-/build/tools/make-standalone-toolchain.sh \ + --platform=android-14 \ + --arch=arm \ + --install-dir=~/ndk-standalone-14-arm + + `--platform` indicates the Android API level - has successfully been tested with 18 as well + `--install-dir` indicates the directory where the toolchain will be installed. You can pick + more or less anything you feel like. + `--arch` must be set to `arm`, or it won't know what toolchain to use. (Imagine that!) + `--toolchain` can also be set to one of the names of the directories in + `android-ndk-/toolchains/` - keeping in mind it must be one of the arm ones. + Only do this if you know why you want to - otherwise, the default of ...-4.8 will work fine. + + If you're on 64bit linux, the android ndk needs 32bit linux libraries, such as `/lib32/libc.so.6`, `/usr/lib32/libz.so.1`, and `/usr/lib32/libstdc++.so.6.0.17`, so make sure they're installed. + + If you're using ubuntu 64, this command will do: + + apt-get install libc6-i386 lib32z1 lib32stdc++6 + + Some of the tool chain will need to be placed in your path, if you plan to build with cargo. + The easy way to do this is to add these environment variables in ~/.bashrc: + + export ANDROID_NDK=".../path/to/android-ndk-" + export ANDROID_TOOLCHAIN=".../path/to/standalone/toolchain" + export PATH="$PATH:$ANDROID_TOOLCHAIN/bin" + + For example: + + export ANDROID_NDK="$HOME/android-ndk-r10d" + export ANDROID_TOOLCHAIN="$HOME/ndk-standalone-14-arm" + export PATH="$PATH:$ANDROID_TOOLCHAIN/bin" + + Then do `source ~/.bashrc` to update your environment. + +2. Download rustc from git repository + + git clone https://github.com/rust-lang/rust.git + +3. Configure with ndk toolchain path + + mkdir build + cd build + + ../configure \ + --host=x86_64-unknown-linux-gnu \ + --target=arm-linux-androideabi \ + --android-cross-path="$ANDROID_TOOLCHAIN" + +4. Build + + make + + make install + + `make install` will copy rustc binaries and libraries into /usr/local (or as defined with --prefix). You will probably need to do it as root, depending on whether you set --prefix when doing configure. + +5. How to cross compile + +#### With rustc: + + rustc --target=arm-linux-androideabi -C linker=$ANDROID_TOOLCHAIN/bin/arm-linux-androideabi-gcc -C link-args=-pie hello.rs + +`-C link-args=-pie` is a workaround for [issue #17437](https://github.com/rust-lang/rust/issues/17437). + +#### With cargo: + +Because of the need for the `-C link-args=-pie` to rustc, a horrifying hack is required +to build code for android using cargo: putting a `rustc` script earlier on PATH, that will +add the `-C link-args=-pie` argument. This is because cargo doesn't allow us to directly +set the link-args argument. This script will set up the workaround automatically: + +```sh +if [[ ! -d ~/bin ]]; then + mkdir ~/bin +fi +# check if ~/bin comes before the location of rustc +# and if it doesn't, put it there +if [[ $PATH != *"$HOME/bin:"*"$(dirname $(which rustc))" ]]; then + echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc +fi + +cat > ~/bin/rustc << +#!/bin/bash +exec "$(which rustc)" -C link-args=-pie "\$@" +END +chmod +x ~/bin/rustc +``` + +After running that script, make sure to `source ~/.bashrc` again, and then the +following instructions should work. + + 1. Create a .cargo directory in your project's root dir + 2. Create a config file inside the .cargo directory (proj/.cargo/config) + 3. Place your deps/linker information inside that file. for example: + + [target.arm-linux-androideabi] + linker = "arm-linux-androideabi-gcc" + + 4. Then, to compile: + + cargo build --target=arm-linux-androideabi + + 5. Copy `target/arm-linux-androideabi/` to your device, *not* `target/`. (This may change depending on how some cargo issues about the paths being confusing get resolved.) + +6. How to run on Android + + 1. If necessary, get your binary off your VM or set up USB to pass through. How to do this varies a lot; if you copy the binary off your vm, you'll need the android sdk on your host, and adb set up in your host's PATH. + + 2. Push your binary + + $ adb push hello /data/local/tmp + + 3. Run using adb shell + + $ adb shell + shell@hammerhead:/ $ cd /data/local/tmp + shell@hammerhead:/ $ chmod 755 hello + shell@hammerhead:/ $ ./hello + it worked!! + + ...okay, hi. hello world. whatever. + shell@hammerhead:/ $ exit + $ + +### How to add rust code into an ndk project to create an APK + +**Note: instructions beyond this point need further re-checking with latest android and rust.** + +Use rustc with --crate-type=staticlib etc to emit rust code which can be linked with C/C++ sources compiled by the android standalone toolchain's g++. From here is it possible to create an APK as with NDK examples. Use #[no_mangle] extern "C" rust functions to export functions which can be called by android frameworks + +Sample of modifications to the android-ndk "native-activity" sample makefile (jni/Android.mk) :- + + LOCAL_PATH := $(call my-dir) + include $(CLEAR_VARS) + + LOCAL_MODULE := rust-prebuilt + LOCAL_SRC_FILES := librust_android.a + + include $(PREBUILT_STATIC_LIBRARY) + include $(CLEAR_VARS) + + LOCAL_MODULE := native-activity + LOCAL_SRC_FILES := main.c + LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM + LOCAL_STATIC_LIBRARIES := android_native_app_glue rust-prebuilt + + include $(BUILD_SHARED_LIBRARY) + + $(call import-module,android/native_app_glue) + +This requires that you compiled rust code into a library beforehand, eg if 'hello_android.rs' is your crate root + + rustc --target=arm-linux-androideabi hello_android.rs --android-cross-path=/opt/ndk-standalone-arm/ --staticlib -o jni/librust_android.a + +execute ndk-build, ant debug|release etc to compile it into a native code .so and package,deploy it + +### How to integrate as a Shared Library into Android Studio using NDK and Gradle +1. Edit your Cargo.toml to build as a dylib + + [lib] + name = "my-awesome-lib" + crate_type = ["dylib"] +2. Create a `jni` and `jniLibs` directory in your Android Studio project in `proj/app/src/main` +3. Copy your `libmy-awesome-lib-SOME_HASH.so` to `proj/app/src/main/jniLibs` without the hash extension cargo automagically generates +4. Place your C++ shim source in `proj/app/src/main/jni` +5. Since we have no headers to register symbols, you will need to register all of the `#[no_mangle] pub extern` Rust functions in your C++ shim before you call them. + + e.g.) + + Rust + ----- + #[no_mangle] + pub extern an_int() -> c_int { + 123 as c_int + } + + C++ Shim + --------- + extern "C" { + + // Rust function prototypes + int an_int(); + + jint + Java_com_namespace_appname_MainActivity_callRustFunction(JNIEnv* env, jobject obj) + { + int some_int = an_int(); + return some_int; + } + + } // End extern + +6. Android Studio will automatically include all `.so` files in `jniLibs` in the APK, but if you want to use a custom shim to wrap the Rust code in order to get the JNI name mangling correct, you will need a custom `Android.mk` with your shim source in your `jni` directory: + + LOCAL_PATH := $(call my-dir) + + include $(CLEAR_VARS) + LOCAL_MODULE := my-awesome-lib + LOCAL_SRC_FILES := ../jniLibs/$(TARGET_ARCH_ABI)/libmy-awesome-lib.so + include $(PREBUILT_SHARED_LIBRARY) + + include $(CLEAR_VARS) + LOCAL_MODULE := app-name + LOCAL_SRC_FILES := shim.cpp + LOCAL_SHARED_LIBRARIES := my-awesome-lib + include $(BUILD_SHARED_LIBRARY) +7. By default, Android Studio generates a makefile in order to link against libs and headers included with the NDK and will generate one for all of your code in the `jni` directory. Unfortunately, this will not allow us to use ours, which includes our shared lib. A custom build.gradle will need created in order to manually build using `Android.mk` and copy the generated libs into the `jniLibs` directory. + +```sh +import org.apache.tools.ant.taskdefs.condition.Os + +def getPlatformNdkBuildCommand() { + def rootDir = project.rootDir + def localProperties = new File(rootDir, "local.properties") + if (localProperties.exists()) { + Properties properties = new Properties() + localProperties.withInputStream { + instr -> properties.load(instr) + } + def ndkDir = properties.getProperty('ndk.dir') + if (ndkDir == null) { + throw new GradleException("The ndk.dir property in local.propeties is not set") + } + def ndkBuild = Os.isFamily(Os.FAMILY_WINDOWS) ? "$ndkDir/ndk-build.cmd" : "$ndkDir/ndk-build" + return ndkBuild + } else { + throw new GradleException("The local.properties file does not exist") + } + } + + apply plugin: 'com.android.application' + + android { + compileSdkVersion 21 + buildToolsVersion "21.1.2" + + sourceSets.main { + jniLibs.srcDir 'src/main/jniLibs' + jni.srcDirs = [] //disable automatic ndk-build call + } + + defaultConfig { + applicationId "com.namespace.appname" + minSdkVersion 15 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + // Compile cpp code from Android.mk + task buildCppShim(type: Exec) { + def ndkBuild = getPlatformNdkBuildCommand() + commandLine "$ndkBuild", '-j8', '-C', file('src/main/jni').absolutePath + } + + // Copy shared libs into jniLibs folder (Hacky workaround) + task copySharedLibs(type: Copy) { + from 'src/main/libs' + into 'src/main/jniLibs' + } + + tasks.withType(JavaCompile) { + compileTask -> compileTask.dependsOn buildCppShim + } + + copySharedLibs.dependsOn buildCppShim + copySharedLibs.execute() + } + + dependencies { + compile fileTree(dir: 'lib', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:21.0.3' + } +``` + +8. Add the location of the NDK to your `local.properties` file + + ndk.dir=/abs/path/to/extracted/android-ndk-r10d +9. Load the libraries in your Activity + + static { + System.loadLibrary("my-awesome-lib"); + System.loadLibrary("cpp-shim"); + } + +## iOS + +You need XCode 5. + +Build Rust cross-compiler: +``` +mkdir build_ios; cd build_ios +../configure --target=armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,aarch64-apple-ios,x86_64-apple-ios +make +``` + +Once compilation complete you can use it. + +To target device (depending on dest architecture): +``` +rustc --target=armv7-apple-ios foo.rs +``` + +``` +rustc --target=armv7s-apple-ios foo.rs +``` + +``` +rustc --target=aarch64-apple-ios foo.rs +``` + +To target simulator: +``` +rustc --target=i386-apple-ios foo.rs +``` + +``` +rustc --target=x86_64-apple-ios foo.rs +``` + +## What you get + +* all Rust superpowers +* exception handling +* LLDB debugging + +## Known limitations + +* segmented stacks are disabled which means no stack protection available + +## Resources + +* [Sample](https://github.com/vhbit/ObjCrust) of using Rust from Objective C + Makefile for simultaneous compiling for both device and simulator (all architectures) diff --git a/src/driver/driver.rs b/src/driver/driver.rs index c5c58bb49ac36..ad33ee0fb2705 100644 --- a/src/driver/driver.rs +++ b/src/driver/driver.rs @@ -17,4 +17,36 @@ extern crate rustdoc as this; #[cfg(rustc)] extern crate rustc_driver as this; +#[cfg(not(target_os = "nacl"))] fn main() { this::main() } + + +// On PNaCl, `libcli_main` defines `main` and in it calls the function +// `nacl_main`, which we define here, by setting the `main_link_name` +// attribute. +#[cfg(target_os = "nacl")] #[link_name = "nacl_main"] +fn main() { this::main() } + +// All of the following libraries are used so "normal" `main` based programs can +// run as they expect on other platforms. +#[cfg(target_os = "nacl")] + +// These libraries communicate with Chrome/the IRT and call into callbacks +// provided by `cli_main`. +#[link(name = "ppapi_cpp", kind = "static")] +#[link(name = "ppapi_simple_cpp", kind = "static")] +#[link(name = "ppapi_stub", kind = "static")] + +// `cli_main` wraps the main function, setting up program arguments and the +// program environment in newlib, as one would expect on a non-PPAPI platform. +#[link(name = "cli_main", kind = "static")] + +// Required dep of `cli_main`. +#[link(name = "tar", kind = "static")] + +// Implements `fork()` and it's ilk by delegating "process" +// creation/waiting/reaping to JS (ie it inserts new embed elements to simulate +// program invocation). It sets up the instance arguments so `cli_main` can do +// its job. +#[link(name = "nacl_spawn", kind = "static")] +extern { } diff --git a/src/etc/maketest.py b/src/etc/maketest.py index f500de5e15d00..4f154886c44b8 100644 --- a/src/etc/maketest.py +++ b/src/etc/maketest.py @@ -49,6 +49,7 @@ def convert_path_spec(name, value): putenv('TARGET_RPATH_DIR', os.path.abspath(sys.argv[10])) putenv('RUST_BUILD_STAGE', sys.argv[11]) putenv('S', os.path.abspath(sys.argv[12])) +putenv('RUSTFLAGS', sys.argv[13]) putenv('PYTHON', sys.executable) if filt not in sys.argv[1]: diff --git a/src/etc/mklldeps.py b/src/etc/mklldeps.py index 1cc65406b2c00..138590d230f4b 100644 --- a/src/etc/mklldeps.py +++ b/src/etc/mklldeps.py @@ -16,7 +16,11 @@ components = sys.argv[2].split() # splits on whitespace enable_static = sys.argv[3] -llvm_config = sys.argv[4] +enable_libcpp = sys.argv[4] +llvm_config = sys.argv[5] + +if enable_libcpp == '': + enable_libcpp = '0' f.write("""// Copyright 2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at @@ -48,6 +52,8 @@ def run(args): # LLVM libs args = [llvm_config, '--libs', '--system-libs'] +llvm_shared = len(run([llvm_config, '--libs']).strip().split(' ')) == 1 + args.extend(components) out = run(args) for lib in out.strip().replace("\n", ' ').split(' '): @@ -62,8 +68,7 @@ def run(args): elif lib[0] == '-': lib = lib.strip()[1:] f.write("#[link(name = \"" + lib + "\"") - # LLVM libraries are all static libraries - if 'LLVM' in lib: + if not llvm_shared and 'LLVM' in lib: f.write(", kind = \"static\"") f.write(")]\n") @@ -75,17 +80,18 @@ def run(args): # C++ runtime library out = run([llvm_config, '--cxxflags']) + +if enable_libcpp != '0' or 'stdlib=libc++' in out: + name = 'c++' +else: + name = 'stdc++' + if enable_static == '1': - assert('stdlib=libc++' not in out) - f.write("#[link(name = \"stdc++\", kind = \"static\")]\n") + kind = ', kind = \"static\"' else: - # Note that we use `cfg_attr` here because on MSVC the C++ standard library - # is not c++ or stdc++, but rather the linker takes care of linking the - # right standard library. - if 'stdlib=libc++' in out: - f.write("#[cfg_attr(not(target_env = \"msvc\"), link(name = \"c++\"))]\n") - else: - f.write("#[cfg_attr(not(target_env = \"msvc\"), link(name = \"stdc++\"))]\n") + kind = '' + +f.write("#[cfg_attr(not(target_env = \"msvc\"), link(name = \"%s\"%s))]\n" % (name, kind)) # Attach everything to an extern block f.write("extern {}\n") diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index 14797d7f4b54d..9ad0c7ad7ba2d 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -145,7 +145,8 @@ const MIN_ALIGN: usize = 8; not(feature = "external_crate"), any(target_arch = "x86", target_arch = "x86_64", - target_arch = "aarch64")))] + target_arch = "aarch64", + target_arch = "le32")))] const MIN_ALIGN: usize = 16; #[cfg(feature = "external_funcs")] diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 80b7587ed3643..9d5a3ad6c5db2 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -44,6 +44,10 @@ they should be used through stabilized interfaces \ in the rest of the standard library")] #![allow(missing_docs)] +#![allow(unused_imports)] + +use f32; +use f64; use marker::Sized; @@ -398,11 +402,6 @@ extern "rust-intrinsic" { /// Returns the square root of an `f64` pub fn sqrtf64(x: f64) -> f64; - /// Raises an `f32` to an integer power. - pub fn powif32(a: f32, x: i32) -> f32; - /// Raises an `f64` to an integer power. - pub fn powif64(a: f64, x: i32) -> f64; - /// Returns the sine of an `f32`. pub fn sinf32(x: f32) -> f32; /// Returns the sine of an `f64`. @@ -490,28 +489,16 @@ extern "rust-intrinsic" { /// Returns the nearest integer to an `f64`. Rounds half-way cases away from zero. pub fn roundf64(x: f64) -> f64; - /// Returns the number of bits set in a `u8`. - pub fn ctpop8(x: u8) -> u8; - /// Returns the number of bits set in a `u16`. - pub fn ctpop16(x: u16) -> u16; /// Returns the number of bits set in a `u32`. pub fn ctpop32(x: u32) -> u32; /// Returns the number of bits set in a `u64`. pub fn ctpop64(x: u64) -> u64; - /// Returns the number of leading bits unset in a `u8`. - pub fn ctlz8(x: u8) -> u8; - /// Returns the number of leading bits unset in a `u16`. - pub fn ctlz16(x: u16) -> u16; /// Returns the number of leading bits unset in a `u32`. pub fn ctlz32(x: u32) -> u32; /// Returns the number of leading bits unset in a `u64`. pub fn ctlz64(x: u64) -> u64; - /// Returns the number of trailing bits unset in a `u8`. - pub fn cttz8(x: u8) -> u8; - /// Returns the number of trailing bits unset in a `u16`. - pub fn cttz16(x: u16) -> u16; /// Returns the number of trailing bits unset in a `u32`. pub fn cttz32(x: u32) -> u32; /// Returns the number of trailing bits unset in a `u64`. @@ -523,6 +510,35 @@ extern "rust-intrinsic" { pub fn bswap32(x: u32) -> u32; /// Reverses the bytes in a `u64`. pub fn bswap64(x: u64) -> u64; +} +/// The following intrinsics are disallowed on PNaCl: +/// We sneakily redirect them to libm or to a larger sized intrinsic. +#[cfg(not(target_os = "nacl"))] +extern "rust-intrinsic" { + + /// Raises an `f32` to an `f32` power. + pub fn powif32(a: f32, x: i32) -> f32; + /// Raises an `f64` to an `f64` power. + pub fn powif64(a: f64, x: i32) -> f64; + + /// Returns the number of bits set in a `u8`. + pub fn ctpop8(x: u8) -> u8; + /// Returns the number of bits set in a `u16`. + pub fn ctpop16(x: u16) -> u16; + + /// Returns the number of leading bits unset in a `u8`. + pub fn ctlz8(x: u8) -> u8; + /// Returns the number of leading bits unset in a `u16`. + pub fn ctlz16(x: u16) -> u16; + + /// Returns the number of trailing bits unset in a `u8`. + pub fn cttz8(x: u8) -> u8; + /// Returns the number of trailing bits unset in a `u16`. + pub fn cttz16(x: u16) -> u16; + +} + +extern "rust-intrinsic" { /// Performs checked `i8` addition. pub fn i8_add_with_overflow(x: i8, y: i8) -> (i8, bool); @@ -608,3 +624,23 @@ extern "rust-intrinsic" { /// is thrown (aka the thread panics). pub fn try(f: fn(*mut u8), data: *mut u8) -> *mut u8; } + +#[cfg(target_os = "nacl")] +#[inline] pub unsafe fn powif32(a: f32, x: i32) -> f32 { powf64(a as f64, x as f64) as f32 } +#[cfg(target_os = "nacl")] +#[inline] pub unsafe fn powif64(a: f64, x: i32) -> f64 { powf64(a, x as f64) } + +#[cfg(target_os = "nacl")] +#[inline] pub unsafe fn ctpop8(x: u8) -> u8 { ctpop32(x as u32) as u8 } +#[cfg(target_os = "nacl")] +#[inline] pub unsafe fn ctpop16(x: u16) -> u16 { ctpop32(x as u32) as u16 } + +#[cfg(target_os = "nacl")] +#[inline] pub unsafe fn ctlz8(x: u8) -> u8 { ctlz32((x as u32) << 24) as u8 } +#[cfg(target_os = "nacl")] +#[inline] pub unsafe fn ctlz16(x: u16) -> u16 { ctlz32((x as u32) << 16) as u16 } + +#[cfg(target_os = "nacl")] +#[inline] pub unsafe fn cttz8(x: u8) -> u8 { cttz32(x as u32) as u8 } +#[cfg(target_os = "nacl")] +#[inline] pub unsafe fn cttz16(x: u16) -> u16 { cttz32(x as u32) as u16 } diff --git a/src/libcore/simd.rs b/src/libcore/simd.rs index d0205fc9b126e..623f22e9279ba 100644 --- a/src/libcore/simd.rs +++ b/src/libcore/simd.rs @@ -40,6 +40,119 @@ #![allow(non_camel_case_types)] #![allow(missing_docs)] +#[cfg(all(target_os = "nacl", target_arch = "le32"))] +use {cmp, ops}; + +// The PNaCl ABI doesn't currently allow the , , +// simd types (i64x2, u64x2, and f64x2, respectively). So, these are a poor +// man's fix so the run-pass simd tests can pass. +macro_rules! pnacl_abi_workaround_arithmetic ( + ($ty:ident) => { + #[cfg(all(target_os = "nacl", target_arch = "le32"))] + impl cmp::PartialEq for $ty { + fn eq(&self, rhs: &$ty) -> bool { + self.0 == rhs.0 && self.1 == rhs.1 + } + } + #[cfg(all(target_os = "nacl", target_arch = "le32"))] + impl ops::Add for $ty { + type Output = $ty; + fn add(self, rhs: $ty) -> $ty { + $ty(self.0 + rhs.0, self.1 + rhs.1) + } + } + #[cfg(all(target_os = "nacl", target_arch = "le32"))] + impl ops::Sub for $ty { + type Output = $ty; + fn sub(self, rhs: $ty) -> $ty { + $ty(self.0 - rhs.0, self.1 - rhs.1) + } + } + #[cfg(all(target_os = "nacl", target_arch = "le32"))] + impl ops::Mul for $ty { + type Output = $ty; + fn mul(self, rhs: $ty) -> $ty { + $ty(self.0 * rhs.0, self.1 * rhs.1) + } + } + #[cfg(all(target_os = "nacl", target_arch = "le32"))] + impl ops::Div for $ty { + type Output = $ty; + fn div(self, rhs: $ty) -> $ty { + $ty(self.0 / rhs.0, self.1 / rhs.1) + } + } + #[cfg(all(target_os = "nacl", target_arch = "le32"))] + impl ops::Rem for $ty { + type Output = $ty; + fn rem(self, rhs: $ty) -> $ty { + $ty(self.0 % rhs.0, self.1 % rhs.1) + } + } + } +); +macro_rules! pnacl_abi_workaround_bit ( + ($ty:ident) => { + #[cfg(all(target_os = "nacl", target_arch = "le32"))] + impl ops::BitAnd for $ty { + type Output = $ty; + fn bitand(self, rhs: $ty) -> $ty { + $ty(self.0 & rhs.0, self.1 & rhs.1) + } + } + #[cfg(all(target_os = "nacl", target_arch = "le32"))] + impl ops::BitOr for $ty { + type Output = $ty; + fn bitor(self, rhs: $ty) -> $ty { + $ty(self.0 | rhs.0, self.1 | rhs.1) + } + } + #[cfg(all(target_os = "nacl", target_arch = "le32"))] + impl ops::BitXor for $ty { + type Output = $ty; + fn bitxor(self, rhs: $ty) -> $ty { + $ty(self.0 ^ rhs.0, self.1 ^ rhs.1) + } + } + #[cfg(all(target_os = "nacl", target_arch = "le32"))] + impl ops::Shl<$ty> for $ty { + type Output = $ty; + fn shl(self, rhs: $ty) -> $ty { + $ty(self.0 << rhs.0, self.1 << rhs.1) + } + } + #[cfg(all(target_os = "nacl", target_arch = "le32"))] + impl ops::Shr<$ty> for $ty { + type Output = $ty; + fn shr(self, rhs: $ty) -> $ty { + $ty(self.0 >> rhs.0, self.1 >> rhs.1) + } + } + #[cfg(all(target_os = "nacl", target_arch = "le32"))] + impl ops::Not for $ty { + type Output = $ty; + fn not(self) -> $ty { + $ty(!self.0, !self.1) + } + } + + + #[cfg(all(target_os = "nacl", target_arch = "le32"))] + impl cmp::Eq for $ty { } + } +); +macro_rules! pnacl_abi_workaround_signed ( + ($ty:ident) => { + #[cfg(all(target_os = "nacl", target_arch = "le32"))] + impl ops::Neg for $ty { + type Output = $ty; + fn neg(self) -> $ty { + $ty(-self.0, -self.1) + } + } + } +); + #[simd] #[derive(Copy, Clone, Debug)] #[repr(C)] @@ -59,10 +172,13 @@ pub struct i16x8(pub i16, pub i16, pub i16, pub i16, #[repr(C)] pub struct i32x4(pub i32, pub i32, pub i32, pub i32); -#[simd] +#[cfg_attr(not(all(target_os = "nacl", target_arch = "le32")), simd)] #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct i64x2(pub i64, pub i64); +pnacl_abi_workaround_arithmetic!(i64x2); +pnacl_abi_workaround_bit!(i64x2); +pnacl_abi_workaround_signed!(i64x2); #[simd] #[derive(Copy, Clone, Debug)] @@ -83,17 +199,21 @@ pub struct u16x8(pub u16, pub u16, pub u16, pub u16, #[repr(C)] pub struct u32x4(pub u32, pub u32, pub u32, pub u32); -#[simd] +#[cfg_attr(not(all(target_os = "nacl", target_arch = "le32")), simd)] #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct u64x2(pub u64, pub u64); +pnacl_abi_workaround_arithmetic!(u64x2); +pnacl_abi_workaround_bit!(u64x2); #[simd] #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct f32x4(pub f32, pub f32, pub f32, pub f32); -#[simd] +#[cfg_attr(not(all(target_os = "nacl", target_arch = "le32")), simd)] #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct f64x2(pub f64, pub f64); +pnacl_abi_workaround_arithmetic!(f64x2); +pnacl_abi_workaround_signed!(f64x2); diff --git a/src/liblibc/lib.rs b/src/liblibc/lib.rs index d895a3e62a32c..cdf6e2fbf7ffb 100644 --- a/src/liblibc/lib.rs +++ b/src/liblibc/lib.rs @@ -170,9 +170,6 @@ extern {} // pnaclmm provides a number of functions that the toolchain's Clang emits calls // to when codegening atomic ops. All the functions within wrap various atomic // operations. -// Yes, it could be linked by rustc explicitly, however by linking it here -// instead we save a bit of time where bins are involved (by not running the -// optimizations on the whole pnaclmm foreach binary built). #[cfg(all(target_os = "nacl", not(feature = "cargo-build"), not(test)))] #[link(name = "pnaclmm", kind = "static")] extern {} @@ -487,7 +484,7 @@ pub mod types { pub type blksize_t = i32; pub type blkcnt_t = i32; - #[repr(C)] + #[repr(C)] #[cfg(not(target_os = "nacl"))] #[derive(Copy, Clone)] pub struct stat { pub st_dev: dev_t, pub __pad1: c_short, @@ -510,6 +507,25 @@ pub mod types { pub __unused4: c_long, pub __unused5: c_long, } + #[repr(C)] #[cfg(target_os = "nacl")] + #[derive(Copy, Clone)] pub struct stat { + pub st_dev: dev_t, + pub st_ino: ino_t, + pub st_mode: mode_t, + pub st_nlink: nlink_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: dev_t, + pub st_size: off_t, + pub st_blksize: blksize_t, + pub st_blocks: blkcnt_t, + pub st_atime: time_t, + pub st_atime_nsec: c_long, + pub st_mtime: time_t, + pub st_mtime_nsec: c_long, + pub st_ctime: time_t, + pub st_ctime_nsec: c_long, + } #[repr(C)] #[derive(Copy, Clone)] pub struct utimbuf { @@ -5915,6 +5931,7 @@ pub mod funcs { -> ssize_t; pub fn rmdir(path: *const c_char) -> c_int; pub fn setgid(gid: gid_t) -> c_int; + pub fn setsid() -> pid_t; pub fn setuid(uid: uid_t) -> c_int; pub fn sleep(secs: c_uint) -> c_uint; pub fn usleep(secs: c_uint) -> c_int; diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 1a15d98d53150..d2b735b88883f 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -115,6 +115,7 @@ pub mod middle { pub mod check_loop; pub mod check_match; pub mod check_rvalues; + pub mod check_no_asm; pub mod const_eval; pub mod dataflow; pub mod dead; diff --git a/src/librustc/middle/check_no_asm.rs b/src/librustc/middle/check_no_asm.rs new file mode 100644 index 0000000000000..139702d7ba3b5 --- /dev/null +++ b/src/librustc/middle/check_no_asm.rs @@ -0,0 +1,40 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// Run over the whole crate and check for ExprInlineAsm. +/// Inline asm isn't allowed on PNaCl, so we reject it here. + +use session::Session; + +use syntax::ast; +use syntax::visit::Visitor; +use syntax::visit; + +pub fn check_crate(sess: &Session, krate: &ast::Crate) { + if !sess.target.target.options.no_asm { return; } + + visit::walk_crate(&mut CheckNoAsm { sess: sess, }, krate); +} + +#[derive(Copy, Clone)] +struct CheckNoAsm<'a> { + sess: &'a Session, +} + +impl<'a, 'v> Visitor<'v> for CheckNoAsm<'a> { + fn visit_expr(&mut self, e: &ast::Expr) { + match e.node { + ast::ExprInlineAsm(_) => self.sess.span_err(e.span, + "asm! is unsupported on this target"), + _ => {}, + } + visit::walk_expr(self, e) + } +} diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index bd8a666ffecb2..56a9fc4aa7c60 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -375,7 +375,7 @@ fn create_and_seed_worklist(tcx: &ty::ctxt, // Seed entry point match *tcx.sess.entry_fn.borrow() { - Some((id, _)) => worklist.push(id), + Some((id, _, _)) => worklist.push(id), None => () } diff --git a/src/librustc/middle/entry.rs b/src/librustc/middle/entry.rs index c6e5b654f9a5c..f1fe5bdd52c43 100644 --- a/src/librustc/middle/entry.rs +++ b/src/librustc/middle/entry.rs @@ -27,13 +27,13 @@ struct EntryContext<'a, 'ast: 'a> { main_name: Name, // The top-level function called 'main' - main_fn: Option<(NodeId, Span)>, + main_fn: Option<(NodeId, token::InternedString, Span)>, // The function that has attribute named 'main' - attr_main_fn: Option<(NodeId, Span)>, + attr_main_fn: Option<(NodeId, token::InternedString, Span)>, // The function that has the attribute 'start' on it - start_fn: Option<(NodeId, Span)>, + start_fn: Option<(NodeId, token::InternedString, Span)>, // The functions that one might think are 'main' but aren't, e.g. // main functions not defined at the top level. For diagnostics. @@ -84,7 +84,13 @@ fn find_item(item: &Item, ctxt: &mut EntryContext) { if path.count() == 1 { // This is a top-level function so can be 'main' if ctxt.main_fn.is_none() { - ctxt.main_fn = Some((item.id, item.span)); + let link_name = + attr::first_attr_value_str_by_name(&item.attrs, + "link_name") + .unwrap_or_else(|| { + token::InternedString::new_from_name(ctxt.main_name) + }); + ctxt.main_fn = Some((item.id, link_name, item.span)); } else { span_err!(ctxt.session, item.span, E0136, "multiple 'main' functions"); @@ -98,7 +104,12 @@ fn find_item(item: &Item, ctxt: &mut EntryContext) { if attr::contains_name(&item.attrs, "main") { if ctxt.attr_main_fn.is_none() { - ctxt.attr_main_fn = Some((item.id, item.span)); + let link_name = attr::first_attr_value_str_by_name(&item.attrs, + "link_name") + .unwrap_or_else(|| { + token::InternedString::new_from_name(ctxt.main_name) + }); + ctxt.attr_main_fn = Some((item.id, link_name, item.span)); } else { span_err!(ctxt.session, item.span, E0137, "multiple functions with a #[main] attribute"); @@ -107,7 +118,12 @@ fn find_item(item: &Item, ctxt: &mut EntryContext) { if attr::contains_name(&item.attrs, "start") { if ctxt.start_fn.is_none() { - ctxt.start_fn = Some((item.id, item.span)); + let link_name = attr::first_attr_value_str_by_name(&item.attrs, + "link_name") + .unwrap_or_else(|| { + token::InternedString::new_from_name(ctxt.main_name) + }); + ctxt.start_fn = Some((item.id, link_name, item.span)); } else { span_err!(ctxt.session, item.span, E0138, "multiple 'start' functions"); @@ -122,13 +138,13 @@ fn find_item(item: &Item, ctxt: &mut EntryContext) { fn configure_main(this: &mut EntryContext) { if this.start_fn.is_some() { - *this.session.entry_fn.borrow_mut() = this.start_fn; + *this.session.entry_fn.borrow_mut() = this.start_fn.clone(); this.session.entry_type.set(Some(config::EntryStart)); } else if this.attr_main_fn.is_some() { - *this.session.entry_fn.borrow_mut() = this.attr_main_fn; + *this.session.entry_fn.borrow_mut() = this.attr_main_fn.clone(); this.session.entry_type.set(Some(config::EntryMain)); } else if this.main_fn.is_some() { - *this.session.entry_fn.borrow_mut() = this.main_fn; + *this.session.entry_fn.borrow_mut() = this.main_fn.clone(); this.session.entry_type.set(Some(config::EntryMain)); } else { // No main function diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index c5db7cd718b8e..fe183d496589a 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -506,6 +506,8 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, "metadata to mangle symbol names with"), extra_filename: String = ("".to_string(), parse_string, "extra data to put in each output filename"), + cross_path: Option = (None, parse_opt_string, + "the path to the target specific toolchain"), codegen_units: usize = (1, parse_uint, "divide crate into N units to optimize in parallel"), remark: Passes = (SomePasses(Vec::new()), parse_passes, diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 99a58f07ae62b..6b6348e7c8aa2 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -43,7 +43,7 @@ pub struct Session { pub cstore: CStore, pub parse_sess: ParseSess, // For a library crate, this is always none - pub entry_fn: RefCell>, + pub entry_fn: RefCell>, pub entry_type: Cell>, pub plugin_registrar_fn: Cell>, pub default_sysroot: Option, @@ -261,7 +261,7 @@ impl Session { self.opts.debugging_opts.print_llvm_passes } pub fn lto(&self) -> bool { - self.opts.cg.lto + self.opts.cg.lto && self.target.target.options.lto_supported } pub fn no_landing_pads(&self) -> bool { self.opts.debugging_opts.no_landing_pads diff --git a/src/librustc_back/target/le32_unknown_nacl.rs b/src/librustc_back/target/le32_unknown_nacl.rs new file mode 100644 index 0000000000000..608728f62ee7f --- /dev/null +++ b/src/librustc_back/target/le32_unknown_nacl.rs @@ -0,0 +1,47 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::{Target, TargetOptions}; + +pub fn target() -> Target { + let opts = TargetOptions { + linker: "pnacl-clang".to_string(), + ar: "pnacl-ar".to_string(), + + data_layout: "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-\ + i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32".to_string(), + + pre_link_args: vec!("--pnacl-exceptions=sjlj".to_string(), + "-Wl,--start-group".to_string()), + post_link_args: vec!("-Wl,--end-group".to_string()), + dynamic_linking: false, + executables: true, + morestack: false, + exe_suffix: ".pexe".to_string(), + no_compiler_rt: true, + linker_is_gnu: true, + is_like_pnacl: true, + no_asm: true, + lto_supported: false, // `pnacl-ld` runs "LTO". + archive_format: "gnu".to_string(), + .. Default::default() + }; + Target { + // Pretend that we are ARM for name mangling and assembly conventions. + // https://code.google.com/p/nativeclient/issues/detail?id=2554 + llvm_target: "armv7a-none-nacl-gnueabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_os: "nacl".to_string(), + target_env: "newlib".to_string(), + arch: "le32".to_string(), + options: opts, + } +} diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index ce05a8878ff4b..4a03f0a578ec2 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -153,6 +153,8 @@ pub struct TargetOptions { /// Whether the target toolchain is like Android's. Only useful for compiling against Android. /// Defaults to false. pub is_like_android: bool, + /// Whether the target is like PNaCl/JS. Defaults to false. + pub is_like_pnacl: bool, /// Whether the linker support GNU-like arguments such as -O. Defaults to false. pub linker_is_gnu: bool, /// Whether the linker support rpaths or not. Defaults to false. @@ -171,6 +173,10 @@ pub struct TargetOptions { /// currently only "gnu" is used to fall into LLVM. Unknown strings cause /// the system linker to be used. pub archive_format: String, + /// Is asm!() not allowed? Defaults to false + pub no_asm: bool, + /// Is LTO allowed? Defaults to true. + pub lto_supported: bool, /// Whether the target uses a custom unwind resumption routine. /// By default LLVM lowers `resume` instructions into calls to `_Unwind_Resume` /// defined in libgcc. If this option is enabled, the target must provide @@ -207,6 +213,7 @@ impl Default for TargetOptions { is_like_windows: false, is_like_android: false, is_like_msvc: false, + is_like_pnacl: false, linker_is_gnu: false, has_rpath: false, no_compiler_rt: false, @@ -215,6 +222,8 @@ impl Default for TargetOptions { post_link_objects: Vec::new(), archive_format: String::new(), custom_unwind_resume: false, + no_asm: false, + lto_supported: true, } } } @@ -304,11 +313,13 @@ impl Target { key!(function_sections, bool); key!(is_like_osx, bool); key!(is_like_windows, bool); + key!(is_like_pnacl, bool); key!(linker_is_gnu, bool); key!(has_rpath, bool); key!(no_compiler_rt, bool); key!(pre_link_args, list); key!(post_link_args, list); + key!(no_asm, bool); base } @@ -398,7 +409,9 @@ impl Target { i686_pc_windows_gnu, x86_64_pc_windows_msvc, - i686_pc_windows_msvc + i686_pc_windows_msvc, + + le32_unknown_nacl ); diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 80db6426917e0..85bf221ff405c 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -159,14 +159,13 @@ pub fn compile_input(sess: Session, }) }; - let (outputs, trans) = if let Ok(out) = result { + let (outputs, mut trans) = if let Ok(out) = result { out } else { return; }; - phase_5_run_llvm_passes(&sess, &trans, &outputs); - + phase_5_run_llvm_passes(&sess, &mut trans, &outputs); controller_entry_point!(after_llvm, sess, CompileState::state_after_llvm(input, @@ -552,6 +551,9 @@ pub fn phase_2_configure_and_expand(sess: &Session, time(time_passes, "checking that all macro invocations are gone", &krate, |krate| syntax::ext::expand::check_for_macros(&sess.parse_sess, krate)); + time(time_passes, "checking for inline asm in case the target doesn't support it", &krate, + |krate| middle::check_no_asm::check_crate(sess, krate) ); + // One final feature gating of the true AST that gets compiled // later, to make sure we've got everything (e.g. configuration // can insert new attributes via `cfg_attr`) @@ -750,9 +752,9 @@ pub fn phase_4_translate_to_llvm(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) /// Run LLVM itself, producing a bitcode file, assembly file or object file /// as a side effect. pub fn phase_5_run_llvm_passes(sess: &Session, - trans: &trans::CrateTranslation, + trans: &mut trans::CrateTranslation, outputs: &OutputFilenames) { - if sess.opts.cg.no_integrated_as { + if sess.opts.cg.no_integrated_as && !sess.target.target.options.is_like_pnacl { let output_type = config::OutputTypeAssembly; time(sess.time_passes(), "LLVM passes", (), |_| @@ -780,11 +782,12 @@ pub fn phase_5_run_llvm_passes(sess: &Session, pub fn phase_6_link_output(sess: &Session, trans: &trans::CrateTranslation, outputs: &OutputFilenames) { - time(sess.time_passes(), "linking", (), |_| - link::link_binary(sess, - trans, - outputs, - &trans.link.crate_name)); + time(sess.time_passes(), "linking", (), |_| { + link::link_binary(sess, + trans, + outputs, + &trans.link.crate_name); + }); } fn escape_dep_filename(filename: &str) -> String { @@ -804,8 +807,12 @@ fn write_out_deps(sess: &Session, match *output_type { config::OutputTypeExe => { for output in sess.crate_types.borrow().iter() { - let p = link::filename_for_input(sess, *output, id, - outputs); + if !sess.target.target.options.dynamic_linking && + *output == config::CrateTypeDylib { + continue; + } + let p = link::filename_for_input(sess, *output, + id, outputs); out_filenames.push(p); } } diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 9ee046915daca..8dda313b13f8d 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -57,6 +57,13 @@ pub use self::DiagnosticSeverity::*; pub use self::Linkage::*; pub use self::DLLStorageClassTypes::*; +// target machine initialization functions: +pub use self::init_x86::*; +pub use self::init_arm::*; +pub use self::init_aarch64::*; +pub use self::init_mips::*; +pub use self::init_powerpc::*; + use std::ffi::CString; use std::cell::RefCell; use std::{slice, mem}; @@ -2007,32 +2014,6 @@ extern { pub fn LLVMIsAAllocaInst(value_ref: ValueRef) -> ValueRef; pub fn LLVMIsAConstantInt(value_ref: ValueRef) -> ValueRef; - pub fn LLVMInitializeX86TargetInfo(); - pub fn LLVMInitializeX86Target(); - pub fn LLVMInitializeX86TargetMC(); - pub fn LLVMInitializeX86AsmPrinter(); - pub fn LLVMInitializeX86AsmParser(); - pub fn LLVMInitializeARMTargetInfo(); - pub fn LLVMInitializeARMTarget(); - pub fn LLVMInitializeARMTargetMC(); - pub fn LLVMInitializeARMAsmPrinter(); - pub fn LLVMInitializeARMAsmParser(); - pub fn LLVMInitializeAArch64TargetInfo(); - pub fn LLVMInitializeAArch64Target(); - pub fn LLVMInitializeAArch64TargetMC(); - pub fn LLVMInitializeAArch64AsmPrinter(); - pub fn LLVMInitializeAArch64AsmParser(); - pub fn LLVMInitializeMipsTargetInfo(); - pub fn LLVMInitializeMipsTarget(); - pub fn LLVMInitializeMipsTargetMC(); - pub fn LLVMInitializeMipsAsmPrinter(); - pub fn LLVMInitializeMipsAsmParser(); - pub fn LLVMInitializePowerPCTargetInfo(); - pub fn LLVMInitializePowerPCTarget(); - pub fn LLVMInitializePowerPCTargetMC(); - pub fn LLVMInitializePowerPCAsmPrinter(); - pub fn LLVMInitializePowerPCAsmParser(); - pub fn LLVMRustAddPass(PM: PassManagerRef, Pass: *const c_char) -> bool; pub fn LLVMRustCreateTargetMachine(Triple: *const c_char, CPU: *const c_char, @@ -2076,6 +2057,8 @@ extern { pub fn LLVMRustLinkInExternalBitcode(M: ModuleRef, bc: *const c_char, len: size_t) -> bool; + pub fn LLVMRustLinkInModule(Dest: ModuleRef, + Src: ModuleRef) -> bool; pub fn LLVMRustRunRestrictionPass(M: ModuleRef, syms: *const *const c_char, len: size_t); @@ -2126,6 +2109,7 @@ extern { pub fn LLVMWriteSMDiagnosticToString(d: SMDiagnosticRef, s: RustStringRef); + pub fn LLVMRustUseArchiveWriter() -> bool; pub fn LLVMRustWriteArchive(Dst: *const c_char, NumMembers: size_t, Members: *const RustArchiveMemberRef, @@ -2325,6 +2309,101 @@ pub unsafe fn debug_loc_to_string(c: ContextRef, tr: DebugLocRef) -> String { .expect("got a non-UTF8 DebugLoc from LLVM") } +#[cfg(have_component_x86)] +mod init_x86 { + extern { + pub fn LLVMInitializeX86TargetInfo(); + pub fn LLVMInitializeX86Target(); + pub fn LLVMInitializeX86TargetMC(); + pub fn LLVMInitializeX86AsmPrinter(); + pub fn LLVMInitializeX86AsmParser(); + } +} +#[cfg(not(have_component_x86))] +mod init_x86 { + pub unsafe fn LLVMInitializeX86TargetInfo() { } + pub unsafe fn LLVMInitializeX86Target() { } + pub unsafe fn LLVMInitializeX86TargetMC() { } + pub unsafe fn LLVMInitializeX86AsmPrinter() { } + pub unsafe fn LLVMInitializeX86AsmParser() { } +} + +#[cfg(have_component_arm)] +mod init_arm { + extern { + pub fn LLVMInitializeARMTargetInfo(); + pub fn LLVMInitializeARMTarget(); + pub fn LLVMInitializeARMTargetMC(); + pub fn LLVMInitializeARMAsmPrinter(); + pub fn LLVMInitializeARMAsmParser(); + } +} +#[cfg(not(have_component_arm))] +mod init_arm { + pub unsafe fn LLVMInitializeARMTargetInfo() { } + pub unsafe fn LLVMInitializeARMTarget() { } + pub unsafe fn LLVMInitializeARMTargetMC() { } + pub unsafe fn LLVMInitializeARMAsmPrinter() { } + pub unsafe fn LLVMInitializeARMAsmParser() { } +} + +#[cfg(have_component_aarch64)] +mod init_aarch64 { + extern { + pub fn LLVMInitializeAArch64TargetInfo(); + pub fn LLVMInitializeAArch64Target(); + pub fn LLVMInitializeAArch64TargetMC(); + pub fn LLVMInitializeAArch64AsmPrinter(); + pub fn LLVMInitializeAArch64AsmParser(); + } +} +#[cfg(not(have_component_aarch64))] +mod init_aarch64 { + pub unsafe fn LLVMInitializeAArch64TargetInfo() { } + pub unsafe fn LLVMInitializeAArch64Target() { } + pub unsafe fn LLVMInitializeAArch64TargetMC() { } + pub unsafe fn LLVMInitializeAArch64AsmPrinter() { } + pub unsafe fn LLVMInitializeAArch64AsmParser() { } +} + +#[cfg(have_component_mips)] +mod init_mips { + extern { + pub fn LLVMInitializeMipsTargetInfo(); + pub fn LLVMInitializeMipsTarget(); + pub fn LLVMInitializeMipsTargetMC(); + pub fn LLVMInitializeMipsAsmPrinter(); + pub fn LLVMInitializeMipsAsmParser(); + } +} +#[cfg(not(have_component_mips))] +mod init_mips { + pub unsafe fn LLVMInitializeMipsTargetInfo() { } + pub unsafe fn LLVMInitializeMipsTarget() { } + pub unsafe fn LLVMInitializeMipsTargetMC() { } + pub unsafe fn LLVMInitializeMipsAsmPrinter() { } + pub unsafe fn LLVMInitializeMipsAsmParser() { } +} + +#[cfg(have_component_powerpc)] +mod init_powerpc { + extern { + pub fn LLVMInitializePowerPCTargetInfo(); + pub fn LLVMInitializePowerPCTarget(); + pub fn LLVMInitializePowerPCTargetMC(); + pub fn LLVMInitializePowerPCAsmPrinter(); + pub fn LLVMInitializePowerPCAsmParser(); + } +} +#[cfg(not(have_component_powerpc))] +mod init_powerpc { + pub unsafe fn LLVMInitializePowerPCTargetInfo() { } + pub unsafe fn LLVMInitializePowerPCTarget() { } + pub unsafe fn LLVMInitializePowerPCTargetMC() { } + pub unsafe fn LLVMInitializePowerPCAsmPrinter() { } + pub unsafe fn LLVMInitializePowerPCAsmParser() { } +} + // The module containing the native LLVM dependencies, generated by the build system // Note that this must come after the rustllvm extern declaration so that // parts of LLVM that rustllvm depends on aren't thrown away by the linker. diff --git a/src/librustc_trans/back/archive.rs b/src/librustc_trans/back/archive.rs index 02f4bc83b7524..0e0a0a354fd51 100644 --- a/src/librustc_trans/back/archive.rs +++ b/src/librustc_trans/back/archive.rs @@ -218,7 +218,7 @@ impl<'a> ArchiveBuilder<'a> { } pub fn llvm_archive_kind(&self) -> Option { - if unsafe { llvm::LLVMVersionMinor() < 7 } { + if !unsafe { llvm::LLVMRustUseArchiveWriter() } { return None } diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 5bdc76bd7c298..bbf2998af0f6d 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -499,6 +499,7 @@ fn link_binary_output(sess: &Session, outputs: &OutputFilenames, crate_name: &str) -> PathBuf { let objects = object_filenames(sess, outputs); + let out_filename = match outputs.single_output_file { Some(ref file) => file.clone(), None => filename_for_input(sess, crate_type, crate_name, outputs), @@ -642,62 +643,64 @@ fn link_rlib<'a>(sess: &'a Session, // For LTO purposes, the bytecode of this library is also inserted // into the archive. If codegen_units > 1, we insert each of the // bitcode files. - for obj in objects { - // Note that we make sure that the bytecode filename in the - // archive is never exactly 16 bytes long by adding a 16 byte - // extension to it. This is to work around a bug in LLDB that - // would cause it to crash if the name of a file in an archive - // was exactly 16 bytes. - let bc_filename = obj.with_extension("bc"); - let bc_deflated_filename = tmpdir.join({ - obj.with_extension("bytecode.deflate").file_name().unwrap() - }); - - let mut bc_data = Vec::new(); - match fs::File::open(&bc_filename).and_then(|mut f| { - f.read_to_end(&mut bc_data) - }) { - Ok(..) => {} - Err(e) => sess.fatal(&format!("failed to read bytecode: {}", - e)) - } - - let bc_data_deflated = flate::deflate_bytes(&bc_data[..]); - - let mut bc_file_deflated = match fs::File::create(&bc_deflated_filename) { - Ok(file) => file, - Err(e) => { - sess.fatal(&format!("failed to create compressed \ - bytecode file: {}", e)) - } - }; - - match write_rlib_bytecode_object_v1(&mut bc_file_deflated, - &bc_data_deflated) { - Ok(()) => {} - Err(e) => { - sess.fatal(&format!("failed to write compressed \ - bytecode: {}", e)); + if sess.target.target.options.lto_supported { + for obj in objects { + // Note that we make sure that the bytecode filename in the + // archive is never exactly 16 bytes long by adding a 16 byte + // extension to it. This is to work around a bug in LLDB that + // would cause it to crash if the name of a file in an archive + // was exactly 16 bytes. + let bc_filename = obj.with_extension("bc"); + let bc_deflated_filename = tmpdir.join({ + obj.with_extension("bytecode.deflate").file_name().unwrap() + }); + + let mut bc_data = Vec::new(); + match fs::File::open(&bc_filename).and_then(|mut f| { + f.read_to_end(&mut bc_data) + }) { + Ok(..) => {} + Err(e) => sess.fatal(&format!("failed to read bytecode: {}", + e)) } - }; - - ab.add_file(&bc_deflated_filename); - // See the bottom of back::write::run_passes for an explanation - // of when we do and don't keep .0.bc files around. - let user_wants_numbered_bitcode = + let bc_data_deflated = flate::deflate_bytes(&bc_data[..]); + + let mut bc_file_deflated = match fs::File::create(&bc_deflated_filename) { + Ok(file) => file, + Err(e) => { + sess.fatal(&format!("failed to create compressed \ + bytecode file: {}", e)) + } + }; + + match write_rlib_bytecode_object_v1(&mut bc_file_deflated, + &bc_data_deflated) { + Ok(()) => {} + Err(e) => { + sess.fatal(&format!("failed to write compressed \ + bytecode: {}", e)); + } + }; + + ab.add_file(&bc_deflated_filename); + + // See the bottom of back::write::run_passes for an explanation + // of when we do and don't keep .0.bc files around. + let user_wants_numbered_bitcode = sess.opts.output_types.contains(&OutputTypeBitcode) && sess.opts.cg.codegen_units > 1; - if !sess.opts.cg.save_temps && !user_wants_numbered_bitcode { - remove(sess, &bc_filename); + if !sess.opts.cg.save_temps && !user_wants_numbered_bitcode { + remove(sess, &bc_filename); + } } - } - // After adding all files to the archive, we need to update the - // symbol table of the archive. This currently dies on OSX (see - // #11162), and isn't necessary there anyway - if !sess.target.target.options.is_like_osx || ab.using_llvm() { - ab.update_symbols(); + // After adding all files to the archive, we need to update the + // symbol table of the archive. This currently dies on OSX (see + // #11162), and isn't necessary there anyway + if !sess.target.target.options.is_like_osx || ab.using_llvm() { + ab.update_symbols(); + } } } @@ -819,6 +822,8 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, let (pname, mut cmd) = get_linker(sess); cmd.env("PATH", command_path(sess)); + // The compiler's sysroot often has some bundled tools, so add it to the + // PATH for the child. let root = sess.target_filesearch(PathKind::Native).get_lib_path(); cmd.args(&sess.target.target.options.pre_link_args); for obj in &sess.target.target.options.pre_link_objects { diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index 3a709955098c3..b01a2f796ede9 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -137,6 +137,17 @@ impl<'a> Linker for GnuLinker<'a> { fn optimize(&mut self) { if !self.sess.target.target.options.linker_is_gnu { return } + if self.sess.target.target.options.is_like_pnacl { + let arg = match self.sess.opts.optimize { + config::OptLevel::No => "-O0", + config::OptLevel::Less => "-O1", + config::OptLevel::Default => "-O2", + config::OptLevel::Aggressive => "-O3", + }; + self.cmd.arg(arg); + return; + } + // GNU-style linkers support optimization with -O. GNU ld doesn't // need a numeric argument, but other linkers do. if self.sess.opts.optimize == config::Default || diff --git a/src/librustc_trans/back/mod.rs b/src/librustc_trans/back/mod.rs new file mode 100644 index 0000000000000..fad6e3d777d42 --- /dev/null +++ b/src/librustc_trans/back/mod.rs @@ -0,0 +1,20 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use rustc_back::abi; +pub use rustc_back::rpath; +pub use rustc_back::svh; + +pub mod archive; +pub mod linker; +pub mod link; +pub mod lto; +pub mod write; +pub mod msvc; diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index b901e31a53a00..c84fa04493c68 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -313,10 +313,12 @@ impl ModuleConfig { // slp vectorization at O3. Otherwise configure other optimization aspects // of this pass manager builder. self.vectorize_loop = !sess.opts.cg.no_vectorize_loops && - (sess.opts.optimize == config::Default || - sess.opts.optimize == config::Aggressive); + (sess.opts.optimize == config::Default || + sess.opts.optimize == config::Aggressive) && + !sess.target.target.options.is_like_pnacl; self.vectorize_slp = !sess.opts.cg.no_vectorize_slp && - sess.opts.optimize == config::Aggressive; + sess.opts.optimize == config::Aggressive && + !sess.target.target.options.is_like_pnacl; self.merge_functions = sess.opts.optimize == config::Default || sess.opts.optimize == config::Aggressive; @@ -334,6 +336,8 @@ struct CodegenContext<'a> { plugin_passes: Vec, // LLVM optimizations for which we want to print remarks. remark: Passes, + + is_like_pnacl: bool, } impl<'a> CodegenContext<'a> { @@ -343,6 +347,8 @@ impl<'a> CodegenContext<'a> { handler: sess.diagnostic().handler(), plugin_passes: sess.plugin_llvm_passes.borrow().clone(), remark: sess.opts.cg.remark.clone(), + + is_like_pnacl: sess.target.target.options.is_like_pnacl, } } } @@ -560,9 +566,14 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, if config.emit_obj { let path = output_names.with_extension(&format!("{}.o", name_extra)); - with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::ObjectFileType); - }); + if !cgcx.is_like_pnacl { + with_codegen(tm, llmod, config.no_builtins, |cpm| { + write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::ObjectFileType); + }); + } else { + let out = path2cstr(&path); + llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr()); + } } }); @@ -572,7 +583,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, } pub fn run_passes(sess: &Session, - trans: &CrateTranslation, + trans: &mut CrateTranslation, output_types: &[config::OutputType], crate_output: &OutputFilenames) { // It's possible that we have `codegen_units > 1` but only one item in @@ -580,7 +591,9 @@ pub fn run_passes(sess: &Session, // case, but it would be confusing to have the validity of // `-Z lto -C codegen-units=2` depend on details of the crate being // compiled, so we complain regardless. - if sess.lto() && sess.opts.cg.codegen_units > 1 { + // PNaCl uses a bitcode linker, so this isn't an issue. + if sess.lto() && sess.opts.cg.codegen_units > 1 && + !sess.target.target.options.is_like_pnacl { // This case is impossible to handle because LTO expects to be able // to combine the entire crate and all its dependencies into a // single compilation unit, but each codegen unit is in a separate @@ -616,7 +629,7 @@ pub fn run_passes(sess: &Session, sess.opts.output_types.contains(&config::OutputTypeExe); let needs_crate_object = sess.opts.output_types.contains(&config::OutputTypeExe); - if needs_crate_bitcode { + if needs_crate_bitcode || sess.target.target.options.is_like_pnacl { modules_config.emit_bc = true; } @@ -645,7 +658,6 @@ pub fn run_passes(sess: &Session, modules_config.set_flags(sess, trans); metadata_config.set_flags(sess, trans); - // Populate a buffer with a list of codegen threads. Items are processed in // LIFO order, just because it's a tiny bit simpler that way. (The order // doesn't actually matter.) @@ -854,6 +866,7 @@ fn run_work_multithreaded(sess: &Session, let work_items_arc = Arc::new(Mutex::new(work_items)); let mut diag_emitter = SharedEmitter::new(); let mut futures = Vec::with_capacity(num_workers); + let is_like_pnacl = sess.target.target.options.is_like_pnacl; for i in 0..num_workers { let work_items_arc = work_items_arc.clone(); @@ -875,6 +888,7 @@ fn run_work_multithreaded(sess: &Session, handler: &diag_handler, plugin_passes: plugin_passes, remark: remark, + is_like_pnacl: is_like_pnacl, }; loop { @@ -967,6 +981,8 @@ pub unsafe fn configure_llvm(sess: &Session) { // using --llvm-root will have multiple platforms that rustllvm // doesn't actually link to and it's pointless to put target info // into the registry that Rust cannot generate machine code for. + // `librustc_llvm` provides no-op wrappers of these functions for + // targets LLVM can't codegen for. llvm::LLVMInitializeX86TargetInfo(); llvm::LLVMInitializeX86Target(); llvm::LLVMInitializeX86TargetMC(); diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index af894b218eff8..bede8b5c6612c 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -66,19 +66,7 @@ pub use rustc::lint; pub use rustc::plugin; pub use rustc::util; -pub mod back { - pub use rustc_back::abi; - pub use rustc_back::rpath; - pub use rustc_back::svh; - - pub mod archive; - pub mod linker; - pub mod link; - pub mod lto; - pub mod write; - pub mod msvc; -} - +pub mod back; pub mod trans; pub mod save; diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs index 326d1e2361e6d..2a81ceaff2749 100644 --- a/src/librustc_trans/trans/adt.rs +++ b/src/librustc_trans/trans/adt.rs @@ -799,12 +799,19 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, // // FIXME #10604: this breaks when vector types are present. let (size, align) = union_size_and_align(&sts[..]); - let align_s = align as u64; - assert_eq!(size % align_s, 0); - let align_units = size / align_s - 1; - + let align_s = if align > 8 && cx.sess().target.target.options.is_like_pnacl { + // On PNaCl, we have no way to represent any alignment larger + // than 8 (well, we do, but the common 16 byte vector is out). + // Fortunately, due to PNaCl's restricted IR, we don't have to + // worry about alignment (I think). + 8u64 + } else { + align as u64 + }; let discr_ty = ll_inttype(cx, ity); let discr_size = machine::llsize_of_alloc(cx, discr_ty); + assert_eq!(size % align_s, 0); + let align_units = size / align_s - 1; let fill_ty = match align_s { 1 => Type::array(&Type::i8(cx), align_units), 2 => Type::array(&Type::i16(cx), align_units), @@ -815,6 +822,13 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, align_units), _ => panic!("unsupported enum alignment: {}", align) }; + + // This check will fail in the presence of SIMD types while + // targeting PNaCl. This is because the DataLayout PNaCl uses + // specifies that vectors of 128 bits get an alignment of only 32 + // bits, preventing us from using them to force alignment. + // However, on PNaCl, it shouldn't matter anyway, hence the hack + // above. assert_eq!(machine::llalign_of_min(cx, fill_ty), align); assert_eq!(align_s % discr_size, 0); let mut fields: Vec = diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index d59cc4f4298e9..11ea2b30a7a1b 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -2216,7 +2216,7 @@ fn register_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, pub fn is_entry_fn(sess: &Session, node_id: ast::NodeId) -> bool { match *sess.entry_fn.borrow() { - Some((entry_id, _)) => node_id == entry_id, + Some((entry_id, _, _)) => node_id == entry_id, None => false } } @@ -2242,8 +2242,13 @@ pub fn create_entry_wrapper(ccx: &CrateContext, let llfty = Type::func(&[ccx.int_type(), Type::i8p(ccx).ptr_to()], &ccx.int_type()); - let llfn = declare::define_cfn(ccx, "main", llfty, - ccx.tcx().mk_nil()).unwrap_or_else(||{ + let main_name = match *ccx.sess().entry_fn.borrow() { + Some((_, ref name, _)) => (*name).to_string(), + None => "main".to_string(), + }; + + let llfn = declare::define_cfn(ccx, &main_name[..], llfty, + ccx.tcx().mk_nil()).unwrap_or_else(||{ ccx.sess().span_err(sp, "entry symbol `main` defined multiple times"); // FIXME: We should be smart and show a better diagnostic here. ccx.sess().help("did you use #[no_mangle] on `fn main`? Use #[start] instead"); @@ -2776,7 +2781,7 @@ pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslat } let modules = shared_ccx.iter() - .map(|ccx| ModuleTranslation { llcx: ccx.llcx(), llmod: ccx.llmod() }) + .map(|ccx| ModuleTranslation { llcx: ccx.llcx(), llmod: ccx.llmod(), }) .collect(); let mut reachable: Vec = shared_ccx.reachable().iter().filter_map(|id| { diff --git a/src/librustc_trans/trans/cabi.rs b/src/librustc_trans/trans/cabi.rs index 0ff5264c00f0f..2aac76eecd458 100644 --- a/src/librustc_trans/trans/cabi.rs +++ b/src/librustc_trans/trans/cabi.rs @@ -111,6 +111,7 @@ pub fn compute_abi_info(ccx: &CrateContext, ret_def: bool) -> FnType { match &ccx.sess().target.target.arch[..] { "x86" => cabi_x86::compute_abi_info(ccx, atys, rty, ret_def), + "le32" => cabi_x86::compute_abi_info(ccx, atys, rty, ret_def), "x86_64" => if ccx.sess().target.target.options.is_like_windows { cabi_x86_win64::compute_abi_info(ccx, atys, rty, ret_def) } else { diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index b7b7b28a42bfb..d62660d56c995 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -798,22 +798,37 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { /// Declare any llvm intrinsics that you might need fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option { macro_rules! ifn { - ($name:expr, fn() -> $ret:expr) => ( - if *key == $name { - let f = declare::declare_cfn(ccx, $name, Type::func(&[], &$ret), + ($name:expr, fn() -> $ret:expr, if ($cond:expr)) => ({ + let is_key = *key == $name; + if $cond && is_key { + let name = $name; + let f = declare::declare_cfn(ccx, name, Type::func(&[], &$ret), ccx.tcx().mk_nil()); - ccx.intrinsics().borrow_mut().insert($name, f.clone()); - return Some(f); - } - ); - ($name:expr, fn($($arg:expr),*) -> $ret:expr) => ( - if *key == $name { - let f = declare::declare_cfn(ccx, $name, Type::func(&[$($arg),*], &$ret), - ccx.tcx().mk_nil()); - ccx.intrinsics().borrow_mut().insert($name, f.clone()); + if ccx.sess().target.target.options.is_like_pnacl { + llvm::SetUnnamedAddr(f, false); + } + ccx.intrinsics().borrow_mut().insert(name, f); return Some(f); - } - ) + } else if is_key { return None; } + }); + ($name:expr, fn() -> $ret:expr) => (ifn!($name, fn() -> $ret, if (true))); + ($name:expr, fn($($arg:expr),*) -> $ret:expr, + if ($cond:expr)) => ({ + let is_key = *key == $name; + if $cond && is_key { + let name = $name; + let f = declare::declare_cfn(ccx, name, + Type::func(&[$($arg),*], &$ret), + ccx.tcx().mk_nil()); + if ccx.sess().target.target.options.is_like_pnacl { + llvm::SetUnnamedAddr(f, false); + } + ccx.intrinsics().borrow_mut().insert(name, f); + return Some(f); + } else if is_key { return None; } + }); + ($name:expr, fn($($arg:expr),*) -> $ret:expr) => + (ifn!($name, fn($($arg),*) -> $ret, if (true))) } macro_rules! mk_struct { ($($field_ty:expr),*) => (Type::struct_(ccx, &[$($field_ty),*], false)) @@ -838,65 +853,35 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option void); ifn!("llvm.debugtrap", fn() -> void); - ifn!("llvm.frameaddress", fn(t_i32) -> i8p); + ifn!("llvm.frameaddress", fn(t_i32) -> i8p, + if (!ccx.sess().target.target.options.is_like_pnacl)); - ifn!("llvm.powi.f32", fn(t_f32, t_i32) -> t_f32); - ifn!("llvm.powi.f64", fn(t_f64, t_i32) -> t_f64); - ifn!("llvm.pow.f32", fn(t_f32, t_f32) -> t_f32); - ifn!("llvm.pow.f64", fn(t_f64, t_f64) -> t_f64); + ifn!("llvm.powi.f32", fn(t_f32, t_i32) -> t_f32, + if (!ccx.sess().target.target.options.is_like_pnacl)); + ifn!("llvm.powi.f64", fn(t_f64, t_i32) -> t_f64, + if (!ccx.sess().target.target.options.is_like_pnacl)); ifn!("llvm.sqrt.f32", fn(t_f32) -> t_f32); ifn!("llvm.sqrt.f64", fn(t_f64) -> t_f64); - ifn!("llvm.sin.f32", fn(t_f32) -> t_f32); - ifn!("llvm.sin.f64", fn(t_f64) -> t_f64); - ifn!("llvm.cos.f32", fn(t_f32) -> t_f32); - ifn!("llvm.cos.f64", fn(t_f64) -> t_f64); - ifn!("llvm.exp.f32", fn(t_f32) -> t_f32); - ifn!("llvm.exp.f64", fn(t_f64) -> t_f64); - ifn!("llvm.exp2.f32", fn(t_f32) -> t_f32); - ifn!("llvm.exp2.f64", fn(t_f64) -> t_f64); - ifn!("llvm.log.f32", fn(t_f32) -> t_f32); - ifn!("llvm.log.f64", fn(t_f64) -> t_f64); - ifn!("llvm.log10.f32", fn(t_f32) -> t_f32); - ifn!("llvm.log10.f64", fn(t_f64) -> t_f64); - ifn!("llvm.log2.f32", fn(t_f32) -> t_f32); - ifn!("llvm.log2.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.fma.f32", fn(t_f32, t_f32, t_f32) -> t_f32); - ifn!("llvm.fma.f64", fn(t_f64, t_f64, t_f64) -> t_f64); - - ifn!("llvm.fabs.f32", fn(t_f32) -> t_f32); - ifn!("llvm.fabs.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.floor.f32", fn(t_f32) -> t_f32); - ifn!("llvm.floor.f64", fn(t_f64) -> t_f64); - ifn!("llvm.ceil.f32", fn(t_f32) -> t_f32); - ifn!("llvm.ceil.f64", fn(t_f64) -> t_f64); - ifn!("llvm.trunc.f32", fn(t_f32) -> t_f32); - ifn!("llvm.trunc.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.copysign.f32", fn(t_f32, t_f32) -> t_f32); - ifn!("llvm.copysign.f64", fn(t_f64, t_f64) -> t_f64); - ifn!("llvm.round.f32", fn(t_f32) -> t_f32); - ifn!("llvm.round.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.rint.f32", fn(t_f32) -> t_f32); - ifn!("llvm.rint.f64", fn(t_f64) -> t_f64); - ifn!("llvm.nearbyint.f32", fn(t_f32) -> t_f32); - ifn!("llvm.nearbyint.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.ctpop.i8", fn(t_i8) -> t_i8); - ifn!("llvm.ctpop.i16", fn(t_i16) -> t_i16); + + ifn!("llvm.ctpop.i8", fn(t_i8) -> t_i8, + if (!ccx.sess().target.target.options.is_like_pnacl)); + ifn!("llvm.ctpop.i16", fn(t_i16) -> t_i16, + if (!ccx.sess().target.target.options.is_like_pnacl)); ifn!("llvm.ctpop.i32", fn(t_i32) -> t_i32); ifn!("llvm.ctpop.i64", fn(t_i64) -> t_i64); - ifn!("llvm.ctlz.i8", fn(t_i8 , i1) -> t_i8); - ifn!("llvm.ctlz.i16", fn(t_i16, i1) -> t_i16); + ifn!("llvm.ctlz.i8", fn(t_i8 , i1) -> t_i8, + if (!ccx.sess().target.target.options.is_like_pnacl)); + ifn!("llvm.ctlz.i16", fn(t_i16, i1) -> t_i16, + if (!ccx.sess().target.target.options.is_like_pnacl)); ifn!("llvm.ctlz.i32", fn(t_i32, i1) -> t_i32); ifn!("llvm.ctlz.i64", fn(t_i64, i1) -> t_i64); - ifn!("llvm.cttz.i8", fn(t_i8 , i1) -> t_i8); - ifn!("llvm.cttz.i16", fn(t_i16, i1) -> t_i16); + ifn!("llvm.cttz.i8", fn(t_i8 , i1) -> t_i8, + if (!ccx.sess().target.target.options.is_like_pnacl)); + ifn!("llvm.cttz.i16", fn(t_i16, i1) -> t_i16, + if (!ccx.sess().target.target.options.is_like_pnacl)); ifn!("llvm.cttz.i32", fn(t_i32, i1) -> t_i32); ifn!("llvm.cttz.i64", fn(t_i64, i1) -> t_i64); @@ -942,6 +927,8 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option void), $llvm_version:expr) => ( if unsafe { llvm::LLVMVersionMinor() >= $llvm_version } { @@ -967,7 +954,20 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option $ret:expr, $llvm_version:expr) => ( - if unsafe { llvm::LLVMVersionMinor() >= $llvm_version } { + if unsafe { llvm::LLVMVersionMinor() >= $llvm_version } && + !ccx.sess().target.target.options.is_like_pnacl { + // The `if key == $name` is already in ifn! + ifn!($name, fn($($arg),*) -> $ret); + } else if *key == $name { + let f = declare::declare_cfn(ccx, stringify!($cname), + Type::func(&[$($arg),*], &$ret), + ty::mk_nil(ccx.tcx())); + ccx.intrinsics().borrow_mut().insert($name, f.clone()); + return Some(f); + } + ); + ($name:expr, $cname:ident ($($arg:expr),*) -> $ret:expr) => ( + if !ccx.sess().target.target.options.is_like_pnacl { // The `if key == $name` is already in ifn! ifn!($name, fn($($arg),*) -> $ret); } else if *key == $name { @@ -980,7 +980,47 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option t_f32); + compatible_ifn!("llvm.copysign.f64", copysign(t_f64, t_f64) -> t_f64); + compatible_ifn!("llvm.round.f32", roundf(t_f32) -> t_f32); + compatible_ifn!("llvm.round.f64", round(t_f64) -> t_f64); + compatible_ifn!("llvm.assume", noop(llvmcompat_assume(i1) -> void), 6); + compatible_ifn!("llvm.pow.f32", powf(t_f32, t_f32) -> t_f32); + compatible_ifn!("llvm.pow.f64", pow (t_f64, t_f64) -> t_f64); + + compatible_ifn!("llvm.sin.f32", sinf(t_f32) -> t_f32); + compatible_ifn!("llvm.sin.f64", sin (t_f64) -> t_f64); + compatible_ifn!("llvm.cos.f32", cosf(t_f32) -> t_f32); + compatible_ifn!("llvm.cos.f64", cos (t_f64) -> t_f64); + compatible_ifn!("llvm.exp.f32", expf(t_f32) -> t_f32); + compatible_ifn!("llvm.exp.f64", exp (t_f64) -> t_f64); + compatible_ifn!("llvm.exp2.f32", exp2f(t_f32) -> t_f32); + compatible_ifn!("llvm.exp2.f64", exp2(t_f64) -> t_f64); + compatible_ifn!("llvm.log.f32", logf(t_f32) -> t_f32); + compatible_ifn!("llvm.log.f64", log (t_f64) -> t_f64); + compatible_ifn!("llvm.log10.f32",log10f(t_f32) -> t_f32); + compatible_ifn!("llvm.log10.f64",log10(t_f64) -> t_f64); + compatible_ifn!("llvm.log2.f32", log2f(t_f32) -> t_f32); + compatible_ifn!("llvm.log2.f64", log2(t_f64) -> t_f64); + + compatible_ifn!("llvm.fma.f32", fmaf(t_f32, t_f32, t_f32) -> t_f32); + compatible_ifn!("llvm.fma.f64", fma (t_f64, t_f64, t_f64) -> t_f64); + + compatible_ifn!("llvm.fabs.f32", fabsf(t_f32) -> t_f32); + compatible_ifn!("llvm.fabs.f64", fabs(t_f64) -> t_f64); + + compatible_ifn!("llvm.floor.f32",floorf(t_f32) -> t_f32); + compatible_ifn!("llvm.floor.f64",floor(t_f64) -> t_f64); + compatible_ifn!("llvm.ceil.f32", ceilf(t_f32) -> t_f32); + compatible_ifn!("llvm.ceil.f64", ceil(t_f64) -> t_f64); + compatible_ifn!("llvm.trunc.f32",truncf(t_f32) -> t_f32); + compatible_ifn!("llvm.trunc.f64",trunc(t_f64) -> t_f64); + + compatible_ifn!("llvm.rint.f32", rintf(t_f32) -> t_f32); + compatible_ifn!("llvm.rint.f64", rint(t_f64) -> t_f64); + compatible_ifn!("llvm.nearbyint.f32", nearbyintf(t_f32) -> t_f32); + compatible_ifn!("llvm.nearbyint.f64", nearbyint(t_f64) -> t_f64); if ccx.sess().opts.debuginfo != NoDebugInfo { ifn!("llvm.dbg.declare", fn(Type::metadata(ccx), Type::metadata(ccx)) -> void); diff --git a/src/librustc_trans/trans/debuginfo/gdb.rs b/src/librustc_trans/trans/debuginfo/gdb.rs index f7b0f37c9ff78..903bd4337b346 100644 --- a/src/librustc_trans/trans/debuginfo/gdb.rs +++ b/src/librustc_trans/trans/debuginfo/gdb.rs @@ -26,16 +26,27 @@ use syntax::attr; /// Inserts a side-effect free instruction sequence that makes sure that the /// .debug_gdb_scripts global is referenced, so it isn't removed by the linker. pub fn insert_reference_to_gdb_debug_scripts_section_global(ccx: &CrateContext) { + use trans::common::C_i32; if needs_gdb_debug_scripts_section(ccx) { let empty = CString::new("").unwrap(); let gdb_debug_scripts_section_global = get_or_insert_gdb_debug_scripts_section_global(ccx); unsafe { + // PNaCl's -nacl-rewrite-atomics can't rewrite loading directly from + // gdb_debug_scripts_section_global, ie a large array of i8. + let indices = [C_i32(ccx, 0), C_i32(ccx, 0)]; + let element = + llvm::LLVMBuildInBoundsGEP(ccx.raw_builder(), + gdb_debug_scripts_section_global, + indices.as_ptr(), + indices.len() as ::libc::c_uint, + empty.as_ptr()); let volative_load_instruction = llvm::LLVMBuildLoad(ccx.raw_builder(), - gdb_debug_scripts_section_global, + element, empty.as_ptr()); llvm::LLVMSetVolatile(volative_load_instruction, llvm::True); + llvm::LLVMSetAlignment(volative_load_instruction, 1); } } } diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 9c0e121e156f9..785fb443ae43a 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -314,7 +314,7 @@ fn check_start_fn_ty(ccx: &CrateCtxt, fn check_for_entry_fn(ccx: &CrateCtxt) { let tcx = ccx.tcx; match *tcx.sess.entry_fn.borrow() { - Some((id, sp)) => match tcx.sess.entry_type.get() { + Some((id, _, sp)) => match tcx.sess.entry_type.get() { Some(config::EntryMain) => check_main_fn_ty(ccx, id, sp), Some(config::EntryStart) => check_start_fn_ty(ccx, id, sp), Some(config::EntryNone) => {} diff --git a/src/librustdoc/flock.rs b/src/librustdoc/flock.rs index 847e28d2bc510..cb775d4fabfb4 100644 --- a/src/librustdoc/flock.rs +++ b/src/librustdoc/flock.rs @@ -47,6 +47,27 @@ mod imp { pub const F_SETLKW: libc::c_int = 7; } + #[cfg(target_os = "nacl")] + mod os { + use libc; + + pub struct flock { + pub l_type: libc::c_short, + pub l_whence: libc::c_short, + pub l_start: libc::off_t, + pub l_len: libc::off_t, + pub l_pid: libc::pid_t, + + // not actually here, but brings in line with freebsd + pub l_sysid: libc::c_int, + } + + pub const F_WRLCK: libc::c_short = 2; + pub const F_UNLCK: libc::c_short = 3; + pub const F_SETLK: libc::c_int = 8; + pub const F_SETLKW: libc::c_int = 9; + } + #[cfg(target_os = "freebsd")] mod os { use libc; diff --git a/src/libstd/dynamic_lib.rs b/src/libstd/dynamic_lib.rs index 3621d18daed81..d3c03e48bb951 100644 --- a/src/libstd/dynamic_lib.rs +++ b/src/libstd/dynamic_lib.rs @@ -118,7 +118,7 @@ impl DynamicLibrary { } } -#[cfg(all(test, not(target_os = "ios")))] +#[cfg(all(test, not(target_os = "ios"), not(target_os = "nacl")))] mod tests { use super::*; use prelude::v1::*; @@ -372,3 +372,30 @@ mod dl { fn SetErrorMode(uMode: libc::c_uint) -> libc::c_uint; } } + +#[cfg(target_os = "nacl")] +pub mod dl { + use ffi::OsStr; + use ptr; + use result::Result; + use result::Result::Err; + use libc; + use string::String; + use ops::FnOnce; + use option::Option; + + pub fn open(_filename: Option<&OsStr>) -> Result<*mut u8, String> { + Err(format!("NaCl + Newlib doesn't impl loading shared objects")) + } + + pub fn check_for_errors_in(_f: F) -> Result + where F: FnOnce() -> T, + { + Err(format!("NaCl doesn't support shared objects")) + } + + pub unsafe fn symbol(_handle: *mut u8, _symbol: *const libc::c_char) -> *mut u8 { + ptr::null_mut() + } + pub unsafe fn close(_handle: *mut u8) { } +} diff --git a/src/libstd/env.rs b/src/libstd/env.rs index d1a49da461ec9..b7f45c55dc809 100644 --- a/src/libstd/env.rs +++ b/src/libstd/env.rs @@ -805,6 +805,27 @@ mod os { pub const EXE_EXTENSION: &'static str = "exe"; } +#[cfg(all(target_os = "nacl", not(target_arch = "le32")))] +mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "nacl"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ".nexe"; + pub const EXE_EXTENSION: &'static str = "nexe"; +} +#[cfg(all(target_os = "nacl", target_arch = "le32"))] +mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "pnacl"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".pso"; + pub const DLL_EXTENSION: &'static str = "pso"; + pub const EXE_SUFFIX: &'static str = ".pexe"; + pub const EXE_EXTENSION: &'static str = "pexe"; +} + #[cfg(target_arch = "x86")] mod arch { pub const ARCH: &'static str = "x86"; @@ -840,6 +861,11 @@ mod arch { pub const ARCH: &'static str = "powerpc"; } +#[cfg(target_arch = "le32")] +mod arch { + pub const ARCH: &'static str = "le32"; +} + #[cfg(test)] mod tests { use prelude::v1::*; diff --git a/src/libstd/os/nacl/raw.rs b/src/libstd/os/nacl/raw.rs index d811b94c847b3..36f8a5fb5c923 100644 --- a/src/libstd/os/nacl/raw.rs +++ b/src/libstd/os/nacl/raw.rs @@ -12,241 +12,36 @@ #![stable(feature = "raw_ext", since = "1.1.0")] +#[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i32; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = i32; #[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u32; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type pid_t = i32; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type uid_t = u32; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type gid_t = u32; #[stable(feature = "raw_ext", since = "1.1.0")] pub type mode_t = u32; - -pub use self::arch::{off_t, ino_t, nlink_t, blksize_t, blkcnt_t, stat, time_t}; - -#[cfg(any(target_arch = "x86", - target_arch = "le32", - target_arch = "powerpc", - target_arch = "arm"))] -mod arch { - use super::{dev_t, mode_t}; - use os::raw::{c_long, c_short}; - use os::unix::raw::{gid_t, uid_t}; - - #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = i32; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = i32; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u32; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u32; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = i32; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i32; - - #[repr(C)] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub struct stat { - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_dev: dev_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad1: c_short, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: ino_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: mode_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: nlink_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: uid_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: gid_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: dev_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad2: c_short, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: off_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: blksize_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: blkcnt_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __unused4: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __unused5: c_long, - } -} - -#[cfg(any(target_arch = "mips", - target_arch = "mipsel"))] -mod arch { - use super::{dev_t, mode_t}; - use os::raw::c_long; - use os::unix::raw::{gid_t, uid_t}; - - #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = i32; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = i32; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u32; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u32; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = i32; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i32; - - #[repr(C)] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub struct stat { - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_dev: c_ulong, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_pad1: [c_long; 3], - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: ino_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: mode_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: nlink_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: uid_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: gid_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: c_ulong, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_pad2: [c_long; 2], - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: off_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_pad3: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: blksize_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: blkcnt_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_pad5: [c_long; 14], - } -} - -#[cfg(target_arch = "aarch64")] -mod arch { - use super::{dev_t, mode_t}; - use os::raw::{c_long, c_int}; - use os::unix::raw::{gid_t, uid_t}; - - #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = i64; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = i32; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u32; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = i64; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64; - - #[repr(C)] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub struct stat { - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_dev: dev_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: ino_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: mode_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: nlink_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: uid_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: gid_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: dev_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad1: dev_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: off_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: blksize_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad2: c_int, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: blkcnt_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __unused: [c_int; 2], - } -} - -#[cfg(target_arch = "x86_64")] -mod arch { - use super::{dev_t, mode_t}; - use os::raw::{c_long, c_int}; - use os::unix::raw::{gid_t, uid_t}; - - #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = i64; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = i64; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = i64; - #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64; - - #[repr(C)] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub struct stat { - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_dev: dev_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: ino_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: nlink_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: mode_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: uid_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: gid_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad0: c_int, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: dev_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: off_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: blksize_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: blkcnt_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __unused: [c_long; 3], - } +#[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u32; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = i32; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = i32; + +#[repr(C)] +#[derive(Copy, Clone)] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub struct stat { + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_dev: dev_t, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_ino: ino_t, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_mode: mode_t, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_nlink: nlink_t, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_uid: uid_t, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_gid: gid_t, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_rdev: dev_t, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_size: off_t, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_blksize: blksize_t, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_blocks: blkcnt_t, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_atime: time_t, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_atime_nsec: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_mtime: time_t, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_mtime_nsec: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_ctime: time_t, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_ctime_nsec: i64, } diff --git a/src/libstd/rt/args.rs b/src/libstd/rt/args.rs index e77a2bbd0b902..0ab2791ffc4ae 100644 --- a/src/libstd/rt/args.rs +++ b/src/libstd/rt/args.rs @@ -46,7 +46,8 @@ pub fn clone() -> Option>> { imp::clone() } target_os = "dragonfly", target_os = "bitrig", target_os = "netbsd", - target_os = "openbsd"))] + target_os = "openbsd", + target_os = "nacl"))] mod imp { use prelude::v1::*; diff --git a/src/libstd/rt/libunwind.rs b/src/libstd/rt/libunwind.rs index fde612014e992..bdc521864d97e 100644 --- a/src/libstd/rt/libunwind.rs +++ b/src/libstd/rt/libunwind.rs @@ -86,6 +86,9 @@ pub const unwinder_private_data_size: usize = 2; #[cfg(target_arch = "powerpc")] pub const unwinder_private_data_size: usize = 2; +#[cfg(target_os = "nacl")] +pub const unwinder_private_data_size: usize = 5; + #[repr(C)] pub struct _Unwind_Exception { pub exception_class: _Unwind_Exception_Class, @@ -99,8 +102,9 @@ pub type _Unwind_Exception_Cleanup_Fn = extern "C" fn(unwind_code: _Unwind_Reason_Code, exception: *mut _Unwind_Exception); -#[cfg(any(all(target_os = "linux", not(target_env = "musl")), - target_os = "freebsd"))] +#[cfg(all(any(all(target_os = "linux", not(target_env = "musl")), + target_os = "freebsd"), + not(target_os = "nacl")))] #[link(name = "gcc_s")] extern {} diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 56bf73db3992e..d02d998445ea9 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -115,8 +115,8 @@ fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize { // // Hence, we set SIGPIPE to ignore when the program starts up in order // to prevent this problem. - #[cfg(windows)] fn ignore_sigpipe() {} - #[cfg(unix)] fn ignore_sigpipe() { + #[cfg(any(windows, target_os = "nacl"))] fn ignore_sigpipe() {} + #[cfg(all(unix, not(target_os = "nacl")))] fn ignore_sigpipe() { use libc; use libc::funcs::posix01::signal::signal; unsafe { diff --git a/src/libstd/rt/unwind/gcc.rs b/src/libstd/rt/unwind/gcc.rs index 55deb048b7ee5..efdd0a98b4090 100644 --- a/src/libstd/rt/unwind/gcc.rs +++ b/src/libstd/rt/unwind/gcc.rs @@ -83,6 +83,7 @@ pub mod eabi { use rt::libunwind as uw; use libc::c_int; + #[cfg(not(target_os = "nacl"))] extern { fn __gcc_personality_v0(version: c_int, actions: uw::_Unwind_Action, @@ -91,6 +92,20 @@ pub mod eabi { context: *mut uw::_Unwind_Context) -> uw::_Unwind_Reason_Code; } + #[cfg(target_os = "nacl")] + unsafe fn __gcc_personality_v0(_version: c_int, + _actions: uw::_Unwind_Action, + _exception_class: uw::_Unwind_Exception_Class, + _ue_header: *mut uw::_Unwind_Exception, + _context: *mut uw::_Unwind_Context) -> + uw::_Unwind_Reason_Code + { + // we just need to be here to prevent linker errors. + // based on my analysis of PNaClSJLJ.cpp and ExceptionInfoWriter.cpp, + // the personality functions are just discarded without any consultation. + use core::intrinsics::abort; + abort() + } #[lang = "eh_personality"] #[no_mangle] diff --git a/src/libstd/rtdeps.rs b/src/libstd/rtdeps.rs index a3b2ab7705eaf..8383b3ec18972 100644 --- a/src/libstd/rtdeps.rs +++ b/src/libstd/rtdeps.rs @@ -44,6 +44,14 @@ extern {} #[link(name = "pthread")] extern {} +// For PNaCl targets, nacl_io is a Pepper wrapper for some IO functions +// missing (ie always error) in Newlib. +#[cfg(all(target_os = "nacl", not(test)))] +#[link(name = "nacl_io", kind = "static")] +#[link(name = "c++", kind = "static")] // for `nacl_io` and EH. +#[link(name = "pthread", kind = "static")] +extern {} + #[cfg(target_os = "macos")] #[link(name = "System")] extern {} diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index b205b6df4cb01..24cdf32fb7524 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -13,7 +13,7 @@ #[cfg(stage0)] use prelude::v1::*; -pub mod backtrace; +#[cfg_attr(target_os = "nacl", allow(dead_code))] pub mod backtrace; pub mod condvar; pub mod mutex; pub mod net; diff --git a/src/libstd/sys/common/stack.rs b/src/libstd/sys/common/stack.rs index 41c8ac4aed30d..6f0e54769a364 100644 --- a/src/libstd/sys/common/stack.rs +++ b/src/libstd/sys/common/stack.rs @@ -204,7 +204,8 @@ pub unsafe fn record_sp_limit(limit: usize) { all(target_arch = "x86", target_os = "freebsd"), target_os = "bitrig", target_os = "netbsd", - target_os = "openbsd"))] + target_os = "openbsd", + target_os = "nacl"))] unsafe fn target_record_sp_limit(_: usize) { } } @@ -303,7 +304,8 @@ pub unsafe fn get_sp_limit() -> usize { all(target_arch = "x86", target_os = "freebsd"), target_os = "bitrig", target_os = "netbsd", - target_os = "openbsd"))] + target_os = "openbsd", + target_os = "nacl"))] #[inline(always)] unsafe fn target_get_sp_limit() -> usize { 1024 diff --git a/src/libstd/sys/unix/backtrace.rs b/src/libstd/sys/unix/backtrace.rs deleted file mode 100644 index ae8bfb07aaf1e..0000000000000 --- a/src/libstd/sys/unix/backtrace.rs +++ /dev/null @@ -1,587 +0,0 @@ -// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/// Backtrace support built on libgcc with some extra OS-specific support -/// -/// Some methods of getting a backtrace: -/// -/// * The backtrace() functions on unix. It turns out this doesn't work very -/// well for green threads on OSX, and the address to symbol portion of it -/// suffers problems that are described below. -/// -/// * Using libunwind. This is more difficult than it sounds because libunwind -/// isn't installed everywhere by default. It's also a bit of a hefty library, -/// so possibly not the best option. When testing, libunwind was excellent at -/// getting both accurate backtraces and accurate symbols across platforms. -/// This route was not chosen in favor of the next option, however. -/// -/// * We're already using libgcc_s for exceptions in rust (triggering thread -/// unwinding and running destructors on the stack), and it turns out that it -/// conveniently comes with a function that also gives us a backtrace. All of -/// these functions look like _Unwind_*, but it's not quite the full -/// repertoire of the libunwind API. Due to it already being in use, this was -/// the chosen route of getting a backtrace. -/// -/// After choosing libgcc_s for backtraces, the sad part is that it will only -/// give us a stack trace of instruction pointers. Thankfully these instruction -/// pointers are accurate (they work for green and native threads), but it's -/// then up to us again to figure out how to translate these addresses to -/// symbols. As with before, we have a few options. Before, that, a little bit -/// of an interlude about symbols. This is my very limited knowledge about -/// symbol tables, and this information is likely slightly wrong, but the -/// general idea should be correct. -/// -/// When talking about symbols, it's helpful to know a few things about where -/// symbols are located. Some symbols are located in the dynamic symbol table -/// of the executable which in theory means that they're available for dynamic -/// linking and lookup. Other symbols end up only in the local symbol table of -/// the file. This loosely corresponds to pub and priv functions in Rust. -/// -/// Armed with this knowledge, we know that our solution for address to symbol -/// translation will need to consult both the local and dynamic symbol tables. -/// With that in mind, here's our options of translating an address to -/// a symbol. -/// -/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr() -/// behind the scenes to translate, and this is why backtrace() was not used. -/// Conveniently, this method works fantastically on OSX. It appears dladdr() -/// uses magic to consult the local symbol table, or we're putting everything -/// in the dynamic symbol table anyway. Regardless, for OSX, this is the -/// method used for translation. It's provided by the system and easy to do.o -/// -/// Sadly, all other systems have a dladdr() implementation that does not -/// consult the local symbol table. This means that most functions are blank -/// because they don't have symbols. This means that we need another solution. -/// -/// * Use unw_get_proc_name(). This is part of the libunwind api (not the -/// libgcc_s version of the libunwind api), but involves taking a dependency -/// to libunwind. We may pursue this route in the future if we bundle -/// libunwind, but libunwind was unwieldy enough that it was not chosen at -/// this time to provide this functionality. -/// -/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a -/// semi-reasonable solution. The stdlib already knows how to spawn processes, -/// so in theory it could invoke readelf, parse the output, and consult the -/// local/dynamic symbol tables from there. This ended up not getting chosen -/// due to the craziness of the idea plus the advent of the next option. -/// -/// * Use `libbacktrace`. It turns out that this is a small library bundled in -/// the gcc repository which provides backtrace and symbol translation -/// functionality. All we really need from it is the backtrace functionality, -/// and we only really need this on everything that's not OSX, so this is the -/// chosen route for now. -/// -/// In summary, the current situation uses libgcc_s to get a trace of stack -/// pointers, and we use dladdr() or libbacktrace to translate these addresses -/// to symbols. This is a bit of a hokey implementation as-is, but it works for -/// all unix platforms we support right now, so it at least gets the job done. - -#[cfg(stage0)] -use prelude::v1::*; -use io::prelude::*; - -use ffi::CStr; -use io; -use libc; -use mem; -use str; -use sync::StaticMutex; - -use sys_common::backtrace::*; - -/// As always - iOS on arm uses SjLj exceptions and -/// _Unwind_Backtrace is even not available there. Still, -/// backtraces could be extracted using a backtrace function, -/// which thanks god is public -/// -/// As mentioned in a huge comment block above, backtrace doesn't -/// play well with green threads, so while it is extremely nice -/// and simple to use it should be used only on iOS devices as the -/// only viable option. -#[cfg(all(target_os = "ios", target_arch = "arm"))] -#[inline(never)] -pub fn write(w: &mut Write) -> io::Result<()> { - extern { - fn backtrace(buf: *mut *mut libc::c_void, - sz: libc::c_int) -> libc::c_int; - } - - // while it doesn't requires lock for work as everything is - // local, it still displays much nicer backtraces when a - // couple of threads panic simultaneously - static LOCK: StaticMutex = StaticMutex::new(); - let _g = LOCK.lock(); - - try!(writeln!(w, "stack backtrace:")); - // 100 lines should be enough - const SIZE: usize = 100; - let mut buf: [*mut libc::c_void; SIZE] = unsafe {mem::zeroed()}; - let cnt = unsafe { backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as usize}; - - // skipping the first one as it is write itself - for i in 1..cnt { - try!(print(w, i as isize, buf[i], buf[i])) - } - Ok(()) -} - -#[cfg(not(all(target_os = "ios", target_arch = "arm")))] -#[inline(never)] // if we know this is a function call, we can skip it when - // tracing -pub fn write(w: &mut Write) -> io::Result<()> { - struct Context<'a> { - idx: isize, - writer: &'a mut (Write+'a), - last_error: Option, - } - - // When using libbacktrace, we use some necessary global state, so we - // need to prevent more than one thread from entering this block. This - // is semi-reasonable in terms of printing anyway, and we know that all - // I/O done here is blocking I/O, not green I/O, so we don't have to - // worry about this being a native vs green mutex. - static LOCK: StaticMutex = StaticMutex::new(); - let _g = LOCK.lock(); - - try!(writeln!(w, "stack backtrace:")); - - let mut cx = Context { writer: w, last_error: None, idx: 0 }; - return match unsafe { - uw::_Unwind_Backtrace(trace_fn, - &mut cx as *mut Context as *mut libc::c_void) - } { - uw::_URC_NO_REASON => { - match cx.last_error { - Some(err) => Err(err), - None => Ok(()) - } - } - _ => Ok(()), - }; - - extern fn trace_fn(ctx: *mut uw::_Unwind_Context, - arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { - let cx: &mut Context = unsafe { mem::transmute(arg) }; - let mut ip_before_insn = 0; - let mut ip = unsafe { - uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void - }; - if !ip.is_null() && ip_before_insn == 0 { - // this is a non-signaling frame, so `ip` refers to the address - // after the calling instruction. account for that. - ip = (ip as usize - 1) as *mut _; - } - - // dladdr() on osx gets whiny when we use FindEnclosingFunction, and - // it appears to work fine without it, so we only use - // FindEnclosingFunction on non-osx platforms. In doing so, we get a - // slightly more accurate stack trace in the process. - // - // This is often because panic involves the last instruction of a - // function being "call std::rt::begin_unwind", with no ret - // instructions after it. This means that the return instruction - // pointer points *outside* of the calling function, and by - // unwinding it we go back to the original function. - let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { - ip - } else { - unsafe { uw::_Unwind_FindEnclosingFunction(ip) } - }; - - // Don't print out the first few frames (they're not user frames) - cx.idx += 1; - if cx.idx <= 0 { return uw::_URC_NO_REASON } - // Don't print ginormous backtraces - if cx.idx > 100 { - match write!(cx.writer, " ... \n") { - Ok(()) => {} - Err(e) => { cx.last_error = Some(e); } - } - return uw::_URC_FAILURE - } - - // Once we hit an error, stop trying to print more frames - if cx.last_error.is_some() { return uw::_URC_FAILURE } - - match print(cx.writer, cx.idx, ip, symaddr) { - Ok(()) => {} - Err(e) => { cx.last_error = Some(e); } - } - - // keep going - return uw::_URC_NO_REASON - } -} - -#[cfg(any(target_os = "macos", target_os = "ios"))] -fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void, - _symaddr: *mut libc::c_void) -> io::Result<()> { - use intrinsics; - #[repr(C)] - struct Dl_info { - dli_fname: *const libc::c_char, - dli_fbase: *mut libc::c_void, - dli_sname: *const libc::c_char, - dli_saddr: *mut libc::c_void, - } - extern { - fn dladdr(addr: *const libc::c_void, - info: *mut Dl_info) -> libc::c_int; - } - - let mut info: Dl_info = unsafe { intrinsics::init() }; - if unsafe { dladdr(addr, &mut info) == 0 } { - output(w, idx,addr, None) - } else { - output(w, idx, addr, Some(unsafe { - CStr::from_ptr(info.dli_sname).to_bytes() - })) - } -} - -#[cfg(not(any(target_os = "macos", target_os = "ios")))] -fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void, - symaddr: *mut libc::c_void) -> io::Result<()> { - use env; - use os::unix::prelude::*; - use ptr; - - //////////////////////////////////////////////////////////////////////// - // libbacktrace.h API - //////////////////////////////////////////////////////////////////////// - type backtrace_syminfo_callback = - extern "C" fn(data: *mut libc::c_void, - pc: libc::uintptr_t, - symname: *const libc::c_char, - symval: libc::uintptr_t, - symsize: libc::uintptr_t); - type backtrace_full_callback = - extern "C" fn(data: *mut libc::c_void, - pc: libc::uintptr_t, - filename: *const libc::c_char, - lineno: libc::c_int, - function: *const libc::c_char) -> libc::c_int; - type backtrace_error_callback = - extern "C" fn(data: *mut libc::c_void, - msg: *const libc::c_char, - errnum: libc::c_int); - enum backtrace_state {} - #[link(name = "backtrace", kind = "static")] - #[cfg(not(test))] - extern {} - - extern { - fn backtrace_create_state(filename: *const libc::c_char, - threaded: libc::c_int, - error: backtrace_error_callback, - data: *mut libc::c_void) - -> *mut backtrace_state; - fn backtrace_syminfo(state: *mut backtrace_state, - addr: libc::uintptr_t, - cb: backtrace_syminfo_callback, - error: backtrace_error_callback, - data: *mut libc::c_void) -> libc::c_int; - fn backtrace_pcinfo(state: *mut backtrace_state, - addr: libc::uintptr_t, - cb: backtrace_full_callback, - error: backtrace_error_callback, - data: *mut libc::c_void) -> libc::c_int; - } - - //////////////////////////////////////////////////////////////////////// - // helper callbacks - //////////////////////////////////////////////////////////////////////// - - type FileLine = (*const libc::c_char, libc::c_int); - - extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char, - _errnum: libc::c_int) { - // do nothing for now - } - extern fn syminfo_cb(data: *mut libc::c_void, - _pc: libc::uintptr_t, - symname: *const libc::c_char, - _symval: libc::uintptr_t, - _symsize: libc::uintptr_t) { - let slot = data as *mut *const libc::c_char; - unsafe { *slot = symname; } - } - extern fn pcinfo_cb(data: *mut libc::c_void, - _pc: libc::uintptr_t, - filename: *const libc::c_char, - lineno: libc::c_int, - _function: *const libc::c_char) -> libc::c_int { - if !filename.is_null() { - let slot = data as *mut &mut [FileLine]; - let buffer = unsafe {ptr::read(slot)}; - - // if the buffer is not full, add file:line to the buffer - // and adjust the buffer for next possible calls to pcinfo_cb. - if !buffer.is_empty() { - buffer[0] = (filename, lineno); - unsafe { ptr::write(slot, &mut buffer[1..]); } - } - } - - 0 - } - - // The libbacktrace API supports creating a state, but it does not - // support destroying a state. I personally take this to mean that a - // state is meant to be created and then live forever. - // - // I would love to register an at_exit() handler which cleans up this - // state, but libbacktrace provides no way to do so. - // - // With these constraints, this function has a statically cached state - // that is calculated the first time this is requested. Remember that - // backtracing all happens serially (one global lock). - // - // An additionally oddity in this function is that we initialize the - // filename via self_exe_name() to pass to libbacktrace. It turns out - // that on Linux libbacktrace seamlessly gets the filename of the - // current executable, but this fails on freebsd. by always providing - // it, we make sure that libbacktrace never has a reason to not look up - // the symbols. The libbacktrace API also states that the filename must - // be in "permanent memory", so we copy it to a static and then use the - // static as the pointer. - // - // FIXME: We also call self_exe_name() on DragonFly BSD. I haven't - // tested if this is required or not. - unsafe fn init_state() -> *mut backtrace_state { - static mut STATE: *mut backtrace_state = 0 as *mut backtrace_state; - static mut LAST_FILENAME: [libc::c_char; 256] = [0; 256]; - if !STATE.is_null() { return STATE } - let selfname = if cfg!(target_os = "freebsd") || - cfg!(target_os = "dragonfly") || - cfg!(target_os = "bitrig") || - cfg!(target_os = "netbsd") || - cfg!(target_os = "openbsd") { - env::current_exe().ok() - } else { - None - }; - let filename = match selfname { - Some(path) => { - let bytes = path.as_os_str().as_bytes(); - if bytes.len() < LAST_FILENAME.len() { - let i = bytes.iter(); - for (slot, val) in LAST_FILENAME.iter_mut().zip(i) { - *slot = *val as libc::c_char; - } - LAST_FILENAME.as_ptr() - } else { - ptr::null() - } - } - None => ptr::null(), - }; - STATE = backtrace_create_state(filename, 0, error_cb, - ptr::null_mut()); - return STATE - } - - //////////////////////////////////////////////////////////////////////// - // translation - //////////////////////////////////////////////////////////////////////// - - // backtrace errors are currently swept under the rug, only I/O - // errors are reported - let state = unsafe { init_state() }; - if state.is_null() { - return output(w, idx, addr, None) - } - let mut data = ptr::null(); - let data_addr = &mut data as *mut *const libc::c_char; - let ret = unsafe { - backtrace_syminfo(state, symaddr as libc::uintptr_t, - syminfo_cb, error_cb, - data_addr as *mut libc::c_void) - }; - if ret == 0 || data.is_null() { - try!(output(w, idx, addr, None)); - } else { - try!(output(w, idx, addr, Some(unsafe { CStr::from_ptr(data).to_bytes() }))); - } - - // pcinfo may return an arbitrary number of file:line pairs, - // in the order of stack trace (i.e. inlined calls first). - // in order to avoid allocation, we stack-allocate a fixed size of entries. - const FILELINE_SIZE: usize = 32; - let mut fileline_buf = [(ptr::null(), -1); FILELINE_SIZE]; - let ret; - let fileline_count; - { - let mut fileline_win: &mut [FileLine] = &mut fileline_buf; - let fileline_addr = &mut fileline_win as *mut &mut [FileLine]; - ret = unsafe { - backtrace_pcinfo(state, addr as libc::uintptr_t, - pcinfo_cb, error_cb, - fileline_addr as *mut libc::c_void) - }; - fileline_count = FILELINE_SIZE - fileline_win.len(); - } - if ret == 0 { - for (i, &(file, line)) in fileline_buf[..fileline_count].iter().enumerate() { - if file.is_null() { continue; } // just to be sure - let file = unsafe { CStr::from_ptr(file).to_bytes() }; - try!(output_fileline(w, file, line, i == FILELINE_SIZE - 1)); - } - } - - Ok(()) -} - -// Finally, after all that work above, we can emit a symbol. -fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void, - s: Option<&[u8]>) -> io::Result<()> { - try!(write!(w, " {:2}: {:2$?} - ", idx, addr, HEX_WIDTH)); - match s.and_then(|s| str::from_utf8(s).ok()) { - Some(string) => try!(demangle(w, string)), - None => try!(write!(w, "")), - } - w.write_all(&['\n' as u8]) -} - -#[allow(dead_code)] -fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int, - more: bool) -> io::Result<()> { - let file = str::from_utf8(file).unwrap_or(""); - // prior line: " ##: {:2$} - func" - try!(write!(w, " {:3$}at {}:{}", "", file, line, HEX_WIDTH)); - if more { - try!(write!(w, " <... and possibly more>")); - } - w.write_all(&['\n' as u8]) -} - -/// Unwind library interface used for backtraces -/// -/// Note that dead code is allowed as here are just bindings -/// iOS doesn't use all of them it but adding more -/// platform-specific configs pollutes the code too much -#[allow(non_camel_case_types)] -#[allow(non_snake_case)] -#[allow(dead_code)] -mod uw { - pub use self::_Unwind_Reason_Code::*; - - use libc; - - #[repr(C)] - pub enum _Unwind_Reason_Code { - _URC_NO_REASON = 0, - _URC_FOREIGN_EXCEPTION_CAUGHT = 1, - _URC_FATAL_PHASE2_ERROR = 2, - _URC_FATAL_PHASE1_ERROR = 3, - _URC_NORMAL_STOP = 4, - _URC_END_OF_STACK = 5, - _URC_HANDLER_FOUND = 6, - _URC_INSTALL_CONTEXT = 7, - _URC_CONTINUE_UNWIND = 8, - _URC_FAILURE = 9, // used only by ARM EABI - } - - pub enum _Unwind_Context {} - - pub type _Unwind_Trace_Fn = - extern fn(ctx: *mut _Unwind_Context, - arg: *mut libc::c_void) -> _Unwind_Reason_Code; - - extern { - // No native _Unwind_Backtrace on iOS - #[cfg(not(all(target_os = "ios", target_arch = "arm")))] - pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, - trace_argument: *mut libc::c_void) - -> _Unwind_Reason_Code; - - // available since GCC 4.2.0, should be fine for our purpose - #[cfg(all(not(all(target_os = "android", target_arch = "arm")), - not(all(target_os = "linux", target_arch = "arm"))))] - pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, - ip_before_insn: *mut libc::c_int) - -> libc::uintptr_t; - - #[cfg(all(not(target_os = "android"), - not(all(target_os = "linux", target_arch = "arm"))))] - pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) - -> *mut libc::c_void; - } - - // On android, the function _Unwind_GetIP is a macro, and this is the - // expansion of the macro. This is all copy/pasted directly from the - // header file with the definition of _Unwind_GetIP. - #[cfg(any(all(target_os = "android", target_arch = "arm"), - all(target_os = "linux", target_arch = "arm")))] - pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t { - #[repr(C)] - enum _Unwind_VRS_Result { - _UVRSR_OK = 0, - _UVRSR_NOT_IMPLEMENTED = 1, - _UVRSR_FAILED = 2, - } - #[repr(C)] - enum _Unwind_VRS_RegClass { - _UVRSC_CORE = 0, - _UVRSC_VFP = 1, - _UVRSC_FPA = 2, - _UVRSC_WMMXD = 3, - _UVRSC_WMMXC = 4, - } - #[repr(C)] - enum _Unwind_VRS_DataRepresentation { - _UVRSD_UINT32 = 0, - _UVRSD_VFPX = 1, - _UVRSD_FPAX = 2, - _UVRSD_UINT64 = 3, - _UVRSD_FLOAT = 4, - _UVRSD_DOUBLE = 5, - } - - type _Unwind_Word = libc::c_uint; - extern { - fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context, - klass: _Unwind_VRS_RegClass, - word: _Unwind_Word, - repr: _Unwind_VRS_DataRepresentation, - data: *mut libc::c_void) - -> _Unwind_VRS_Result; - } - - let mut val: _Unwind_Word = 0; - let ptr = &mut val as *mut _Unwind_Word; - let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15, - _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, - ptr as *mut libc::c_void); - (val & !1) as libc::uintptr_t - } - - // This function doesn't exist on Android or ARM/Linux, so make it same - // to _Unwind_GetIP - #[cfg(any(all(target_os = "android", target_arch = "arm"), - all(target_os = "linux", target_arch = "arm")))] - pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, - ip_before_insn: *mut libc::c_int) - -> libc::uintptr_t - { - *ip_before_insn = 0; - _Unwind_GetIP(ctx) - } - - // This function also doesn't exist on Android or ARM/Linux, so make it - // a no-op - #[cfg(any(target_os = "android", - all(target_os = "linux", target_arch = "arm")))] - pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) - -> *mut libc::c_void - { - pc - } -} diff --git a/src/libstd/sys/unix/backtrace/gcc_s.rs b/src/libstd/sys/unix/backtrace/gcc_s.rs new file mode 100644 index 0000000000000..385e7f404fb30 --- /dev/null +++ b/src/libstd/sys/unix/backtrace/gcc_s.rs @@ -0,0 +1,231 @@ +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use io::prelude::*; +use libc; +use mem; +#[cfg(stage0)] use prelude::v1::*; +use sync::StaticMutex; + +use super::printer::print; + +#[inline(never)] // if we know this is a function call, we can skip it when + // tracing +pub fn write(w: &mut Write) -> io::Result<()> { + struct Context<'a> { + idx: isize, + writer: &'a mut (Write+'a), + last_error: Option, + } + + // When using libbacktrace, we use some necessary global state, so we + // need to prevent more than one thread from entering this block. This + // is semi-reasonable in terms of printing anyway, and we know that all + // I/O done here is blocking I/O, not green I/O, so we don't have to + // worry about this being a native vs green mutex. + static LOCK: StaticMutex = StaticMutex::new(); + let _g = LOCK.lock(); + + try!(writeln!(w, "stack backtrace:")); + + let mut cx = Context { writer: w, last_error: None, idx: 0 }; + return match unsafe { + uw::_Unwind_Backtrace(trace_fn, + &mut cx as *mut Context as *mut libc::c_void) + } { + uw::_URC_NO_REASON => { + match cx.last_error { + Some(err) => Err(err), + None => Ok(()) + } + } + _ => Ok(()), + }; + + extern fn trace_fn(ctx: *mut uw::_Unwind_Context, + arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { + let cx: &mut Context = unsafe { mem::transmute(arg) }; + let mut ip_before_insn = 0; + let mut ip = unsafe { + uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void + }; + if !ip.is_null() && ip_before_insn == 0 { + // this is a non-signaling frame, so `ip` refers to the address + // after the calling instruction. account for that. + ip = (ip as usize - 1) as *mut _; + } + + // dladdr() on osx gets whiny when we use FindEnclosingFunction, and + // it appears to work fine without it, so we only use + // FindEnclosingFunction on non-osx platforms. In doing so, we get a + // slightly more accurate stack trace in the process. + // + // This is often because panic involves the last instruction of a + // function being "call std::rt::begin_unwind", with no ret + // instructions after it. This means that the return instruction + // pointer points *outside* of the calling function, and by + // unwinding it we go back to the original function. + let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { + ip + } else { + unsafe { uw::_Unwind_FindEnclosingFunction(ip) } + }; + + // Don't print out the first few frames (they're not user frames) + cx.idx += 1; + if cx.idx <= 0 { return uw::_URC_NO_REASON } + // Don't print ginormous backtraces + if cx.idx > 100 { + match write!(cx.writer, " ... \n") { + Ok(()) => {} + Err(e) => { cx.last_error = Some(e); } + } + return uw::_URC_FAILURE + } + + // Once we hit an error, stop trying to print more frames + if cx.last_error.is_some() { return uw::_URC_FAILURE } + + match print(cx.writer, cx.idx, ip, symaddr) { + Ok(()) => {} + Err(e) => { cx.last_error = Some(e); } + } + + // keep going + return uw::_URC_NO_REASON + } +} + +/// Unwind library interface used for backtraces +/// +/// Note that dead code is allowed as here are just bindings +/// iOS doesn't use all of them it but adding more +/// platform-specific configs pollutes the code too much +#[allow(non_camel_case_types)] +#[allow(non_snake_case)] +mod uw { + pub use self::_Unwind_Reason_Code::*; + + use libc; + + #[repr(C)] + pub enum _Unwind_Reason_Code { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8, + _URC_FAILURE = 9, // used only by ARM EABI + } + + pub enum _Unwind_Context {} + + pub type _Unwind_Trace_Fn = + extern fn(ctx: *mut _Unwind_Context, + arg: *mut libc::c_void) -> _Unwind_Reason_Code; + + extern { + // No native _Unwind_Backtrace on iOS + #[cfg(not(any(all(target_os = "ios", target_arch = "arm"), + target_os = "nacl")))] + pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, + trace_argument: *mut libc::c_void) + -> _Unwind_Reason_Code; + + // available since GCC 4.2.0, should be fine for our purpose + #[cfg(all(not(all(target_os = "android", target_arch = "arm")), + not(all(target_os = "linux", target_arch = "arm")), + not(target_os = "nacl")))] + pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, + ip_before_insn: *mut libc::c_int) + -> libc::uintptr_t; + + #[cfg(all(not(target_os = "android"), + not(all(target_os = "linux", target_arch = "arm")), + not(target_os = "nacl")))] + pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) + -> *mut libc::c_void; + } + + // On android, the function _Unwind_GetIP is a macro, and this is the + // expansion of the macro. This is all copy/pasted directly from the + // header file with the definition of _Unwind_GetIP. + #[cfg(any(all(target_os = "android", target_arch = "arm"), + all(target_os = "linux", target_arch = "arm")))] + pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t { + #[repr(C)] + enum _Unwind_VRS_Result { + _UVRSR_OK = 0, + _UVRSR_NOT_IMPLEMENTED = 1, + _UVRSR_FAILED = 2, + } + #[repr(C)] + enum _Unwind_VRS_RegClass { + _UVRSC_CORE = 0, + _UVRSC_VFP = 1, + _UVRSC_FPA = 2, + _UVRSC_WMMXD = 3, + _UVRSC_WMMXC = 4, + } + #[repr(C)] + enum _Unwind_VRS_DataRepresentation { + _UVRSD_UINT32 = 0, + _UVRSD_VFPX = 1, + _UVRSD_FPAX = 2, + _UVRSD_UINT64 = 3, + _UVRSD_FLOAT = 4, + _UVRSD_DOUBLE = 5, + } + + type _Unwind_Word = libc::c_uint; + extern { + fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context, + klass: _Unwind_VRS_RegClass, + word: _Unwind_Word, + repr: _Unwind_VRS_DataRepresentation, + data: *mut libc::c_void) + -> _Unwind_VRS_Result; + } + + let mut val: _Unwind_Word = 0; + let ptr = &mut val as *mut _Unwind_Word; + let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15, + _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, + ptr as *mut libc::c_void); + (val & !1) as libc::uintptr_t + } + + // This function doesn't exist on Android or ARM/Linux, so make it same + // to _Unwind_GetIP + #[cfg(any(all(target_os = "android", target_arch = "arm"), + all(target_os = "linux", target_arch = "arm")))] + pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, + ip_before_insn: *mut libc::c_int) + -> libc::uintptr_t + { + *ip_before_insn = 0; + _Unwind_GetIP(ctx) + } + + // This function also doesn't exist on Android or ARM/Linux, so make it + // a no-op + #[cfg(any(target_os = "android", + all(target_os = "linux", target_arch = "arm")))] + pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) + -> *mut libc::c_void + { + pc + } +} diff --git a/src/libstd/sys/unix/backtrace/libbacktrace.rs b/src/libstd/sys/unix/backtrace/libbacktrace.rs new file mode 100644 index 0000000000000..6571b6f657c80 --- /dev/null +++ b/src/libstd/sys/unix/backtrace/libbacktrace.rs @@ -0,0 +1,55 @@ +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// As always - iOS on arm uses SjLj exceptions and +/// _Unwind_Backtrace is even not available there. Still, +/// backtraces could be extracted using a backtrace function, +/// which thanks god is public +/// +/// As mentioned in a huge comment block above, backtrace doesn't +/// play well with green threads, so while it is extremely nice +/// and simple to use it should be used only on iOS devices as the +/// only viable option. + +use io; +use io::prelude::*; +use iter::Iterator; +use libc; +use mem; +use result::Result::Ok; +use sync::StaticMutex; + +use super::printer::print; + +#[inline(never)] +pub fn write(w: &mut Write) -> io::Result<()> { + extern { + fn backtrace(buf: *mut *mut libc::c_void, + sz: libc::c_int) -> libc::c_int; + } + + // while it doesn't requires lock for work as everything is + // local, it still displays much nicer backtraces when a + // couple of threads panic simultaneously + static LOCK: StaticMutex = StaticMutex::new(); + let _g = LOCK.lock(); + + try!(writeln!(w, "stack backtrace:")); + // 100 lines should be enough + const SIZE: usize = 100; + let mut buf: [*mut libc::c_void; SIZE] = unsafe {mem::zeroed()}; + let cnt = unsafe { backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as usize}; + + // skipping the first one as it is write itself + for i in 1..cnt { + try!(print(w, i as isize, buf[i], buf[i])) + } + Ok(()) +} diff --git a/src/libstd/sys/unix/backtrace/mod.rs b/src/libstd/sys/unix/backtrace/mod.rs new file mode 100644 index 0000000000000..54b6df682211f --- /dev/null +++ b/src/libstd/sys/unix/backtrace/mod.rs @@ -0,0 +1,132 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// Backtrace support built on libgcc with some extra OS-specific support +/// +/// Some methods of getting a backtrace: +/// +/// * The backtrace() functions on unix. It turns out this doesn't work very +/// well for green threads on OSX, and the address to symbol portion of it +/// suffers problems that are described below. +/// +/// * Using libunwind. This is more difficult than it sounds because libunwind +/// isn't installed everywhere by default. It's also a bit of a hefty library, +/// so possibly not the best option. When testing, libunwind was excellent at +/// getting both accurate backtraces and accurate symbols across platforms. +/// This route was not chosen in favor of the next option, however. +/// +/// * We're already using libgcc_s for exceptions in rust (triggering thread +/// unwinding and running destructors on the stack), and it turns out that it +/// conveniently comes with a function that also gives us a backtrace. All of +/// these functions look like _Unwind_*, but it's not quite the full +/// repertoire of the libunwind API. Due to it already being in use, this was +/// the chosen route of getting a backtrace. +/// +/// After choosing libgcc_s for backtraces, the sad part is that it will only +/// give us a stack trace of instruction pointers. Thankfully these instruction +/// pointers are accurate (they work for green and native threads), but it's +/// then up to us again to figure out how to translate these addresses to +/// symbols. As with before, we have a few options. Before, that, a little bit +/// of an interlude about symbols. This is my very limited knowledge about +/// symbol tables, and this information is likely slightly wrong, but the +/// general idea should be correct. +/// +/// When talking about symbols, it's helpful to know a few things about where +/// symbols are located. Some symbols are located in the dynamic symbol table +/// of the executable which in theory means that they're available for dynamic +/// linking and lookup. Other symbols end up only in the local symbol table of +/// the file. This loosely corresponds to pub and priv functions in Rust. +/// +/// Armed with this knowledge, we know that our solution for address to symbol +/// translation will need to consult both the local and dynamic symbol tables. +/// With that in mind, here's our options of translating an address to +/// a symbol. +/// +/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr() +/// behind the scenes to translate, and this is why backtrace() was not used. +/// Conveniently, this method works fantastically on OSX. It appears dladdr() +/// uses magic to consult the local symbol table, or we're putting everything +/// in the dynamic symbol table anyway. Regardless, for OSX, this is the +/// method used for translation. It's provided by the system and easy to do.o +/// +/// Sadly, all other systems have a dladdr() implementation that does not +/// consult the local symbol table. This means that most functions are blank +/// because they don't have symbols. This means that we need another solution. +/// +/// * Use unw_get_proc_name(). This is part of the libunwind api (not the +/// libgcc_s version of the libunwind api), but involves taking a dependency +/// to libunwind. We may pursue this route in the future if we bundle +/// libunwind, but libunwind was unwieldy enough that it was not chosen at +/// this time to provide this functionality. +/// +/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a +/// semi-reasonable solution. The stdlib already knows how to spawn processes, +/// so in theory it could invoke readelf, parse the output, and consult the +/// local/dynamic symbol tables from there. This ended up not getting chosen +/// due to the craziness of the idea plus the advent of the next option. +/// +/// * Use `libbacktrace`. It turns out that this is a small library bundled in +/// the gcc repository which provides backtrace and symbol translation +/// functionality. All we really need from it is the backtrace functionality, +/// and we only really need this on everything that's not OSX, so this is the +/// chosen route for now. +/// +/// In summary, the current situation uses libgcc_s to get a trace of stack +/// pointers, and we use dladdr() or libbacktrace to translate these addresses +/// to symbols. This is a bit of a hokey implementation as-is, but it works for +/// all unix platforms we support right now, so it at least gets the job done. + +#[cfg(target_os = "nacl")] +pub use sys::backtrace::none::write; + +#[cfg(not(any(all(target_os = "ios", target_arch = "arm"), target_os = "nacl")))] +pub use sys::backtrace::gcc_s::write; + +#[cfg(all(target_os = "ios", target_arch = "arm"))] +pub use sys::backtrace::libbacktrace::write; + +#[cfg(not(target_os = "nacl"))] use io; +#[cfg(not(target_os = "nacl"))] use io::prelude::*; +#[cfg(not(target_os = "nacl"))] use libc; +#[cfg(all(not(target_os = "nacl"), stage0))] use prelude::v1::*; +#[cfg(not(target_os = "nacl"))] use str; + +#[cfg(not(target_os = "nacl"))] use sys_common::backtrace::{demangle, HEX_WIDTH}; + +// symbol resolvers: +mod printer; + +// tracing impls: +#[cfg(not(any(all(target_os = "ios", target_arch = "arm"), target_os = "nacl")))] mod gcc_s; +#[cfg(all(target_os = "ios", target_arch = "arm"))] mod libbacktrace; +#[cfg(target_os = "nacl")] mod none; + +#[cfg(not(target_os = "nacl"))] +pub fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void, + s: Option<&[u8]>) -> io::Result<()> { + try!(write!(w, " {:2}: {:2$?} - ", idx, addr, HEX_WIDTH)); + match s.and_then(|s| str::from_utf8(s).ok()) { + Some(string) => try!(demangle(w, string)), + None => try!(write!(w, "")), + } + w.write_all(&['\n' as u8]) +} + +#[cfg(not(target_os = "nacl"))] +pub fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int, + more: bool) -> io::Result<()> { + let file = str::from_utf8(file).unwrap_or(""); + // prior line: " ##: {:2$} - func" + try!(write!(w, " {:3$}at {}:{}", "", file, line, HEX_WIDTH)); + if more { + try!(write!(w, " <... and possibly more>")); + } + w.write_all(&['\n' as u8]) +} diff --git a/src/libstd/sys/unix/backtrace/none.rs b/src/libstd/sys/unix/backtrace/none.rs new file mode 100644 index 0000000000000..b465b899410a9 --- /dev/null +++ b/src/libstd/sys/unix/backtrace/none.rs @@ -0,0 +1,22 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// No backtraces support. + +use io; +use io::prelude::*; +use result::Result::Err; + +#[inline(always)] +pub fn write(_w: &mut Write) -> io::Result<()> { + use io::ErrorKind; + Err(io::Error::new(ErrorKind::Other, + "no library is available to print stack backtraces")) +} diff --git a/src/libstd/sys/unix/backtrace/printer.rs b/src/libstd/sys/unix/backtrace/printer.rs new file mode 100644 index 0000000000000..794e3fac5a885 --- /dev/null +++ b/src/libstd/sys/unix/backtrace/printer.rs @@ -0,0 +1,249 @@ +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use self::dladdr::*; +pub use self::libbacktrace::*; + +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +mod dladdr { } +#[cfg(any(target_os = "macos", target_os = "ios"))] +mod dladdr { + pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void, + _symaddr: *mut libc::c_void) -> io::Result<()> { + use sys::backtrace::{output}; + use intrinsics; + #[repr(C)] + struct Dl_info { + dli_fname: *const libc::c_char, + dli_fbase: *mut libc::c_void, + dli_sname: *const libc::c_char, + dli_saddr: *mut libc::c_void, + } + extern { + fn dladdr(addr: *const libc::c_void, + info: *mut Dl_info) -> libc::c_int; + } + + let mut info: Dl_info = unsafe { intrinsics::init() }; + if unsafe { dladdr(addr, &mut info) == 0 } { + output(w, idx,addr, None) + } else { + output(w, idx, addr, Some(unsafe { + CStr::from_ptr(info.dli_sname).to_bytes() + })) + } + } +} + + +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "nacl"))] +mod libbacktrace { } +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "nacl")))] +mod libbacktrace { + use io; + use io::prelude::*; + use libc; + + #[cfg(stage0)] + use prelude::v1::*; + + use sys::backtrace::{output, output_fileline}; + pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void, + symaddr: *mut libc::c_void) -> io::Result<()> { + use env; + use ffi::CStr; + use os::unix::prelude::*; + use ptr; + + //////////////////////////////////////////////////////////////////////// + // libbacktrace.h API + //////////////////////////////////////////////////////////////////////// + type backtrace_syminfo_callback = + extern "C" fn(data: *mut libc::c_void, + pc: libc::uintptr_t, + symname: *const libc::c_char, + symval: libc::uintptr_t, + symsize: libc::uintptr_t); + type backtrace_full_callback = + extern "C" fn(data: *mut libc::c_void, + pc: libc::uintptr_t, + filename: *const libc::c_char, + lineno: libc::c_int, + function: *const libc::c_char) -> libc::c_int; + type backtrace_error_callback = + extern "C" fn(data: *mut libc::c_void, + msg: *const libc::c_char, + errnum: libc::c_int); + enum backtrace_state {} + #[link(name = "backtrace", kind = "static")] + #[cfg(not(test))] + extern {} + + extern { + fn backtrace_create_state(filename: *const libc::c_char, + threaded: libc::c_int, + error: backtrace_error_callback, + data: *mut libc::c_void) + -> *mut backtrace_state; + fn backtrace_syminfo(state: *mut backtrace_state, + addr: libc::uintptr_t, + cb: backtrace_syminfo_callback, + error: backtrace_error_callback, + data: *mut libc::c_void) -> libc::c_int; + fn backtrace_pcinfo(state: *mut backtrace_state, + addr: libc::uintptr_t, + cb: backtrace_full_callback, + error: backtrace_error_callback, + data: *mut libc::c_void) -> libc::c_int; + } + + //////////////////////////////////////////////////////////////////////// + // helper callbacks + //////////////////////////////////////////////////////////////////////// + + type FileLine = (*const libc::c_char, libc::c_int); + + extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char, + _errnum: libc::c_int) { + // do nothing for now + } + extern fn syminfo_cb(data: *mut libc::c_void, + _pc: libc::uintptr_t, + symname: *const libc::c_char, + _symval: libc::uintptr_t, + _symsize: libc::uintptr_t) { + let slot = data as *mut *const libc::c_char; + unsafe { *slot = symname; } + } + extern fn pcinfo_cb(data: *mut libc::c_void, + _pc: libc::uintptr_t, + filename: *const libc::c_char, + lineno: libc::c_int, + _function: *const libc::c_char) -> libc::c_int { + if !filename.is_null() { + let slot = data as *mut &mut [FileLine]; + let buffer = unsafe {ptr::read(slot)}; + + // if the buffer is not full, add file:line to the buffer + // and adjust the buffer for next possible calls to pcinfo_cb. + if !buffer.is_empty() { + buffer[0] = (filename, lineno); + unsafe { ptr::write(slot, &mut buffer[1..]); } + } + } + + 0 + } + + // The libbacktrace API supports creating a state, but it does not + // support destroying a state. I personally take this to mean that a + // state is meant to be created and then live forever. + // + // I would love to register an at_exit() handler which cleans up this + // state, but libbacktrace provides no way to do so. + // + // With these constraints, this function has a statically cached state + // that is calculated the first time this is requested. Remember that + // backtracing all happens serially (one global lock). + // + // An additionally oddity in this function is that we initialize the + // filename via self_exe_name() to pass to libbacktrace. It turns out + // that on Linux libbacktrace seamlessly gets the filename of the + // current executable, but this fails on freebsd. by always providing + // it, we make sure that libbacktrace never has a reason to not look up + // the symbols. The libbacktrace API also states that the filename must + // be in "permanent memory", so we copy it to a static and then use the + // static as the pointer. + // + // FIXME: We also call self_exe_name() on DragonFly BSD. I haven't + // tested if this is required or not. + unsafe fn init_state() -> *mut backtrace_state { + static mut STATE: *mut backtrace_state = 0 as *mut backtrace_state; + static mut LAST_FILENAME: [libc::c_char; 256] = [0; 256]; + if !STATE.is_null() { return STATE } + let selfname = if cfg!(target_os = "freebsd") || + cfg!(target_os = "dragonfly") || + cfg!(target_os = "bitrig") || + cfg!(target_os = "openbsd") { + env::current_exe().ok() + } else { + None + }; + let filename = match selfname { + Some(path) => { + let bytes = path.as_os_str().as_bytes(); + if bytes.len() < LAST_FILENAME.len() { + let i = bytes.iter(); + for (slot, val) in LAST_FILENAME.iter_mut().zip(i) { + *slot = *val as libc::c_char; + } + LAST_FILENAME.as_ptr() + } else { + ptr::null() + } + } + None => ptr::null(), + }; + STATE = backtrace_create_state(filename, 0, error_cb, + ptr::null_mut()); + return STATE + } + + //////////////////////////////////////////////////////////////////////// + // translation + //////////////////////////////////////////////////////////////////////// + + // backtrace errors are currently swept under the rug, only I/O + // errors are reported + let state = unsafe { init_state() }; + if state.is_null() { + return output(w, idx, addr, None) + } + let mut data = ptr::null(); + let data_addr = &mut data as *mut *const libc::c_char; + let ret = unsafe { + backtrace_syminfo(state, symaddr as libc::uintptr_t, + syminfo_cb, error_cb, + data_addr as *mut libc::c_void) + }; + if ret == 0 || data.is_null() { + try!(output(w, idx, addr, None)); + } else { + try!(output(w, idx, addr, Some(unsafe { CStr::from_ptr(data).to_bytes() }))); + } + + // pcinfo may return an arbitrary number of file:line pairs, + // in the order of stack trace (i.e. inlined calls first). + // in order to avoid allocation, we stack-allocate a fixed size of entries. + const FILELINE_SIZE: usize = 32; + let mut fileline_buf = [(ptr::null(), -1); FILELINE_SIZE]; + let ret; + let fileline_count; + { + let mut fileline_win: &mut [FileLine] = &mut fileline_buf; + let fileline_addr = &mut fileline_win as *mut &mut [FileLine]; + ret = unsafe { + backtrace_pcinfo(state, addr as libc::uintptr_t, + pcinfo_cb, error_cb, + fileline_addr as *mut libc::c_void) + }; + fileline_count = FILELINE_SIZE - fileline_win.len(); + } + if ret == 0 { + for (i, &(file, line)) in fileline_buf[..fileline_count].iter().enumerate() { + if file.is_null() { continue; } // just to be sure + let file = unsafe { CStr::from_ptr(file).to_bytes() }; + try!(output_fileline(w, file, line, i == FILELINE_SIZE - 1)); + } + } + + Ok(()) + } +} diff --git a/src/libstd/sys/unix/c.rs b/src/libstd/sys/unix/c.rs index eeecf7f50f79a..3ce1cc625b916 100644 --- a/src/libstd/sys/unix/c.rs +++ b/src/libstd/sys/unix/c.rs @@ -24,8 +24,7 @@ #![allow(dead_code)] #![allow(non_camel_case_types)] -pub use self::signal_os::{sigaction, siginfo, sigset_t, sigaltstack}; -pub use self::signal_os::{SA_ONSTACK, SA_SIGINFO, SIGBUS, SIGSTKSZ, SIG_SETMASK}; +pub use self::signal_os::*; use libc; @@ -78,6 +77,18 @@ pub struct passwd { pub pw_dir: *mut libc::c_char, pub pw_shell: *mut libc::c_char, } +#[repr(C)] +#[cfg(target_os = "nacl")] +pub struct passwd { + pub pw_name: *mut libc::c_char, + pub pw_passwd: *mut libc::c_char, + pub pw_uid: libc::uid_t, + pub pw_gid: libc::gid_t, + pub pw_comment: *mut libc::c_char, + pub pw_gecos: *mut libc::c_char, + pub pw_dir: *mut libc::c_char, + pub pw_shell: *mut libc::c_char, +} #[repr(C)] #[cfg(any(target_os = "macos", @@ -135,6 +146,7 @@ extern { act: *const sigaction, oldact: *mut sigaction) -> libc::c_int; + #[cfg(not(target_os = "nacl"))] pub fn sigaltstack(ss: *const sigaltstack, oss: *mut sigaltstack) -> libc::c_int; @@ -319,6 +331,28 @@ mod signal_os { } } +/// Note: Although the signal functions are defined on NaCl, they always fail. +#[cfg(target_os = "nacl")] +mod signal_os { + use libc; + + pub static SA_NOCLDSTOP: libc::c_ulong = 1; + pub static SA_SIGINFO: libc::c_ulong = 2; + #[repr(C)] + pub struct siginfo { + pub si_signo: libc::c_int, + pub si_code: libc::c_int, + pub si_val: usize, + } + pub type sigset_t = libc::c_ulong; + #[repr(C)] + pub struct sigaction { + pub sa_flags: libc::c_int, + pub sa_mask: sigset_t, + pub handler: extern fn(libc::c_int), + } +} + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs index bdbe120f79d0d..c8f4b4a200317 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -14,7 +14,6 @@ use core::prelude::v1::*; use io; use libc::{self, c_int, size_t, c_void}; use mem; -use sys::c; use sys::cvt; use sys_common::AsInner; @@ -54,12 +53,16 @@ impl FileDesc { Ok(ret as usize) } + #[cfg(not(target_os = "nacl"))] pub fn set_cloexec(&self) { + use sys::c; unsafe { let ret = c::ioctl(self.fd, c::FIOCLEX); debug_assert_eq!(ret, 0); } } + #[cfg(target_os = "nacl")] + pub fn set_cloexec(&self) { } } impl AsInner for FileDesc { diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index ddab24b133fb7..251a78f423374 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -463,9 +463,18 @@ pub fn rmdir(p: &Path) -> io::Result<()> { } pub fn readlink(p: &Path) -> io::Result { + #[cfg(not(target_env = "newlib"))] + unsafe fn pathconf(p: *mut libc::c_char) -> i64 { + libc::pathconf(p, libc::_PC_NAME_MAX) as i64 + } + #[cfg(target_env = "newlib")] + unsafe fn pathconf(_: *mut libc::c_char) -> i64 { + libc::sysconf(libc::_PC_NAME_MAX) as i64 + } + let c_path = try!(cstr(p)); let p = c_path.as_ptr(); - let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) }; + let mut len = unsafe { pathconf(p as *mut _) }; if len < 0 { len = 1024; // FIXME: read PATH_MAX from C ffi? } diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index 2b6b50a1a56d7..6bb236218ad8f 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -60,6 +60,15 @@ pub fn errno() -> i32 { extern { fn __errno_location() -> *const c_int; } __errno_location() } + #[cfg(target_env = "newlib")] + fn errno_location() -> *const c_int { + extern { + fn __errno() -> *const c_int; + } + unsafe { + __errno() + } + } unsafe { (*errno_location()) as i32 @@ -68,13 +77,13 @@ pub fn errno() -> i32 { /// Gets a detailed string description for the given error number. pub fn error_string(errno: i32) -> String { - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "nacl"))] extern { #[link_name = "__xpg_strerror_r"] fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int; } - #[cfg(not(target_os = "linux"))] + #[cfg(not(any(target_os = "linux", target_os = "nacl")))] extern { fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int; @@ -233,7 +242,7 @@ pub fn current_exe() -> io::Result { } } -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "nacl"))] pub fn current_exe() -> io::Result { ::fs::read_link("/proc/self/exe") } @@ -354,7 +363,8 @@ pub fn args() -> Args { target_os = "dragonfly", target_os = "bitrig", target_os = "netbsd", - target_os = "openbsd"))] + target_os = "openbsd", + target_os = "nacl"))] pub fn args() -> Args { use rt; let bytes = rt::args::clone().unwrap_or(Vec::new()); @@ -466,10 +476,12 @@ pub fn home_dir() -> Option { }).map(PathBuf::from); #[cfg(any(target_os = "android", - target_os = "ios"))] + target_os = "ios", + target_os = "nacl"))] unsafe fn fallback() -> Option { None } #[cfg(not(any(target_os = "android", - target_os = "ios")))] + target_os = "ios", + target_os = "nacl")))] unsafe fn fallback() -> Option { let amt = match libc::sysconf(c::_SC_GETPW_R_SIZE_MAX) { n if n < 0 => 512 as usize, diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index 2a365cff6cb94..1419d6bddab2c 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -17,7 +17,6 @@ use ffi::{OsString, OsStr, CString, CStr}; use fmt; use io::{self, Error, ErrorKind}; use libc::{self, pid_t, c_void, c_int, gid_t, uid_t}; -use mem; use ptr; use sys::fd::FileDesc; use sys::fs::{File, OpenOptions}; @@ -315,21 +314,31 @@ impl Process { *sys::os::environ() = envp as *const _; } - // Reset signal handling so the child process starts in a - // standardized state. libstd ignores SIGPIPE, and signal-handling - // libraries often set a mask. Child processes inherit ignored - // signals and the signal mask from their parent, but most - // UNIX programs do not reset these things on their own, so we - // need to clean things up now to avoid confusing the program - // we're about to run. - let mut set: c::sigset_t = mem::uninitialized(); - if c::sigemptyset(&mut set) != 0 || - c::pthread_sigmask(c::SIG_SETMASK, &set, ptr::null_mut()) != 0 || - libc::funcs::posix01::signal::signal( - libc::SIGPIPE, mem::transmute(c::SIG_DFL) - ) == mem::transmute(c::SIG_ERR) { - fail(&mut output); + #[cfg(not(target_os = "nacl"))] + unsafe fn reset_signal_handling(output: &mut AnonPipe) { + use mem; + // Reset signal handling so the child process starts in a + // standardized state. libstd ignores SIGPIPE, and signal-handling + // libraries often set a mask. Child processes inherit ignored + // signals and the signal mask from their parent, but most + // UNIX programs do not reset these things on their own, so we + // need to clean things up now to avoid confusing the program + // we're about to run. + let mut set: c::sigset_t = mem::uninitialized(); + if c::sigemptyset(&mut set) != 0 || + c::pthread_sigmask(c::SIG_SETMASK, &set, ptr::null_mut()) != 0 || + libc::funcs::posix01::signal::signal( + libc::SIGPIPE, mem::transmute(c::SIG_DFL) + ) == mem::transmute(c::SIG_ERR) + { + fail(output); + } + } + #[cfg(target_os = "nacl")] + unsafe fn reset_signal_handling(_output: &mut AnonPipe) { + // NaCl has no signal support. } + reset_signal_handling(&mut output); let _ = libc::execvp(*argv, argv); fail(&mut output) @@ -411,7 +420,8 @@ fn make_envp(env: Option<&HashMap>) fn translate_status(status: c_int) -> ExitStatus { #![allow(non_snake_case)] - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "nacl"))] mod imp { pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 } pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff } diff --git a/src/libstd/sys/unix/sync.rs b/src/libstd/sys/unix/sync.rs index 9c8a1f4ca40ec..fa335d939c59a 100644 --- a/src/libstd/sys/unix/sync.rs +++ b/src/libstd/sys/unix/sync.rs @@ -34,6 +34,8 @@ extern { // cvars pub fn pthread_cond_wait(cond: *mut pthread_cond_t, lock: *mut pthread_mutex_t) -> libc::c_int; + + #[cfg_attr(target_os = "nacl", link_name = "pthread_cond_timedwait_abs")] pub fn pthread_cond_timedwait(cond: *mut pthread_cond_t, lock: *mut pthread_mutex_t, abstime: *const libc::timespec) -> libc::c_int; @@ -247,3 +249,53 @@ mod os { }; pub const PTHREAD_MUTEX_RECURSIVE: libc::c_int = 1; } +#[cfg(target_os = "nacl")] +mod os { + use libc; + + pub type __nc_basic_thread_data = libc::c_void; + + #[repr(C)] + pub struct pthread_mutex_t { + mutex_state: libc::c_int, + mutex_type: libc::c_int, + owner_thread_id: *mut __nc_basic_thread_data, + recursion_counter: libc::uint32_t, + _unused: libc::c_int, + } + #[repr(C)] + pub struct pthread_mutexattr_t { + kind: libc::c_int, + } + #[repr(C)] + pub struct pthread_cond_t { + sequence_number: libc::c_int, + _unused: libc::c_int, + } + #[repr(C)] + pub struct pthread_rwlock_t { + readers: libc::c_int, + writers: libc::c_int, + } + + const NC_INVALID_HANDLE: libc::c_int = -1; + const NACL_PTHREAD_ILLEGAL_THREAD_ID: *mut __nc_basic_thread_data + = 0 as *mut __nc_basic_thread_data; + + pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { + mutex_state: 0, + mutex_type: 1, + owner_thread_id: NACL_PTHREAD_ILLEGAL_THREAD_ID, + recursion_counter: 0, + _unused: NC_INVALID_HANDLE, + }; + pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t { + sequence_number: 0, + _unused: NC_INVALID_HANDLE, + }; + pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t { + readers: 0, + writers: 0, + }; + pub const PTHREAD_MUTEX_RECURSIVE: libc::c_int = 1; +} diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index 67ecd4d922980..ad07bd82fd266 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -14,6 +14,7 @@ use prelude::v1::*; use alloc::boxed::FnBox; use cmp; +#[cfg(not(target_os = "nacl"))] use ffi::CString; use io; use libc::consts::os::posix01::PTHREAD_STACK_MIN; @@ -128,6 +129,10 @@ impl Thread { pthread_setname_np(cname.as_ptr()); } } + #[cfg(target_env = "newlib")] + pub unsafe fn set_name(_name: &str) { + // Newlib has no way to set a thread name. + } pub fn sleep(dur: Duration) { let mut ts = libc::timespec { @@ -203,7 +208,7 @@ pub mod guard { current().map(|s| s as *mut libc::c_void) } - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", target_os = "nacl"))] unsafe fn get_stack_start() -> Option<*mut libc::c_void> { use super::pthread_attr_init; diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs index db0d0f1506139..b9266fc1e79eb 100644 --- a/src/libstd/sys/unix/time.rs +++ b/src/libstd/sys/unix/time.rs @@ -77,12 +77,13 @@ mod inner { // Apparently android provides this in some other library? // Bitrig's RT extensions are in the C library, not a separate librt - // OpenBSD provide it via libc + // OpenBSD and NaCl provide it via libc #[cfg(not(any(target_os = "android", target_os = "bitrig", target_os = "netbsd", target_os = "openbsd", - target_env = "musl")))] + target_env = "musl", + target_os = "nacl")))] #[link(name = "rt")] extern {} diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index 0615033736e90..cdb07fb1b8455 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -110,14 +110,16 @@ macro_rules! thread_local { (static $name:ident: $t:ty = $init:expr) => ( static $name: $crate::thread::LocalKey<$t> = __thread_local_inner!($t, $init, - #[cfg_attr(all(any(target_os = "macos", target_os = "linux"), + #[cfg_attr(all(any(target_os = "macos", target_os = "linux", + target_os = "nacl"), not(target_arch = "aarch64")), thread_local)]); ); (pub static $name:ident: $t:ty = $init:expr) => ( pub static $name: $crate::thread::LocalKey<$t> = __thread_local_inner!($t, $init, - #[cfg_attr(all(any(target_os = "macos", target_os = "linux"), + #[cfg_attr(all(any(target_os = "macos", target_os = "linux", + target_os = "nacl"), not(target_arch = "aarch64")), thread_local)]); ); @@ -267,7 +269,8 @@ impl LocalKey { } } -#[cfg(all(any(target_os = "macos", target_os = "linux"), +#[cfg(all(any(target_os = "macos", target_os = "linux", + target_os = "nacl"), not(target_arch = "aarch64"), not(no_elf_tls)))] #[doc(hidden)] @@ -327,7 +330,7 @@ mod imp { // fallback implementation to use as well. // // Due to rust-lang/rust#18804, make sure this is not generic! - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "nacl"))] unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { use prelude::v1::*; use mem; @@ -418,7 +421,8 @@ mod imp { } } -#[cfg(any(not(any(target_os = "macos", target_os = "linux")), +#[cfg(any(not(any(target_os = "macos", target_os = "linux", + target_os = "nacl")), target_arch = "aarch64", no_elf_tls))] #[doc(hidden)] diff --git a/src/libsyntax/abi.rs b/src/libsyntax/abi.rs index 50c86a80b4ab5..fe9c18144848e 100644 --- a/src/libsyntax/abi.rs +++ b/src/libsyntax/abi.rs @@ -27,6 +27,7 @@ pub enum Os { OsBitrig, OsNetbsd, OsOpenbsd, + OsNaCl, } #[derive(PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Clone, Copy, Debug)] @@ -56,7 +57,8 @@ pub enum Architecture { X86_64, Arm, Mips, - Mipsel + Mipsel, + Le32, } #[derive(Copy, Clone)] @@ -141,6 +143,7 @@ impl fmt::Display for Os { OsBitrig => "bitrig".fmt(f), OsNetbsd => "netbsd".fmt(f), OsOpenbsd => "openbsd".fmt(f), + OsNaCl => "nacl".fmt(f), } } } diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 7777ea51f822e..06ae3a3adc778 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -1085,13 +1085,15 @@ impl MetricMap { /// elimination. /// /// This function is a no-op, and does not even read from `dummy`. +#[cfg(not(target_os = "nacl"))] pub fn black_box(dummy: T) -> T { // we need to "use" the argument in some way LLVM can't // introspect. unsafe {asm!("" : : "r"(&dummy))} dummy } - +#[inline(never)] #[cfg(target_os = "nacl")] +pub fn black_box(dummy: T) -> T { dummy } impl Bencher { /// Callback for benchmark functions to run in their body. diff --git a/src/rt/rust_builtin.c b/src/rt/rust_builtin.c index 9a5eaad42433c..b4b6a23a6c626 100644 --- a/src/rt/rust_builtin.c +++ b/src/rt/rust_builtin.c @@ -25,6 +25,10 @@ #include #include +#if _POSIX_C_SOURCE < 1 +#include +#endif + #ifdef __APPLE__ #include #include @@ -67,7 +71,25 @@ rust_opendir(char *dirname) { int rust_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) { +#if _POSIX_C_SOURCE < 1 + /// This is needed for Newlib. + if(result == NULL || entry == NULL || dirp == NULL) { + errno = EBADF; + return EBADF; + } + + errno = 0; + struct dirent* next_entry = readdir(dirp); + if(next_entry == NULL) { + *result = NULL; + } else { + memcpy(entry, next_entry, sizeof(struct dirent)); + *result = next_entry; + } + return 0; +#else return readdir_r(dirp, entry, result); +#endif } int @@ -448,7 +470,6 @@ const char * rust_current_exe() { #endif #endif // !defined(_WIN32) - // // Local Variables: // mode: C++ diff --git a/src/rustllvm/ArchiveWrapper.cpp b/src/rustllvm/ArchiveWrapper.cpp index a40f0a245d17e..dceb6bd9ec73f 100644 --- a/src/rustllvm/ArchiveWrapper.cpp +++ b/src/rustllvm/ArchiveWrapper.cpp @@ -12,7 +12,7 @@ #include "llvm/Object/Archive.h" -#if LLVM_VERSION_MINOR >= 7 +#if LLVM_VERSION_MINOR >= 7 && !PNACL_LLVM #include "llvm/Object/ArchiveWriter.h" #endif @@ -36,6 +36,11 @@ typedef Archive RustArchive; #define GET_ARCHIVE(a) (a) #endif +extern "C" bool +LLVMRustUseArchiveWriter() { + return LLVM_VERSION_MINOR >= 7 && !PNACL_LLVM; +} + extern "C" void* LLVMRustOpenArchive(char *path) { ErrorOr> buf_or = MemoryBuffer::getFile(path, @@ -156,7 +161,7 @@ LLVMRustWriteArchive(char *Dst, const LLVMRustArchiveMember **NewMembers, bool WriteSymbtab, Archive::Kind Kind) { -#if LLVM_VERSION_MINOR >= 7 +#if LLVM_VERSION_MINOR >= 7 && !PNACL_LLVM std::vector Members; for (size_t i = 0; i < NumMembers; i++) { diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 6513fdfd2f2c1..f1bd546efd872 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -12,6 +12,7 @@ #include "rustllvm.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" #include "llvm/Support/CBindingWrapping.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" @@ -103,6 +104,7 @@ LLVMRustCreateTargetMachine(const char *triple, if (UseSoftFloat) { Options.FloatABIType = FloatABI::Soft; } + Options.MCOptions = InitMCTargetOptionsFromFlags(); Options.DataSections = DataSections; Options.FunctionSections = FunctionSections; @@ -138,7 +140,9 @@ LLVMRustAddAnalysisPasses(LLVMTargetMachineRef TM, #else PM->add(new DataLayoutPass(unwrap(M))); #endif - unwrap(TM)->addAnalysisPasses(*PM); + if(TM != NULL) { + unwrap(TM)->addAnalysisPasses(*PM); + } #endif } diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 5007af0e777b8..eda252c4a1e0e 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -238,9 +238,29 @@ inline Metadata **unwrap(LLVMMetadataRef *Vals) { typedef LLVMValueRef LLVMMetadataRef; #endif +// Allows us to override return types for select types: +template ::value> struct UnwrapDI; +template +struct UnwrapDI { + static DIT unwrap(LLVMMetadataRef ref) { + MDNode* node = nullptr; + if(ref != nullptr) { node = llvm::unwrap(ref); } + + return cast_or_null::type>(node); + } +}; +template +struct UnwrapDI { + typedef typename std::remove_reference::type* NodePtrTy; + static DIT unwrap(LLVMMetadataRef ref) { + NodePtrTy node = UnwrapDI::unwrap(ref); + return DIT(node); + } +}; + template DIT* unwrapDIptr(LLVMMetadataRef ref) { - return (DIT*) (ref ? unwrap(ref) : NULL); + return UnwrapDI::unwrap(ref); } #if LLVM_VERSION_MINOR <= 6 @@ -250,8 +270,42 @@ DIT unwrapDI(LLVMMetadataRef ref) { } #else #define DIDescriptor DIScope -#define DIArray DINodeArray -#define unwrapDI unwrapDIptr + +# if LLVM_VERSION_MINOR >= 7 + +# define SPECIALIZE_UNWRAP(type, to) template <> \ + struct UnwrapDI::value> { \ + static to unwrap(LLVMMetadataRef ref) { \ + return UnwrapDI::unwrap(ref); \ + } \ + } + +# if PNACL_LLVM +// The NaCl SDK and Rust use slightly different LLVM 3.7 versions. +// In the small space between these two revisions, LLVM has managed to modify +// just about every DIBuilder function. + +SPECIALIZE_UNWRAP(DICompositeType, MDCompositeType*); + +# else + +SPECIALIZE_UNWRAP(DIFile, DIFile*); +SPECIALIZE_UNWRAP(DIScope, DIScope*); +SPECIALIZE_UNWRAP(DISubroutineType, DISubroutineType*); +SPECIALIZE_UNWRAP(DIType, DIType*); +SPECIALIZE_UNWRAP(DICompositeType, DICompositeType*); + +# define DIArray DINodeArray + +# endif + +# endif + + template + auto unwrapDI(LLVMMetadataRef ref) -> decltype(UnwrapDI::unwrap(ref)) { + return UnwrapDI::unwrap(ref); + } + #endif #if LLVM_VERSION_MINOR <= 5 @@ -321,9 +375,9 @@ extern "C" LLVMMetadataRef LLVMDIBuilderCreateSubroutineType( LLVMMetadataRef ParameterTypes) { return wrap(Builder->createSubroutineType( unwrapDI(File), -#if LLVM_VERSION_MINOR >= 7 +#if LLVM_VERSION_MINOR >= 7 && !PNACL_LLVM DITypeRefArray(unwrap(ParameterTypes)))); -#elif LLVM_VERSION_MINOR >= 6 +#elif LLVM_VERSION_MINOR >= 6 || PNACL_LLVM unwrapDI(ParameterTypes))); #else unwrapDI(ParameterTypes))); @@ -400,11 +454,7 @@ extern "C" LLVMMetadataRef LLVMDIBuilderCreateStructType( AlignInBits, Flags, unwrapDI(DerivedFrom), -#if LLVM_VERSION_MINOR >= 7 - DINodeArray(unwrapDI(Elements)), -#else unwrapDI(Elements), -#endif RunTimeLang, unwrapDI(VTableHolder), UniqueId @@ -517,11 +567,7 @@ extern "C" LLVMMetadataRef LLVMDIBuilderCreateArrayType( LLVMMetadataRef Subscripts) { return wrap(Builder->createArrayType(Size, AlignInBits, unwrapDI(Ty), -#if LLVM_VERSION_MINOR >= 7 - DINodeArray(unwrapDI(Subscripts)) -#else unwrapDI(Subscripts) -#endif )); } @@ -533,11 +579,7 @@ extern "C" LLVMMetadataRef LLVMDIBuilderCreateVectorType( LLVMMetadataRef Subscripts) { return wrap(Builder->createVectorType(Size, AlignInBits, unwrapDI(Ty), -#if LLVM_VERSION_MINOR >= 7 - DINodeArray(unwrapDI(Subscripts)) -#else unwrapDI(Subscripts) -#endif )); } @@ -576,7 +618,7 @@ extern "C" LLVMValueRef LLVMDIBuilderInsertDeclareAtEnd( LLVMBasicBlockRef InsertAtEnd) { return wrap(Builder->insertDeclare( unwrap(Val), -#if LLVM_VERSION_MINOR >= 7 +#if LLVM_VERSION_MINOR >= 7 && !PNACL_LLVM unwrap(VarInfo), #else unwrapDI(VarInfo), @@ -603,7 +645,7 @@ extern "C" LLVMValueRef LLVMDIBuilderInsertDeclareBefore( #endif return wrap(Builder->insertDeclare( unwrap(Val), -#if LLVM_VERSION_MINOR >= 7 +#if LLVM_VERSION_MINOR >= 7 && !PNACL_LLVM unwrap(VarInfo), #else unwrapDI(VarInfo), @@ -644,11 +686,7 @@ extern "C" LLVMMetadataRef LLVMDIBuilderCreateEnumerationType( LineNumber, SizeInBits, AlignInBits, -#if LLVM_VERSION_MINOR >= 7 - DINodeArray(unwrapDI(Elements)), -#else unwrapDI(Elements), -#endif unwrapDI(ClassType))); } @@ -673,11 +711,7 @@ extern "C" LLVMMetadataRef LLVMDIBuilderCreateUnionType( SizeInBits, AlignInBits, Flags, -#if LLVM_VERSION_MINOR >= 7 - DINodeArray(unwrapDI(Elements)), -#else unwrapDI(Elements), -#endif RunTimeLang, UniqueId )); @@ -735,8 +769,8 @@ extern "C" void LLVMDICompositeTypeSetTypeArray( LLVMMetadataRef TypeArray) { #if LLVM_VERSION_MINOR >= 7 - DICompositeType *tmp = unwrapDI(CompositeType); - Builder->replaceArrays(tmp, DINodeArray(unwrap(TypeArray))); + auto *tmp = unwrapDI(CompositeType); + Builder->replaceArrays(tmp, unwrapDI(TypeArray)); #elif LLVM_VERSION_MINOR >= 6 DICompositeType tmp = unwrapDI(CompositeType); Builder->replaceArrays(tmp, unwrapDI(TypeArray)); @@ -784,12 +818,25 @@ extern "C" void LLVMWriteValueToString(LLVMValueRef Value, RustStringRef str) { os << ")"; } +static bool MaterializeModule(Module* M) { + // LinkModules overrides the module materializer, orphaning any globalvalues + // still unmaterialized. This is here to force the loading of any lazy + // bitcode parsing. + const auto ec = M->materializeAllPermanently(); + if(ec) { + LLVMRustSetLastError(ec.message().c_str()); + return false; + } else { + return true; + } +} + extern "C" bool LLVMRustLinkInExternalBitcode(LLVMModuleRef dst, char *bc, size_t len) { Module *Dst = unwrap(dst); #if LLVM_VERSION_MINOR >= 6 std::unique_ptr buf = MemoryBuffer::getMemBufferCopy(StringRef(bc, len)); -#if LLVM_VERSION_MINOR >= 7 +#if LLVM_VERSION_MINOR >= 7 && !PNACL_LLVM ErrorOr> Src = llvm::getLazyBitcodeModule(std::move(buf), Dst->getContext()); #else @@ -807,12 +854,16 @@ LLVMRustLinkInExternalBitcode(LLVMModuleRef dst, char *bc, size_t len) { return false; } + if(!MaterializeModule(Dst)) { + return false; + } + std::string Err; #if LLVM_VERSION_MINOR >= 6 raw_string_ostream Stream(Err); DiagnosticPrinterRawOStream DP(Stream); -#if LLVM_VERSION_MINOR >= 7 +#if LLVM_VERSION_MINOR >= 7 && !PNACL_LLVM if (Linker::LinkModules(Dst, Src->get(), [&](const DiagnosticInfo &DI) { DI.print(DP); })) { #else if (Linker::LinkModules(Dst, *Src, [&](const DiagnosticInfo &DI) { DI.print(DP); })) { @@ -951,7 +1002,7 @@ LLVMRustBuildLandingPad(LLVMBuilderRef Builder, unsigned NumClauses, const char* Name, LLVMValueRef F) { -#if LLVM_VERSION_MINOR >= 7 +#if LLVM_VERSION_MINOR >= 7 && !PNACL_LLVM unwrap(F)->setPersonalityFn(unwrap(PersFn)); return LLVMBuildLandingPad(Builder, Ty, NumClauses, Name); #else diff --git a/src/rustllvm/rustllvm.h b/src/rustllvm/rustllvm.h index 2a47e8b089549..aa7582bf667b2 100644 --- a/src/rustllvm/rustllvm.h +++ b/src/rustllvm/rustllvm.h @@ -56,6 +56,10 @@ #include "llvm/IR/DIBuilder.h" #include "llvm/Linker/Linker.h" +#ifndef PNACL_LLVM +#define PNACL_LLVM 0 +#endif + void LLVMRustSetLastError(const char*); typedef struct OpaqueRustString *RustStringRef; diff --git a/src/test/run-make/tools.mk b/src/test/run-make/tools.mk index 223296286bd89..eb1aec22152da 100644 --- a/src/test/run-make/tools.mk +++ b/src/test/run-make/tools.mk @@ -6,7 +6,7 @@ TARGET_RPATH_ENV = \ $(LD_LIB_PATH_ENVVAR)="$(TMPDIR):$(TARGET_RPATH_DIR):$($(LD_LIB_PATH_ENVVAR))" BARE_RUSTC := $(HOST_RPATH_ENV) $(RUSTC) -RUSTC := $(BARE_RUSTC) --out-dir $(TMPDIR) -L $(TMPDIR) +RUSTC := $(BARE_RUSTC) --out-dir $(TMPDIR) -L $(TMPDIR) $(RUSTFLAGS) CC := $(CC) -L $(TMPDIR) HTMLDOCCK := $(PYTHON) $(S)/src/etc/htmldocck.py diff --git a/src/test/run-pass/asm-concat-src.rs b/src/test/run-pass/asm-concat-src.rs index 716c3d47a0365..4a9d802e32173 100644 --- a/src/test/run-pass/asm-concat-src.rs +++ b/src/test/run-pass/asm-concat-src.rs @@ -9,6 +9,7 @@ // except according to those terms. // pretty-expanded FIXME #23616 +// ignore-pnacl #![feature(asm)] diff --git a/src/test/run-pass/atomic-print.rs b/src/test/run-pass/atomic-print.rs index ae0a358ac4e3f..0859d2948edf1 100644 --- a/src/test/run-pass/atomic-print.rs +++ b/src/test/run-pass/atomic-print.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-pnacl no child processes allowed + use std::{env, fmt, process, sync, thread}; struct SlowFmt(u32); diff --git a/src/test/run-pass/backtrace-debuginfo.rs b/src/test/run-pass/backtrace-debuginfo.rs index 8f41c68b0473f..aebc3479e271e 100644 --- a/src/test/run-pass/backtrace-debuginfo.rs +++ b/src/test/run-pass/backtrace-debuginfo.rs @@ -10,6 +10,7 @@ // compile-flags:-g // ignore-pretty as this critically relies on line numbers +// ignore-pnacl no child processes allowed use std::io; use std::io::prelude::*; diff --git a/src/test/run-pass/backtrace.rs b/src/test/run-pass/backtrace.rs index 3f4849dbcb2b1..f24c612c7a3ec 100644 --- a/src/test/run-pass/backtrace.rs +++ b/src/test/run-pass/backtrace.rs @@ -11,6 +11,7 @@ // no-pretty-expanded FIXME #15189 // ignore-windows FIXME #13259 // ignore-android FIXME #17520 +// ignore-pnacl no child processes allowed use std::env; use std::process::{Command, Stdio}; diff --git a/src/test/run-pass/bitwise.rs b/src/test/run-pass/bitwise.rs index a9f19c12b0278..921c2e157a365 100644 --- a/src/test/run-pass/bitwise.rs +++ b/src/test/run-pass/bitwise.rs @@ -10,7 +10,7 @@ #![feature(negate_unsigned)] -#[cfg(any(target_arch = "x86", target_arch = "arm"))] +#[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "le32"))] fn target() { assert_eq!(-1000 as usize >> 3_usize, 536870787_usize); } diff --git a/src/test/run-pass/issue-12133-3.rs b/src/test/run-pass/issue-12133-3.rs index 66201ff901f30..1f9b8d750ee03 100644 --- a/src/test/run-pass/issue-12133-3.rs +++ b/src/test/run-pass/issue-12133-3.rs @@ -12,6 +12,7 @@ // aux-build:issue-12133-dylib.rs // aux-build:issue-12133-dylib2.rs // ignore-musl +// ignore-pnacl // pretty-expanded FIXME #23616 diff --git a/src/test/run-pass/tcp-stress.rs b/src/test/run-pass/tcp-stress.rs index dca65f03f690a..f8497a08c2732 100644 --- a/src/test/run-pass/tcp-stress.rs +++ b/src/test/run-pass/tcp-stress.rs @@ -12,6 +12,7 @@ // ignore-bitrig system ulimit (Too many open files) // ignore-netbsd system ulimit (Too many open files) // ignore-openbsd system ulimit (Too many open files) +// ignore-pnacl sandboxed use std::io::prelude::*; use std::net::{TcpListener, TcpStream}; diff --git a/src/test/run-pass/wait-forked-but-failed-child.rs b/src/test/run-pass/wait-forked-but-failed-child.rs index 1d0004bafa356..9b3fc4d326eed 100644 --- a/src/test/run-pass/wait-forked-but-failed-child.rs +++ b/src/test/run-pass/wait-forked-but-failed-child.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-pnacl no child spawning #![feature(libc)]