From 041d027fca3c2da53f6258f70d0c424c4ded9ab5 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Sat, 23 Nov 2019 12:30:33 -0800 Subject: [PATCH] Support whole-program LTO Build and install an LTO version of libc.a and the startup files, alongside the non-LTO versions. This also adds support for the proposed `__main_argc_argv` convention, supporting compilers both with and without that change. --- Makefile | 546 +++++++++++--------- basics/crt/crt1.c | 7 +- expected/wasm32-wasi/defined-symbols.txt | 2 + libc-bottom-half/crt/crt1.c | 8 +- libc-bottom-half/sources/__main_argc_argv.c | 15 + libc-bottom-half/sources/__main_void.c | 55 ++ libc-bottom-half/sources/__original_main.c | 52 +- 7 files changed, 378 insertions(+), 307 deletions(-) create mode 100644 libc-bottom-half/sources/__main_argc_argv.c create mode 100644 libc-bottom-half/sources/__main_void.c diff --git a/Makefile b/Makefile index 4fa5fcb96..d5ad32da4 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -# These variables are specifically meant to be overridable via -# the make command-line. +# These variables are specifically meant to be overridable via the make +# command-line. WASM_CC ?= clang WASM_NM ?= $(patsubst %clang,%llvm-nm,$(WASM_CC)) WASM_AR ?= $(patsubst %clang,%llvm-ar,$(WASM_CC)) @@ -9,13 +9,17 @@ SYSROOT ?= $(CURDIR)/sysroot # A directory to install to for "make install". INSTALL_DIR ?= /usr/local # single or posix -THREAD_MODEL = single +THREAD_MODEL ?= single # yes or no -BUILD_DLMALLOC = yes -BUILD_LIBC_BOTTOM_HALF = yes -BUILD_LIBC_TOP_HALF = yes +BUILD_DLMALLOC ?= yes +BUILD_LIBC_BOTTOM_HALF ?= yes +BUILD_LIBC_TOP_HALF ?= yes # The directory where we're store intermediate artifacts. -OBJDIR = $(CURDIR)/build +BUILD_DIR ?= $(CURDIR)/build + +ifeq ($(CLANG_VERSION),) +$(error CLANG_VERSION should be set to the clang version string) +endif # Check dependencies. ifeq ($(BUILD_LIBC_TOP_HALF),yes) @@ -29,103 +33,80 @@ $(error BUILD_LIBC_BOTTOM_HALF=yes depends on BUILD_DLMALLOC=yes) endif endif -# These variables describe the locations of various files and -# directories in the source tree. -BASICS_DIR = $(CURDIR)/basics -BASICS_INC = $(BASICS_DIR)/include -BASICS_CRT_SOURCES = $(wildcard $(BASICS_DIR)/crt/*.c) -BASICS_SOURCES = \ - $(wildcard $(BASICS_DIR)/sources/*.c) \ - $(wildcard $(BASICS_DIR)/sources/math/*.c) -DLMALLOC_DIR = $(CURDIR)/dlmalloc -DLMALLOC_SRC_DIR = $(DLMALLOC_DIR)/src -DLMALLOC_SOURCES = $(DLMALLOC_SRC_DIR)/dlmalloc.c -DLMALLOC_INC = $(DLMALLOC_DIR)/include -LIBC_BOTTOM_HALF_DIR = $(CURDIR)/libc-bottom-half -LIBC_BOTTOM_HALF_CLOUDLIBC_SRC = $(LIBC_BOTTOM_HALF_DIR)/cloudlibc/src -LIBC_BOTTOM_HALF_CLOUDLIBC_SRC_INC = $(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC)/include -LIBC_BOTTOM_HALF_HEADERS_PUBLIC = $(LIBC_BOTTOM_HALF_DIR)/headers/public -LIBC_BOTTOM_HALF_HEADERS_PRIVATE = $(LIBC_BOTTOM_HALF_DIR)/headers/private -LIBC_BOTTOM_HALF_LIBPREOPEN_DIR = $(LIBC_BOTTOM_HALF_DIR)/libpreopen -LIBC_BOTTOM_HALF_SOURCES = $(LIBC_BOTTOM_HALF_DIR)/sources -LIBC_BOTTOM_HALF_ALL_SOURCES = \ +# Set the target variables. Multiarch triples notably omit the vendor field, +# which happens to be what we do for the main target triple too. +override TARGET_TRIPLE = wasm32-wasi +override MULTIARCH_TRIPLE = wasm32-wasi + +# The directory we built .o files in. +override OBJDIR := $(BUILD_DIR) +ifeq ($(LTO),yes) +override OBJDIR := $(OBJDIR)/llvm-lto/$(CLANG_VERSION) +endif + +# These variables describe the locations of various files and directories in +# the source tree. +override BASICS_DIR = $(CURDIR)/basics +override BASICS_INC = $(BASICS_DIR)/include +override BASICS_CRT_SOURCES = $(wildcard $(BASICS_DIR)/crt/*.c) +override BASICS_SOURCES = $(wildcard $(BASICS_DIR)/sources/*.c) +override DLMALLOC_DIR = $(CURDIR)/dlmalloc +override DLMALLOC_SRC_DIR = $(DLMALLOC_DIR)/src +override DLMALLOC_SOURCES = $(DLMALLOC_SRC_DIR)/dlmalloc.c +override DLMALLOC_INC = $(DLMALLOC_DIR)/include +override LIBC_BOTTOM_HALF_DIR = $(CURDIR)/libc-bottom-half +override LIBC_BOTTOM_HALF_CLOUDLIBC_SRC = $(LIBC_BOTTOM_HALF_DIR)/cloudlibc/src +override LIBC_BOTTOM_HALF_CLOUDLIBC_SRC_INC = $(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC)/include +override LIBC_BOTTOM_HALF_HEADERS_PUBLIC = $(LIBC_BOTTOM_HALF_DIR)/headers/public +override LIBC_BOTTOM_HALF_HEADERS_PRIVATE = $(LIBC_BOTTOM_HALF_DIR)/headers/private +override LIBC_BOTTOM_HALF_LIBPREOPEN_DIR = $(LIBC_BOTTOM_HALF_DIR)/libpreopen +override LIBC_BOTTOM_HALF_SOURCES = $(LIBC_BOTTOM_HALF_DIR)/sources +override LIBC_BOTTOM_HALF_ALL_SOURCES = \ $(shell find $(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC) -name \*.c) \ $(LIBC_BOTTOM_HALF_LIBPREOPEN_DIR)/libpreopen.c \ $(shell find $(LIBC_BOTTOM_HALF_SOURCES) -name \*.c) -LIBWASI_EMULATED_MMAN_SOURCES = \ +override LIBWASI_EMULATED_MMAN_SOURCES = \ $(shell find $(LIBC_BOTTOM_HALF_DIR)/mman -name \*.c) -LIBC_BOTTOM_HALF_CRT_SOURCES = $(wildcard $(LIBC_BOTTOM_HALF_DIR)/crt/*.c) -LIBC_TOP_HALF_DIR = $(CURDIR)/libc-top-half -LIBC_TOP_HALF_MUSL_DIR = $(LIBC_TOP_HALF_DIR)/musl -LIBC_TOP_HALF_MUSL_SRC_DIR = $(LIBC_TOP_HALF_MUSL_DIR)/src -LIBC_TOP_HALF_MUSL_INC = $(LIBC_TOP_HALF_MUSL_DIR)/include -LIBC_TOP_HALF_MUSL_SOURCES = \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/misc/a64l.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/misc/basename.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/misc/dirname.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/misc/ffs.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/misc/ffsl.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/misc/ffsll.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/misc/fmtmsg.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/misc/getdomainname.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/misc/gethostid.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/misc/getopt.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/misc/getopt_long.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/misc/getsubopt.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/misc/uname.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/misc/nftw.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/errno/strerror.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/network/htonl.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/network/htons.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/network/ntohl.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/network/ntohs.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/network/inet_ntop.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/network/inet_pton.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/network/inet_aton.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/network/in6addr_any.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/network/in6addr_loopback.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/fenv/fenv.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/fenv/fesetround.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/fenv/feupdateenv.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/fenv/fesetexceptflag.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/fenv/fegetexceptflag.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/fenv/feholdexcept.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/exit/exit.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/exit/atexit.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/exit/assert.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/exit/quick_exit.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/exit/at_quick_exit.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/strftime.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/asctime.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/asctime_r.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/ctime.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/ctime_r.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/wcsftime.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/strptime.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/difftime.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/timegm.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/ftime.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/gmtime.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/gmtime_r.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/timespec_get.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/getdate.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/localtime.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/localtime_r.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/mktime.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/__tm_to_secs.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/__month_to_secs.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/__secs_to_tm.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/__year_to_secs.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/time/__tz.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/fcntl/creat.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/dirent/alphasort.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/dirent/versionsort.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/env/clearenv.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/env/getenv.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/env/putenv.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/env/setenv.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/env/unsetenv.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/unistd/posix_close.c \ +override LIBC_BOTTOM_HALF_CRT_SOURCES = $(wildcard $(LIBC_BOTTOM_HALF_DIR)/crt/*.c) +override LIBC_TOP_HALF_DIR = $(CURDIR)/libc-top-half +override LIBC_TOP_HALF_MUSL_DIR = $(LIBC_TOP_HALF_DIR)/musl +override LIBC_TOP_HALF_MUSL_SRC_DIR = $(LIBC_TOP_HALF_MUSL_DIR)/src +override LIBC_TOP_HALF_MUSL_INC = $(LIBC_TOP_HALF_MUSL_DIR)/include +override LIBC_TOP_HALF_MUSL_SOURCES = \ + $(addprefix $(LIBC_TOP_HALF_MUSL_SRC_DIR)/, \ + misc/a64l.c \ + misc/basename.c misc/dirname.c \ + misc/ffs.c misc/ffsl.c misc/ffsll.c \ + misc/fmtmsg.c \ + misc/getdomainname.c misc/gethostid.c \ + misc/getopt.c misc/getopt_long.c \ + misc/getsubopt.c \ + misc/uname.c \ + misc/nftw.c \ + errno/strerror.c \ + network/htonl.c network/htons.c network/ntohl.c network/ntohs.c \ + network/inet_ntop.c network/inet_pton.c network/inet_aton.c \ + network/in6addr_any.c network/in6addr_loopback.c \ + fenv/fenv.c fenv/fesetround.c fenv/feupdateenv.c \ + fenv/fesetexceptflag.c fenv/fegetexceptflag.c fenv/feholdexcept.c \ + exit/exit.c exit/atexit.c exit/assert.c \ + exit/quick_exit.c exit/at_quick_exit.c \ + time/strftime.c time/asctime.c time/asctime_r.c \ + time/ctime.c time/ctime_r.c \ + time/wcsftime.c time/strptime.c time/difftime.c \ + time/timegm.c time/ftime.c time/gmtime.c time/gmtime_r.c \ + time/timespec_get.c \ + time/getdate.c \ + time/localtime.c time/localtime_r.c \ + time/mktime.c \ + time/__tm_to_secs.c time/__month_to_secs.c \ + time/__secs_to_tm.c time/__year_to_secs.c \ + time/__tz.c \ + fcntl/creat.c \ + dirent/alphasort.c dirent/versionsort.c \ + env/clearenv.c env/getenv.c env/putenv.c env/setenv.c env/unsetenv.c \ + unistd/posix_close.c \ + ) \ $(filter-out %/procfdname.c %/syscall.c %/syscall_ret.c %/vdso.c %/version.c, \ $(wildcard $(LIBC_TOP_HALF_MUSL_SRC_DIR)/internal/*.c)) \ $(filter-out %/flockfile.c %/funlockfile.c %/__lockfile.c %/ftrylockfile.c \ @@ -146,6 +127,20 @@ LIBC_TOP_HALF_MUSL_SOURCES = \ $(wildcard $(LIBC_TOP_HALF_MUSL_SRC_DIR)/prng/*.c) \ $(wildcard $(LIBC_TOP_HALF_MUSL_SRC_DIR)/conf/*.c) \ $(wildcard $(LIBC_TOP_HALF_MUSL_SRC_DIR)/ctype/*.c) \ + $(filter-out %/crealf.c %/creal.c \ + %/cimagf.c %/cimag.c, \ + $(wildcard $(LIBC_TOP_HALF_MUSL_SRC_DIR)/complex/*.c)) \ + $(wildcard $(LIBC_TOP_HALF_MUSL_SRC_DIR)/crypt/*.c) +override MUSL_PRINTSCAN_SOURCES = \ + $(addprefix $(LIBC_TOP_HALF_MUSL_SRC_DIR)/, \ + internal/floatscan.c \ + stdio/vfprintf.c stdio/vfwprintf.c stdio/vfscanf.c \ + stdlib/strtod.c stdlib/wcstod.c \ + ) +override LIBC_TOP_HALF_HEADERS_PRIVATE = $(LIBC_TOP_HALF_DIR)/headers/private +override LIBC_TOP_HALF_SOURCES = $(LIBC_TOP_HALF_DIR)/sources +override MATH_SOURCES = \ + $(wildcard $(BASICS_DIR)/sources/math/*.c) \ $(filter-out %/__signbit.c %/__signbitf.c %/__signbitl.c \ %/__fpclassify.c %/__fpclassifyf.c %/__fpclassifyl.c \ %/ceilf.c %/ceil.c \ @@ -158,53 +153,36 @@ LIBC_TOP_HALF_MUSL_SOURCES = \ %/copysignf.c %/copysign.c \ %/fminf.c %/fmaxf.c \ %/fmin.c %/fmax.c, \ - $(wildcard $(LIBC_TOP_HALF_MUSL_SRC_DIR)/math/*.c)) \ - $(filter-out %/crealf.c %/creal.c \ - %/cimagf.c %/cimag.c, \ - $(wildcard $(LIBC_TOP_HALF_MUSL_SRC_DIR)/complex/*.c)) \ - $(wildcard $(LIBC_TOP_HALF_MUSL_SRC_DIR)/crypt/*.c) -MUSL_PRINTSCAN_SOURCES = \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/internal/floatscan.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/stdio/vfprintf.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/stdio/vfwprintf.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/stdio/vfscanf.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/stdlib/strtod.c \ - $(LIBC_TOP_HALF_MUSL_SRC_DIR)/stdlib/wcstod.c -LIBC_TOP_HALF_HEADERS_PRIVATE = $(LIBC_TOP_HALF_DIR)/headers/private -LIBC_TOP_HALF_SOURCES = $(LIBC_TOP_HALF_DIR)/sources -LIBC_TOP_HALF_ALL_SOURCES = \ - $(LIBC_TOP_HALF_MUSL_SOURCES) \ - $(shell find $(LIBC_TOP_HALF_SOURCES) -name \*.c) - -# Set the target variables. Multiarch triples notably omit the vendor -# field, which happens to be what we do for the main target triple too. -TARGET_TRIPLE = wasm32-wasi -MULTIARCH_TRIPLE = wasm32-wasi - -# These variables describe the locations of various files and -# directories in the generated sysroot tree. -SYSROOT_LIB = $(SYSROOT)/lib/$(MULTIARCH_TRIPLE) -SYSROOT_INC = $(SYSROOT)/include -SYSROOT_SHARE = $(SYSROOT)/share/$(MULTIARCH_TRIPLE) - -# Set the target. -override WASM_CFLAGS += --target=$(TARGET_TRIPLE) -# WebAssembly floating-point match doesn't trap. -# TODO: Add -fno-signaling-nans when the compiler supports it. -override WASM_CFLAGS += -fno-trapping-math - -# Configure support for threads. -ifeq ($(THREAD_MODEL), single) -override WASM_CFLAGS += -mthread-model single + $(wildcard $(LIBC_TOP_HALF_MUSL_SRC_DIR)/math/*.c)) +ifeq ($(BUILD_LIBC_BOTTOM_HALF),no) +override CRT_SOURCES = $(BASICS_CRT_SOURCES) +else +override CRT_SOURCES = $(LIBC_BOTTOM_HALF_CRT_SOURCES) endif -ifeq ($(THREAD_MODEL), posix) -override WASM_CFLAGS += -mthread-model posix -pthread +ifeq ($(LTO),yes) +# The following files define functions which are called by LLVM CodeGen, +# which runs after LLVM LTO, so LTO libraries don't satisfy them. They're +# also relatively uninteresting to LTO, as LLVM recognizes most of them +# even without seeing their definitions. +override LIBC_NONLTO_SOURCES = \ + $(MATH_SOURCES) \ + $(addprefix $(LIBC_TOP_HALF_MUSL_SRC_DIR)/, \ + exit/atexit.c \ + string/memcpy.c string/memmove.c string/memset.c \ + ) +override LIBC_TOP_HALF_ALL_SOURCES := \ + $(filter-out $(LIBC_NONLTO_SOURCES), $(LIBC_TOP_HALF_MUSL_SOURCES)) +else +override LIBC_TOP_HALF_ALL_SOURCES := \ + $(LIBC_TOP_HALF_MUSL_SOURCES) \ + $(MATH_SOURCES) endif +override LIBC_TOP_HALF_ALL_SOURCES += \ + $(shell find $(LIBC_TOP_HALF_SOURCES) -name \*.c) -# Set the sysroot. -override WASM_CFLAGS += --sysroot="$(SYSROOT)" - -objs = $(patsubst $(CURDIR)/%.c,$(OBJDIR)/%.o,$(1)) +# These variables describe the locations of various files and directories in +# the build tree. +override objs = $(patsubst $(CURDIR)/%.c,$(OBJDIR)/%.o,$(1)) override BASICS_OBJS = $(call objs,$(BASICS_SOURCES)) override DLMALLOC_OBJS = $(call objs,$(DLMALLOC_SOURCES)) override LIBC_BOTTOM_HALF_ALL_OBJS = $(call objs,$(LIBC_BOTTOM_HALF_ALL_SOURCES)) @@ -231,6 +209,87 @@ override MUSL_PRINTSCAN_OBJS = $(call objs,$(MUSL_PRINTSCAN_SOURCES)) override MUSL_PRINTSCAN_LONG_DOUBLE_OBJS = $(patsubst %.o,%.long-double.o,$(MUSL_PRINTSCAN_OBJS)) override MUSL_PRINTSCAN_NO_FLOATING_POINT_OBJS = $(patsubst %.o,%.no-floating-point.o,$(MUSL_PRINTSCAN_OBJS)) override LIBWASI_EMULATED_MMAN_OBJS = $(call objs,$(LIBWASI_EMULATED_MMAN_SOURCES)) +ifeq ($(LTO),yes) +override LIBC_NONLTO_OBJS = $(call objs,$(LIBC_NONLTO_SOURCES)) +endif + +# These variables describe the locations of various files and +# directories in the generated sysroot tree. +override SYSROOT_LIB := $(SYSROOT)/lib/$(MULTIARCH_TRIPLE) +ifeq ($(LTO),yes) +override SYSROOT_LIB := $(SYSROOT_LIB)/llvm-lto/$(CLANG_VERSION) +endif +override SYSROOT_INC = $(SYSROOT)/include +override SYSROOT_SHARE = $(SYSROOT)/share/$(MULTIARCH_TRIPLE) + +# Compute WASM_CFLAGS. + +# Set the target. +override WASM_CFLAGS += --target=$(TARGET_TRIPLE) +# WebAssembly floating-point match doesn't trap. +# TODO: Add -fno-signaling-nans when the compiler supports it. +override WASM_CFLAGS += -fno-trapping-math + +# Configure support for threads. +ifeq ($(THREAD_MODEL), single) +override WASM_CFLAGS += -mthread-model single +endif +ifeq ($(THREAD_MODEL), posix) +override WASM_CFLAGS += -mthread-model posix -pthread +endif + +# Set the sysroot. +override WASM_CFLAGS += --sysroot="$(SYSROOT)" + +# If we're building LTO, enable it. +ifeq ($(LTO),yes) +override WASM_CFLAGS += -flto +endif + +# By default, build the sysroot. +default: sysroot + +# In an LTO build, we disable LTO for select source files. +ifeq ($(LTO),yes) +$(LIBC_NONLTO_OBJS): override WASM_CFLAGS += -fno-lto +endif + +# Configure wasi-libc for building printf with and without long double support. +$(MUSL_PRINTSCAN_OBJS): override WASM_CFLAGS += \ + -D__wasilibc_printscan_no_long_double \ + -D__wasilibc_printscan_full_support_option="\"add -lc-printscan-long-double to the link command\"" +$(MUSL_PRINTSCAN_NO_FLOATING_POINT_OBJS): override WASM_CFLAGS += \ + -D__wasilibc_printscan_no_floating_point \ + -D__wasilibc_printscan_floating_point_support_option="\"remove -lc-printscan-no-floating-point from the link command\"" + +# Add internal include directories. +$(DLMALLOC_OBJS): override WASM_CFLAGS += \ + -I$(DLMALLOC_INC) + +# Add internal include directories. +crt $(LIBC_BOTTOM_HALF_ALL_OBJS): override WASM_CFLAGS += \ + -I$(LIBC_BOTTOM_HALF_HEADERS_PRIVATE) \ + -I$(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC_INC) \ + -I$(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC) + +# Add internal include directories and suppression flags for harmless warnings. +$(LIBC_TOP_HALF_ALL_OBJS) \ + $(MUSL_PRINTSCAN_LONG_DOUBLE_OBJS) \ + $(MUSL_PRINTSCAN_NO_FLOATING_POINT_OBJS) \ + $(LIBC_NONLTO_OBJS) \ +: override WASM_CFLAGS += \ + -I$(LIBC_TOP_HALF_MUSL_SRC_DIR)/include \ + -I$(LIBC_TOP_HALF_MUSL_SRC_DIR)/internal \ + -I$(LIBC_TOP_HALF_MUSL_DIR)/arch/wasm32 \ + -I$(LIBC_TOP_HALF_MUSL_DIR)/arch/generic \ + -I$(LIBC_TOP_HALF_HEADERS_PRIVATE) \ + -Wno-parentheses \ + -Wno-shift-op-parentheses \ + -Wno-bitwise-op-parentheses \ + -Wno-logical-op-parentheses \ + -Wno-string-plus-int \ + -Wno-dangling-else \ + -Wno-unknown-pragmas # Files from musl's include directory that we don't want to install in the # sysroot's include directory. @@ -317,72 +376,8 @@ ifeq ($(THREAD_MODEL), single) override MUSL_OMIT_HEADERS += "aio.h" "pthread.h" endif -default: check - -$(SYSROOT_LIB)/libc.a: $(LIBC_OBJS) - -$(SYSROOT_LIB)/libc-printscan-long-double.a: $(MUSL_PRINTSCAN_LONG_DOUBLE_OBJS) - -$(SYSROOT_LIB)/libc-printscan-no-floating-point.a: $(MUSL_PRINTSCAN_NO_FLOATING_POINT_OBJS) - -$(SYSROOT_LIB)/libwasi-emulated-mman.a: $(LIBWASI_EMULATED_MMAN_OBJS) - -%.a: - @mkdir -p "$(@D)" - # On Windows, the commandline for the ar invocation got too long, so it needs to be split up. - $(WASM_AR) crs $@ $(wordlist 1, 199, $^) - $(WASM_AR) crs $@ $(wordlist 200, 399, $^) - $(WASM_AR) crs $@ $(wordlist 400, 599, $^) - $(WASM_AR) crs $@ $(wordlist 600, 799, $^) - # This might eventually overflow again, but at least it'll do so in a loud way instead of - # silently dropping the tail. - $(WASM_AR) crs $@ $(wordlist 800, 100000, $^) - -$(MUSL_PRINTSCAN_OBJS): override WASM_CFLAGS += \ - -D__wasilibc_printscan_no_long_double \ - -D__wasilibc_printscan_full_support_option="\"add -lc-printscan-long-double to the link command\"" - -$(MUSL_PRINTSCAN_NO_FLOATING_POINT_OBJS): override WASM_CFLAGS += \ - -D__wasilibc_printscan_no_floating_point \ - -D__wasilibc_printscan_floating_point_support_option="\"remove -lc-printscan-no-floating-point from the link command\"" - -$(OBJDIR)/%.long-double.o: $(CURDIR)/%.c include_dirs - @mkdir -p "$(@D)" - "$(WASM_CC)" $(WASM_CFLAGS) -MD -MP -o $@ -c $< - -$(OBJDIR)/%.no-floating-point.o: $(CURDIR)/%.c include_dirs - @mkdir -p "$(@D)" - "$(WASM_CC)" $(WASM_CFLAGS) -MD -MP -o $@ -c $< - -$(OBJDIR)/%.o: $(CURDIR)/%.c include_dirs - @mkdir -p "$(@D)" - "$(WASM_CC)" $(WASM_CFLAGS) -MD -MP -o $@ -c $< - --include $(shell find $(OBJDIR) -name \*.d) - -$(DLMALLOC_OBJS): override WASM_CFLAGS += \ - -I$(DLMALLOC_INC) - -startup_files $(LIBC_BOTTOM_HALF_ALL_OBJS): override WASM_CFLAGS += \ - -I$(LIBC_BOTTOM_HALF_HEADERS_PRIVATE) \ - -I$(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC_INC) \ - -I$(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC) - -$(LIBC_TOP_HALF_ALL_OBJS) $(MUSL_PRINTSCAN_LONG_DOUBLE_OBJS) $(MUSL_PRINTSCAN_NO_FLOATING_POINT_OBJS): override WASM_CFLAGS += \ - -I$(LIBC_TOP_HALF_MUSL_SRC_DIR)/include \ - -I$(LIBC_TOP_HALF_MUSL_SRC_DIR)/internal \ - -I$(LIBC_TOP_HALF_MUSL_DIR)/arch/wasm32 \ - -I$(LIBC_TOP_HALF_MUSL_DIR)/arch/generic \ - -I$(LIBC_TOP_HALF_HEADERS_PRIVATE) \ - -Wno-parentheses \ - -Wno-shift-op-parentheses \ - -Wno-bitwise-op-parentheses \ - -Wno-logical-op-parentheses \ - -Wno-string-plus-int \ - -Wno-dangling-else \ - -Wno-unknown-pragmas - -include_dirs: +# Build the sysroot and perform basic sanity checks. +sysroot: $(RM) -r "$(SYSROOT)" # @@ -408,29 +403,18 @@ include_dirs: # Remove selected header files. $(RM) $(patsubst %,$(SYSROOT_INC)/%,$(MUSL_OMIT_HEADERS)) -ifeq ($(BUILD_LIBC_BOTTOM_HALF),no) -override CRT_SOURCES = $(BASICS_CRT_SOURCES) -else -override CRT_SOURCES = $(LIBC_BOTTOM_HALF_CRT_SOURCES) -endif - -startup_files: include_dirs # # Build the startup files. # - @mkdir -p "$(OBJDIR)" - cd "$(OBJDIR)" && \ - "$(WASM_CC)" $(WASM_CFLAGS) -c $(CRT_SOURCES) -MD -MP && \ - mkdir -p "$(SYSROOT_LIB)" && \ - mv *.o "$(SYSROOT_LIB)" + $(MAKE) crt + $(MAKE) crt LTO=yes -libc: include_dirs \ - $(SYSROOT_LIB)/libc.a \ - $(SYSROOT_LIB)/libc-printscan-long-double.a \ - $(SYSROOT_LIB)/libc-printscan-no-floating-point.a \ - $(SYSROOT_LIB)/libwasi-emulated-mman.a + # + # Build the libc files. + # + $(MAKE) libc + $(MAKE) libc LTO=yes -finish: startup_files libc # # Create empty placeholder libraries. # @@ -441,13 +425,15 @@ finish: startup_files libc # # Collect metadata on the sysroot and perform sanity checks. # - mkdir -p "$(SYSROOT_SHARE)" + @mkdir -p "$(SYSROOT_SHARE)" + # # Collect symbol information. - # TODO: Use llvm-nm --extern-only instead of grep. This is blocked on - # LLVM PR40497, which is fixed in 9.0, but not in 8.0. - # Ignore certain llvm builtin symbols such as those starting with __mul - # since these dependencies can vary between llvm versions. + # + @# TODO: Use llvm-nm --extern-only instead of grep. This is blocked on + @# LLVM PR40497, which is fixed in 9.0, but not in 8.0. + @# Ignore certain llvm builtin symbols such as those starting with __mul + @# since these dependencies can vary between llvm versions. "$(WASM_NM)" --defined-only "$(SYSROOT_LIB)"/libc.a "$(SYSROOT_LIB)"/*.o \ |grep ' [[:upper:]] ' |sed 's/.* [[:upper:]] //' |LC_ALL=C sort > "$(SYSROOT_SHARE)/defined-symbols.txt" for undef_sym in $$("$(WASM_NM)" --undefined-only "$(SYSROOT_LIB)"/*.a "$(SYSROOT_LIB)"/*.o \ @@ -456,28 +442,36 @@ finish: startup_files libc done | grep -v "^__mul" > "$(SYSROOT_SHARE)/undefined-symbols.txt" grep '^_*wasi_' "$(SYSROOT_SHARE)/undefined-symbols.txt" \ > "$(SYSROOT_LIB)/libc.imports" + cp "$(SYSROOT_LIB)/libc.imports" "$(SYSROOT_LIB)/llvm-lto/$(CLANG_VERSION)/libc.imports" + # # Generate a test file that includes all public header files. + # cd "$(SYSROOT)" && \ - for header in $$(find include -type f -not -name mman.h |grep -v /bits/); do \ - echo '#include <'$$header'>' | sed 's/include\///' ; \ + for header in $$(find include -type f -not -name mman.h |grep -v /bits/); do \ + echo '#include <'$$header'>' | sed 's/include\///' ; \ done |LC_ALL=C sort >share/$(MULTIARCH_TRIPLE)/include-all.c ; \ cd - >/dev/null + # # Test that it compiles. + # "$(WASM_CC)" $(WASM_CFLAGS) -fsyntax-only "$(SYSROOT_SHARE)/include-all.c" -Wno-\#warnings - # Collect all the predefined macros, except for compiler version macros - # which we don't need to track here. For the __*_ATOMIC_*_LOCK_FREE - # macros, squash individual compiler names to attempt, toward keeping - # these files compiler-independent. # - # We have to add `-isystem $(SYSROOT_INC)` because otherwise clang puts - # its builtin include path first, which produces compiler-specific - # output. + # Collect all the predefined macros, except for compiler version macros + # which we don't need to track here. # - # TODO: Undefine __FLOAT128__ for now since it's not in clang 8.0. - # TODO: Filter out __FLT16_* for now, as not all versions of clang have these. + @# + @# For the __*_ATOMIC_*_LOCK_FREE macros, squash individual compiler names + @# to attempt, toward keeping these files compiler-independent. + @# + @# We have to add `-isystem $(SYSROOT_INC)` because otherwise clang puts + @# its builtin include path first, which produces compiler-specific + @# output. + @# + @# TODO: Undefine __FLOAT128__ for now since it's not in clang 8.0. + @# TODO: Filter out __FLT16_* for now, as not all versions of clang have these. "$(WASM_CC)" $(WASM_CFLAGS) "$(SYSROOT_SHARE)/include-all.c" \ -isystem $(SYSROOT_INC) \ -E -dM -Wno-\#warnings \ @@ -497,17 +491,65 @@ finish: startup_files libc | grep -v '^#define __FLT16_' \ > "$(SYSROOT_SHARE)/predefined-macros.txt" + # Check that the computed metadata matches the expected metadata. + diff -ur "$(CURDIR)/expected/$(MULTIARCH_TRIPLE)" "$(SYSROOT_SHARE)" + # # The build succeeded! The generated sysroot is in $(SYSROOT). # -check: finish - # Check that the computed metadata matches the expected metadata. - # This ignores whitespace because on Windows the output has CRLF line endings. - diff -wur "$(CURDIR)/expected/$(MULTIARCH_TRIPLE)" "$(SYSROOT_SHARE)" - -install: finish - mkdir -p "$(INSTALL_DIR)" +# Build the sysroot and install it into $(INSTALL_DIR). +install: sysroot + @mkdir -p "$(INSTALL_DIR)" cp -r "$(SYSROOT)/lib" "$(SYSROOT)/share" "$(SYSROOT)/include" "$(INSTALL_DIR)" -.PHONY: default startup_files libc finish check install include_dirs +crt: + @mkdir -p "$(OBJDIR)" + @mkdir -p "$(SYSROOT_LIB)" + cd "$(OBJDIR)" && \ + "$(WASM_CC)" $(WASM_CFLAGS) -c $(CRT_SOURCES) -MD -MP && \ + mv *.o "$(SYSROOT_LIB)" + +libc: \ + $(SYSROOT_LIB)/libc.a \ + $(SYSROOT_LIB)/libc-printscan-long-double.a \ + $(SYSROOT_LIB)/libc-printscan-no-floating-point.a \ + $(SYSROOT_LIB)/libwasi-emulated-mman.a +ifeq ($(LTO),yes) +libc: $(SYSROOT_LIB)/libc-nonlto.a +endif + +$(SYSROOT_LIB)/libc.a: $(LIBC_OBJS) +$(SYSROOT_LIB)/libc-printscan-long-double.a: $(MUSL_PRINTSCAN_LONG_DOUBLE_OBJS) +$(SYSROOT_LIB)/libc-printscan-no-floating-point.a: $(MUSL_PRINTSCAN_NO_FLOATING_POINT_OBJS) +$(SYSROOT_LIB)/libwasi-emulated-mman.a: $(LIBWASI_EMULATED_MMAN_OBJS) +ifeq ($(LTO),yes) +$(SYSROOT_LIB)/libc-nonlto.a: $(LIBC_NONLTO_OBJS) +endif + +%.a: + @mkdir -p "$(@D)" + # On Windows, the commandline for the ar invocation got too long, so it needs to be split up. + $(WASM_AR) crs $@ $(wordlist 1, 199, $^) + $(WASM_AR) crs $@ $(wordlist 200, 399, $^) + $(WASM_AR) crs $@ $(wordlist 400, 599, $^) + $(WASM_AR) crs $@ $(wordlist 600, 799, $^) + # This might eventually overflow again, but at least it'll do so in a loud way instead of + # silently dropping the tail. + $(WASM_AR) crs $@ $(wordlist 800, 100000, $^) + +$(OBJDIR)/%.o: $(CURDIR)/%.c + @mkdir -p "$(@D)" + "$(WASM_CC)" $(WASM_CFLAGS) -MD -MP -o $@ -c $< + +$(OBJDIR)/%.long-double.o: $(CURDIR)/%.c + @mkdir -p "$(@D)" + "$(WASM_CC)" $(WASM_CFLAGS) -MD -MP -o $@ -c $< + +$(OBJDIR)/%.no-floating-point.o: $(CURDIR)/%.c + @mkdir -p "$(@D)" + "$(WASM_CC)" $(WASM_CFLAGS) -MD -MP -o $@ -c $< + +-include $(shell find $(OBJDIR) -name \*.d) + +.PHONY: default sysroot install crt libc diff --git a/basics/crt/crt1.c b/basics/crt/crt1.c index a0f5a7349..744baab85 100644 --- a/basics/crt/crt1.c +++ b/basics/crt/crt1.c @@ -7,10 +7,9 @@ void _start(void) { // The linker synthesizes this to call constructors. __wasm_call_ctors(); - // Call `__original_main` which will either be the application's - // zero-argument `main` function (renamed by the compiler) or a libc - // routine which populates `argv` and `argc` and calls the application's - // two-argument `main`. + // Call `__original_main` which will either be the application's zero-argument + // `__original_main` function or a libc routine which calls `__main_void`. + // TODO: Call `main` directly once we no longer have to support old compilers. int r = __original_main(); // Call atexit functions, destructors, stdio cleanup, etc. diff --git a/expected/wasm32-wasi/defined-symbols.txt b/expected/wasm32-wasi/defined-symbols.txt index b3a46b869..e9819067b 100644 --- a/expected/wasm32-wasi/defined-symbols.txt +++ b/expected/wasm32-wasi/defined-symbols.txt @@ -139,6 +139,8 @@ __log2_data __log2f_data __log_data __logf_data +__main_argc_argv +__main_void __math_divzero __math_divzerof __math_invalid diff --git a/libc-bottom-half/crt/crt1.c b/libc-bottom-half/crt/crt1.c index 1b9cadb46..f70c24a9f 100644 --- a/libc-bottom-half/crt/crt1.c +++ b/libc-bottom-half/crt/crt1.c @@ -1,5 +1,4 @@ #include -#include extern void __wasm_call_ctors(void); extern int __original_main(void); extern void __prepare_for_exit(void); @@ -8,10 +7,9 @@ void _start(void) { // The linker synthesizes this to call constructors. __wasm_call_ctors(); - // Call `__original_main` which will either be the application's - // zero-argument `main` function (renamed by the compiler) or a libc - // routine which populates `argv` and `argc` and calls the application's - // two-argument `main`. + // Call `__original_main` which will either be the application's zero-argument + // `__original_main` function or a libc routine which calls `__main_void`. + // TODO: Call `main` directly once we no longer have to support old compilers. int r = __original_main(); // Call atexit functions, destructors, stdio cleanup, etc. diff --git a/libc-bottom-half/sources/__main_argc_argv.c b/libc-bottom-half/sources/__main_argc_argv.c new file mode 100644 index 000000000..d8c9001c2 --- /dev/null +++ b/libc-bottom-half/sources/__main_argc_argv.c @@ -0,0 +1,15 @@ +#include +#include +#include +#include + +// New compilers define `__main_argc_argv`. If that doesn't exist, we +// may get called here. Old compilers define `main` expecting an +// argv/argc, so call that. +// TODO: Remove this layer when we no longer have to support old compilers. +int __wasilibc_main(int argc, char *argv[]) asm("main"); + +__attribute__((weak, nodebug)) +int __main_argc_argv(int argc, char *argv[]) { + return __wasilibc_main(argc, argv); +} diff --git a/libc-bottom-half/sources/__main_void.c b/libc-bottom-half/sources/__main_void.c new file mode 100644 index 000000000..51c171bce --- /dev/null +++ b/libc-bottom-half/sources/__main_void.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include + +// The user's `main` function, expecting arguments. +int __main_argc_argv(int argc, char *argv[]); + +// If the user's `main` function expects arguments, the compiler will rename +// it to `__main_argc_argv`, and this version will get linked in, which +// initializes the argument data and calls `__main_argc_argv`. +__attribute__((weak, nodebug)) +int __main_void(void) { + __wasi_errno_t err; + + // Get the sizes of the arrays we'll have to create to copy in the args. + size_t argv_buf_size; + size_t argc; + err = __wasi_args_sizes_get(&argc, &argv_buf_size); + if (err != __WASI_ERRNO_SUCCESS) { + _Exit(EX_OSERR); + } + + // Add 1 for the NULL pointer to mark the end, and check for overflow. + size_t num_ptrs = argc + 1; + if (num_ptrs == 0) { + _Exit(EX_SOFTWARE); + } + + // Allocate memory for storing the argument chars. + char *argv_buf = malloc(argv_buf_size); + if (argv_buf == NULL) { + _Exit(EX_SOFTWARE); + } + + // Allocate memory for the array of pointers. This uses `calloc` both to + // handle overflow and to initialize the NULL pointer at the end. + char **argv = calloc(num_ptrs, sizeof(char *)); + if (argv == NULL) { + free(argv_buf); + _Exit(EX_SOFTWARE); + } + + // Fill the argument chars, and the argv array with pointers into those chars. + // TODO: Remove the casts on `argv_ptrs` and `argv_buf` once the witx is updated with char8 support. + err = __wasi_args_get((uint8_t **)argv, (uint8_t *)argv_buf); + if (err != __WASI_ERRNO_SUCCESS) { + free(argv_buf); + free(argv); + _Exit(EX_OSERR); + } + + // Call `__main_argc_argv` with the arguments! + return __main_argc_argv(argc, argv); +} diff --git a/libc-bottom-half/sources/__original_main.c b/libc-bottom-half/sources/__original_main.c index b0982d50b..70c8a94aa 100644 --- a/libc-bottom-half/sources/__original_main.c +++ b/libc-bottom-half/sources/__original_main.c @@ -3,53 +3,13 @@ #include #include -// The user's `main` function, expecting arguments. -int main(int argc, char *argv[]); +// Old compilers define `__original_main`. If that doesn't exist, we +// get called here. New compilers define `__main_void`. If that doesn't +// exist, we'll try something else. +// TODO: Remove this layer when we no longer have to support old compilers. +int __main_void(void); -// If the user's `main` function expects arguments, the compiler won't emit -// an `__original_main` function so this version will get linked in, which -// initializes the argument data and calls `main`. __attribute__((weak)) int __original_main(void) { - __wasi_errno_t err; - - // Get the sizes of the arrays we'll have to create to copy in the args. - size_t argv_buf_size; - size_t argc; - err = __wasi_args_sizes_get(&argc, &argv_buf_size); - if (err != __WASI_ERRNO_SUCCESS) { - _Exit(EX_OSERR); - } - - // Add 1 for the NULL pointer to mark the end, and check for overflow. - size_t num_ptrs = argc + 1; - if (num_ptrs == 0) { - _Exit(EX_SOFTWARE); - } - - // Allocate memory for storing the argument chars. - char *argv_buf = malloc(argv_buf_size); - if (argv_buf == NULL) { - _Exit(EX_SOFTWARE); - } - - // Allocate memory for the array of pointers. This uses `calloc` both to - // handle overflow and to initialize the NULL pointer at the end. - char **argv = calloc(num_ptrs, sizeof(char *)); - if (argv == NULL) { - free(argv_buf); - _Exit(EX_SOFTWARE); - } - - // Fill the argument chars, and the argv array with pointers into those chars. - // TODO: Remove the casts on `argv_ptrs` and `argv_buf` once the witx is updated with char8 support. - err = __wasi_args_get((uint8_t **)argv, (uint8_t *)argv_buf); - if (err != __WASI_ERRNO_SUCCESS) { - free(argv_buf); - free(argv); - _Exit(EX_OSERR); - } - - // Call main with the arguments! - return main(argc, argv); + return __main_void(); }