diff --git a/src/goto-cc/compile.cpp b/src/goto-cc/compile.cpp index b639489fa7..4f84f36bc3 100644 --- a/src/goto-cc/compile.cpp +++ b/src/goto-cc/compile.cpp @@ -13,6 +13,7 @@ Date: June 2006 #include "compile.h" +#include #include #include #include @@ -135,56 +136,114 @@ bool compilet::doit() warnings_before; } +enum class file_typet +{ + FAILED_TO_OPEN_FILE, + UNKNOWN, + SOURCE_FILE, + NORMAL_ARCHIVE, + THIN_ARCHIVE, + GOTO_BINARY, + ELF_OBJECT +}; + +static file_typet detect_file_type(const std::string &file_name) +{ + // first of all, try to open the file + std::ifstream in(file_name); + if(!in) + return file_typet::FAILED_TO_OPEN_FILE; + + const std::string::size_type r = file_name.rfind('.'); + + const std::string ext = + r == std::string::npos ? "" : file_name.substr(r + 1, file_name.length()); + + if( + ext == "c" || ext == "cc" || ext == "cp" || ext == "cpp" || ext == "CPP" || + ext == "c++" || ext == "C" || ext == "i" || ext == "ii" || ext == "class" || + ext == "jar" || ext == "jsil") + { + return file_typet::SOURCE_FILE; + } + + char hdr[8]; + in.get(hdr, 8); + if((ext == "a" || ext == "o") && strncmp(hdr, "!", 8) == 0) + return file_typet::THIN_ARCHIVE; + + if(ext == "a") + return file_typet::NORMAL_ARCHIVE; + + if(is_goto_binary(file_name)) + return file_typet::GOTO_BINARY; + + if(hdr[0] == 0x7f && memcmp(hdr + 1, "ELF", 3) == 0) + return file_typet::ELF_OBJECT; + + return file_typet::UNKNOWN; +} + /// puts input file names into a list and does preprocessing for libraries. /// \return false on success, true on error. bool compilet::add_input_file(const std::string &file_name) { - // first of all, try to open the file + switch(detect_file_type(file_name)) { - std::ifstream in(file_name); - if(!in) - { - warning() << "failed to open file `" << file_name << "'" << eom; - return warning_is_fatal; // generously ignore unless -Werror - } - } + case file_typet::FAILED_TO_OPEN_FILE: + warning() << "failed to open file `" << file_name + << "': " << std::strerror(errno) << eom; + return warning_is_fatal; // generously ignore unless -Werror - size_t r=file_name.rfind('.', file_name.length()-1); + case file_typet::UNKNOWN: + // unknown extension, not a goto binary, will silently ignore + debug() << "unknown file type in `" << file_name << "'" << eom; + return false; - if(r==std::string::npos) - { - // a file without extension; will ignore - warning() << "input file `" << file_name - << "' has no extension, not considered" << eom; - return warning_is_fatal; - } + case file_typet::ELF_OBJECT: + // ELF file without goto-cc section, silently ignore + debug() << "ELF object without goto-cc section: `" << file_name << "'" + << eom; + return false; - std::string ext = file_name.substr(r+1, file_name.length()); - - if(ext=="c" || - ext=="cc" || - ext=="cp" || - ext=="cpp" || - ext=="CPP" || - ext=="c++" || - ext=="C" || - ext=="i" || - ext=="ii" || - ext=="class" || - ext=="jar" || - ext=="jsil") - { + case file_typet::SOURCE_FILE: source_files.push_back(file_name); - } - else if(ext=="a") - { - #ifdef _WIN32 - char td[MAX_PATH+1]; - #else - char td[] = "goto-cc.XXXXXX"; - #endif + return false; - std::string tstr=get_temporary_directory(td); + case file_typet::NORMAL_ARCHIVE: + return add_files_from_archive(file_name, false); + + case file_typet::THIN_ARCHIVE: + return add_files_from_archive(file_name, true); + + case file_typet::GOTO_BINARY: + object_files.push_back(file_name); + return false; + } + + UNREACHABLE; +} + +/// extracts goto binaries from AR archive and add them as input files. +/// \return false on success, true on error. +bool compilet::add_files_from_archive( + const std::string &file_name, + bool thin_archive) +{ +#ifdef _WIN32 + char td[MAX_PATH + 1]; +#else + char td[] = "goto-cc.XXXXXX"; +#endif + + std::stringstream cmd; + FILE *stream; + + std::string tstr = working_directory; + + if(!thin_archive) + { + tstr = get_temporary_directory(td); if(tstr=="") { @@ -193,7 +252,6 @@ bool compilet::add_input_file(const std::string &file_name) } tmp_dirs.push_back(tstr); - std::stringstream cmd(""); if(chdir(tmp_dirs.back().c_str())!=0) { error() << "Cannot switch to temporary directory" << eom; @@ -203,76 +261,47 @@ bool compilet::add_input_file(const std::string &file_name) // unpack now cmd << "ar x " << concat_dir_file(working_directory, file_name); - FILE *stream; - stream=popen(cmd.str().c_str(), "r"); pclose(stream); cmd.clear(); cmd.str(""); - - // add the files from "ar t" - #ifdef _WIN32 - if(file_name[0]!='/' && file_name[1]!=':') // NOLINT(readability/braces) - #else - if(file_name[0]!='/') // NOLINT(readability/braces) - #endif - { - cmd << "ar t " << - #ifdef _WIN32 - working_directory << "\\" << file_name; - #else - working_directory << "/" << file_name; - #endif - } - else - { - cmd << "ar t " << file_name; - } - - stream=popen(cmd.str().c_str(), "r"); - - if(stream!=nullptr) - { - std::string line; - int ch; // fgetc returns an int, not char - while((ch=fgetc(stream))!=EOF) - { - if(ch!='\n') - { - line+=static_cast(ch); - } - else - { - std::string t; - #ifdef _WIN32 - t = tmp_dirs.back() + '\\' + line; - #else - t = tmp_dirs.back() + '/' + line; - #endif - - if(is_goto_binary(t)) - object_files.push_back(t); - - line = ""; - } - } - - pclose(stream); - } - - cmd.str(""); - - if(chdir(working_directory.c_str())!=0) - error() << "Could not change back to working directory" << eom; } - else if(is_goto_binary(file_name)) - object_files.push_back(file_name); - else + + // add the files from "ar t" + cmd << "ar t " << concat_dir_file(working_directory, file_name); + + stream = popen(cmd.str().c_str(), "r"); + + if(stream != nullptr) { - // unknown extension, not a goto binary, will silently ignore + std::string line; + int ch; // fgetc returns an int, not char + while((ch = fgetc(stream)) != EOF) + { + if(ch != '\n') + { + line += static_cast(ch); + } + else + { + std::string t = concat_dir_file(tstr, line); + + if(is_goto_binary(t)) + object_files.push_back(t); + else + debug() << "Object file is not a goto binary: " << line << eom; + + line = ""; + } + } + + pclose(stream); } + if(!thin_archive && chdir(working_directory.c_str()) != 0) + error() << "Could not change back to working directory" << eom; + return false; } @@ -302,12 +331,17 @@ bool compilet::find_library(const std::string &name) { std::string libname=tmp+name+".so"; - if(is_goto_binary(libname)) - return !add_input_file(libname); - else if(is_elf_file(libname)) + switch(detect_file_type(libname)) { + case file_typet::GOTO_BINARY: + return !add_input_file(libname); + + case file_typet::ELF_OBJECT: warning() << "Warning: Cannot read ELF library " << libname << eom; return warning_is_fatal; + + default: + break; } } } @@ -315,28 +349,6 @@ bool compilet::find_library(const std::string &name) return false; } -/// checking if we can load an object file -/// \par parameters: file name -/// \return true if the given file name exists and is an ELF file, false -/// otherwise -bool compilet::is_elf_file(const std::string &file_name) -{ - std::fstream in; - - in.open(file_name, std::ios::in); - if(in.is_open()) - { - char buf[4]; - for(std::size_t i=0; i<4; i++) - buf[i]=static_cast(in.get()); - if(buf[0]==0x7f && buf[1]=='E' && - buf[2]=='L' && buf[3]=='F') - return true; - } - - return false; -} - /// parses object files and links them /// \return true on error, false otherwise bool compilet::link() diff --git a/src/goto-cc/compile.h b/src/goto-cc/compile.h index 4ae2a2eb0e..839e340e79 100644 --- a/src/goto-cc/compile.h +++ b/src/goto-cc/compile.h @@ -53,7 +53,7 @@ public: bool add_input_file(const std::string &); bool find_library(const std::string &); - bool is_elf_file(const std::string &); + bool add_files_from_archive(const std::string &file_name, bool thin_archive); bool parse(const std::string &filename); bool parse_stdin(); diff --git a/src/goto-cc/gcc_cmdline.cpp b/src/goto-cc/gcc_cmdline.cpp index e4b2bb1ddb..d2f5eefbe2 100644 --- a/src/goto-cc/gcc_cmdline.cpp +++ b/src/goto-cc/gcc_cmdline.cpp @@ -18,9 +18,7 @@ Author: CM Wintersteiger, 2006 #include -/// parses the command line options into a cmdlinet -/// \par parameters: argument count, argument strings -/// \return none +// clang-format off // non-gcc options const char *goto_cc_options_with_separated_argument[]= { @@ -163,6 +161,8 @@ const char *gcc_options_without_argument[]= "-print-multi-directory", "-print-multi-lib", "-print-search-dirs", + "-print-sysroot", + "-print-sysroot-headers-suffix", "-Q", "-Qn", "-Qy", @@ -211,7 +211,11 @@ const char *gcc_options_without_argument[]= "-fast", // Apple only nullptr }; +// clang-format on +/// parses the command line options into a cmdlinet +/// \par parameters: argument count, argument strings +/// \return none bool gcc_cmdlinet::parse(int argc, const char **argv) { assert(argc>0); diff --git a/src/goto-cc/gcc_mode.cpp b/src/goto-cc/gcc_mode.cpp index 88c940aa9d..fade9d0307 100644 --- a/src/goto-cc/gcc_mode.cpp +++ b/src/goto-cc/gcc_mode.cpp @@ -380,12 +380,24 @@ int gcc_modet::doit() return EX_OK; // Exit! } - if(cmdline.isset("dumpversion")) + if( + cmdline.isset("dumpmachine") || cmdline.isset("dumpspecs") || + cmdline.isset("dumpversion") || cmdline.isset("print-sysroot") || + cmdline.isset("print-sysroot-headers-suffix")) { if(produce_hybrid_binary) return run_gcc(compiler); - std::cout << "3.4.4\n"; + // GCC will only print one of these, even when multiple arguments are + // passed, so we do the same + if(cmdline.isset("dumpmachine")) + std::cout << config.this_architecture() << '\n'; + else if(cmdline.isset("dumpversion")) + std::cout << "3.4.4\n"; + + // we don't have any meaningful output for the other options, and GCC + // doesn't necessarily produce non-empty output either + return EX_OK; } diff --git a/src/goto-cc/ld_cmdline.cpp b/src/goto-cc/ld_cmdline.cpp index c77ee68773..6e8efecb70 100644 --- a/src/goto-cc/ld_cmdline.cpp +++ b/src/goto-cc/ld_cmdline.cpp @@ -16,9 +16,7 @@ Author: Daniel Kroening, 2013 #include -/// parses the command line options into a cmdlinet -/// \par parameters: argument count, argument strings -/// \return none +// clang-format off const char *goto_ld_options_with_argument[]= { "--verbosity", @@ -99,6 +97,7 @@ const char *ld_options_with_argument[]= "--ios_version_min", // Apple only "--macosx_version_min", // Apple only "--install_name", // Apple only + "--build-id", nullptr }; @@ -235,7 +234,11 @@ const char *ld_options_without_argument[]= "--bundle", // Apple only nullptr }; +// clang-format on +/// parses the command line options into a cmdlinet +/// \par parameters: argument count, argument strings +/// \return none bool ld_cmdlinet::parse(int argc, const char **argv) { assert(argc>0);