work around gcc lto wrappers bug with gcc 7.x

gcc lto wrapper commands such as `gcc-ar` will segfault with some
versions of gcc 7.x when called via an absolute path, see:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80717

Generalize the mechanism for compiling a small C program on the host
(currently used for bin2c for Wx resources) and use it to wrap the gcc
wrappers in a little executable that prepends their dirname to PATH and
runs them with `execvp()`.

Make LTO default to ON again, except on win32 with gcc < 7, because
those toolchains produce broken binaries with LTO enabled.

Also add `-ffat-lto-objects` to compiler flags for gcc when LTO is
enabled, this will increase the chances that the LTO build will succeed
even if there are issues with the binutils wrappers. Clang does not
support this.
This commit is contained in:
Rafael Kitover 2017-09-16 19:47:39 -07:00
parent b82cab14e2
commit 7f1fec88af
6 changed files with 133 additions and 27 deletions

View File

@ -77,7 +77,12 @@ ENDIF()
option(ENABLE_FFMPEG "Enable ffmpeg A/V recording" ${FFMPEG_DEFAULT})
SET(LTO_DEFAULT OFF)
SET(LTO_DEFAULT ON)
IF(WIN32 AND CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0.0)
# lto produces buggy binaries on gcc < 7 on win32
SET(LTO_DEFAULT OFF)
ENDIF()
OPTION(ENABLE_LTO "Compile with Link Time Optimization (gcc and clang only)" ${LTO_DEFAULT})
@ -296,18 +301,18 @@ ENDIF()
# Compiler flags
IF(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
SET(LTO_FLAG "")
SET(LTO_FLAGS "")
IF(ENABLE_LTO)
IF(CMAKE_COMPILER_IS_GNUCXX)
SET(LTO_FLAG -flto=10)
SET(LTO_FLAGS -flto=10 -ffat-lto-objects)
ELSE()
SET(LTO_FLAG -flto)
SET(LTO_FLAGS -flto)
ENDIF()
ENDIF(ENABLE_LTO)
# common optimization flags
IF(NOT (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.3))
SET(MY_C_OPT_FLAGS -O2 -fomit-frame-pointer ${LTO_FLAG})
SET(MY_C_OPT_FLAGS -O2 -fomit-frame-pointer ${LTO_FLAGS})
ELSE()
# LTO and -fomit-frame-pointer generate broken binaries on Lion with XCode 4.2 tools
SET(MY_C_OPT_FLAGS -O2)

29
cmake/HostCompile.cmake Normal file
View File

@ -0,0 +1,29 @@
function(host_compile src dst_cmd)
unset(link_flags)
if(CMAKE_HOST_WIN32)
if(NOT dst MATCHES "\\.[Ee][Xx][Ee]\$")
set(dst "${dst_cmd}.exe")
endif()
if(CMAKE_COMPILER_IS_GNUCXX)
set(link_flags -Wl,--subsystem,console)
endif()
else()
set(dst "${dst_cmd}")
endif()
if(NOT MSVC)
# assume cc foo.c -o foo # will work on most hosts
set(compile_command cc ${src} -o ${dst} ${link_flags})
else()
# special case for Visual Studio
set(compile_command cl ${src} /link "/out:${dst}")
endif()
execute_process(COMMAND ${compile_command} OUTPUT_VARIABLE compile_out ERROR_VARIABLE compile_out RESULT_VARIABLE compile_result)
if(NOT compile_result EQUAL 0)
message(FATAL_ERROR "Failed compiling ${src} for the host: ${compile_out}")
endif()
endfunction()

71
cmake/PathRun.cmake Normal file
View File

@ -0,0 +1,71 @@
function(make_path_run_wrapper cmd target)
get_filename_component(cmd_resolved "${cmd}" REALPATH)
get_filename_component(base_name "${cmd_resolved}" NAME)
get_filename_component(dir_name "${cmd_resolved}" DIRECTORY)
set(source "${target}.c")
file(WRITE "${source}"
"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define BUF_SZ 4096
#ifdef _WIN32
#include <process.h>
#define PATH_SEP ';'
#define setenv(var, val, dummy) _putenv_s(var, val)
#define execvp my_execvp
#else
#include <unistd.h>
#define PATH_SEP ':'
#endif
char* dir_name = \"${dir_name}\";
char* base_name = \"${base_name}\";
int main(int argc, char** argv) {
size_t dir_len = strlen(dir_name);
char* path = getenv(\"PATH\");
size_t path_len = strlen(path);
char* new_path = malloc(dir_len + path_len + 2);
char** new_argv = malloc(sizeof(char*) * argc);
char** p;
char buf[BUF_SZ];
strcpy(new_path, dir_name);
new_path[dir_len] = PATH_SEP;
strcpy(new_path + dir_len + 1, path);
setenv(\"PATH\", new_path, 1);
free(new_path);
p = new_argv;
*(p++) = base_name;
while (*(++argv)) *(p++) = *argv;
*p = NULL;
execvp(base_name, new_argv);
// this is only reached if exec failed
snprintf(buf, BUF_SZ, \"%s: exec failed\", argv[0]);
perror(buf);
return EXIT_FAILURE;
}
#ifdef _WIN32
int my_execvp(char* cmd, char** argv) {
int ret = _spawnvp(_P_WAIT, cmd, argv);
if (ret == -1) return ret;
exit(ret);
}
#endif
")
include(HostCompile)
host_compile("${source}" "${target}")
endfunction()

View File

@ -72,21 +72,32 @@ IF(NOT EXISTS ${GCC_RANLIB})
FIND_PROGRAM(GCC_RANLIB NAMES gcc-ranlib gcc-ranlib.exe GCC-RANLIB.EXE HINTS ${GCC_DIRNAME})
ENDIF()
INCLUDE(PathRun)
IF(EXISTS ${GCC_AR})
SET(CMAKE_AR ${GCC_AR})
MESSAGE("-- Found gcc-ar: ${CMAKE_AR}")
MESSAGE("-- Found gcc-ar: ${GCC_AR}")
SET(target "${CMAKE_BINARY_DIR}/gcc-ar-wrap")
MAKE_PATH_RUN_WRAPPER("${GCC_AR}" "${target}")
SET(CMAKE_AR "${target}")
ENDIF()
IF(EXISTS ${GCC_NM})
SET(CMAKE_NM ${GCC_NM})
MESSAGE("-- Found gcc-nm: ${CMAKE_NM}")
MESSAGE("-- Found gcc-nm: ${GCC_NM}")
SET(target "${CMAKE_BINARY_DIR}/gcc-nm-wrap")
MAKE_PATH_RUN_WRAPPER("${GCC_NM}" "${target}")
SET(CMAKE_NM "${target}")
ENDIF()
IF(EXISTS ${GCC_RANLIB})
SET(CMAKE_RANLIB ${GCC_RANLIB})
MESSAGE("-- Found gcc-ranlib: ${CMAKE_RANLIB}")
MESSAGE("-- Found gcc-ranlib: ${GCC_RANLIB}")
SET(target "${CMAKE_BINARY_DIR}/gcc-ranlib-wrap")
MAKE_PATH_RUN_WRAPPER("${GCC_RANLIB}" "${target}")
SET(CMAKE_RANLIB "${target}")
ENDIF()
FOREACH(VAR "GCC_AR" "GCC_NM" "GCC_RANLIB" "GCC_DIRNAME" "GCC_BASENAME" "GCC_EXE_SUFFIX")
FOREACH(VAR "GCC_AR" "GCC_NM" "GCC_RANLIB" "GCC_DIRNAME" "GCC_BASENAME" "GCC_EXE_SUFFIX" "target")
UNSET(${VAR})
ENDFOREACH()

@ -1 +1 @@
Subproject commit 76e2d57b891b11aeb203d906fc6c171d7d8b739e
Subproject commit 226f6601fab7f8c9e0920262cdb073c6a9279544

View File

@ -369,19 +369,9 @@ SET(XRC_SOURCES
SET(BIN2C ${CMAKE_BINARY_DIR}/bin2c)
IF(CMAKE_HOST_WIN32)
SET(BIN2C ${BIN2C}.exe)
ENDIF()
INCLUDE(HostCompile)
IF(MSVC)
ADD_CUSTOM_COMMAND(OUTPUT ${BIN2C}
COMMAND cl ${CMAKE_CURRENT_SOURCE_DIR}/bin2c.c /link "/out:${BIN2C}"
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bin2c.c)
ELSE(MSVC)
ADD_CUSTOM_COMMAND(OUTPUT ${BIN2C}
COMMAND cc ${CMAKE_CURRENT_SOURCE_DIR}/bin2c.c -o ${BIN2C}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bin2c.c)
ENDIF(MSVC)
HOST_COMPILE(${CMAKE_CURRENT_SOURCE_DIR}/bin2c.c ${BIN2C})
ADD_CUSTOM_COMMAND(OUTPUT wxvbam.xrs
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
@ -389,12 +379,12 @@ ADD_CUSTOM_COMMAND(OUTPUT wxvbam.xrs
DEPENDS ${XRC_SOURCES})
ADD_CUSTOM_COMMAND(OUTPUT builtin-xrc.h
COMMAND ${BIN2C} wxvbam.xrs builtin-xrc.h builtin_xrs
DEPENDS ${BIN2C} wxvbam.xrs)
DEPENDS wxvbam.xrs)
# use a built-in vba-over.ini if no config file present
ADD_CUSTOM_COMMAND(OUTPUT builtin-over.h
COMMAND ${BIN2C} ${CMAKE_CURRENT_SOURCE_DIR}/../vba-over.ini builtin-over.h builtin_over
DEPENDS ${BIN2C} ../vba-over.ini)
DEPENDS ../vba-over.ini)
# I don't like duplicating/triplicating code, so I only declare
# event handlers once, and copy them in other places they are needed