Currently, commands like: gcc -o file.c -lm will delete the user's code. This patch makes the linker write executables to a temp file, and then renames the temp file if successful. This fixes the case above, but has limitations. The source file will still get overwritten if the link "succeeds", such as the case of: gcc -o file.c -lm -r
It's not perfect, but it should hopefully stop some people from ruining their day. gcc/ChangeLog: PR driver/80182 * gcc.cc (output_file_temp): New global variable (driver_handle_option): Create temp file for executable output (driver::maybe_run_linker): Rename output_file_temp to output_file if the linker ran successfully Signed-off-by: Peter Damianov <peter0...@disroot.org> --- gcc/gcc.cc | 50 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/gcc/gcc.cc b/gcc/gcc.cc index 830a4700a87..cd796d190de 100644 --- a/gcc/gcc.cc +++ b/gcc/gcc.cc @@ -2138,6 +2138,11 @@ static int have_E = 0; /* Pointer to output file name passed in with -o. */ static const char *output_file = 0; +/* We write the output file to a temp file, and rename it if linking + is successful. This is to prevent mistakes like: gcc -o file.c -lm from + deleting the user's code. */ +static const char *output_file_temp = 0; + /* Pointer to input file name passed in with -truncate. This file should be truncated after linking. */ static const char *totruncate_file = 0; @@ -2294,7 +2299,7 @@ close_at_file (void) record_temp_file (temp_file, !save_temps_flag, !save_temps_flag); } - + /* Load specs from a file name named FILENAME, replacing occurrences of various different types of line-endings, \r\n, \n\r and just \r, with a single \n. */ @@ -4610,10 +4615,13 @@ driver_handle_option (struct gcc_options *opts, #if defined(HAVE_TARGET_EXECUTABLE_SUFFIX) || defined(HAVE_TARGET_OBJECT_SUFFIX) arg = convert_filename (arg, ! have_c, 0); #endif - output_file = arg; + output_file_temp = output_file = arg; + /* If creating an executable, output to a temp file. */ + if (!have_c) + output_file_temp = make_temp_file(""); /* On some systems, ld cannot handle "-o" without a space. So split the option from its argument. */ - save_switch ("-o", 1, &arg, validated, true); + save_switch ("-o", 1, &output_file_temp, validated, true); return true; case OPT_pie: @@ -9266,22 +9274,30 @@ driver::maybe_run_linker (const char *argv0) const linker_was_run = (tmp != execution_count); } - /* If options said don't run linker, - complain about input files to be given to the linker. */ - - if (! linker_was_run && !seen_error ()) - for (i = 0; (int) i < n_infiles; i++) - if (explicit_link_files[i] - && !(infiles[i].language && infiles[i].language[0] == '*')) + if (!seen_error ()) + { + if (linker_was_run) + /* If the linker finished without errors, rename the output from the + temporary file to the real output name. */ + rename(output_file_temp, output_file); + else { - warning (0, "%s: linker input file unused because linking not done", - outfiles[i]); - if (access (outfiles[i], F_OK) < 0) - /* This is can be an indication the user specifed an errorneous - separated option value, (or used the wrong prefix for an - option). */ - error ("%s: linker input file not found: %m", outfiles[i]); + /* If options said don't run linker, + complain about input files to be given to the linker. */ + for (i = 0; (int) i < n_infiles; i++) + if (explicit_link_files[i] + && !(infiles[i].language && infiles[i].language[0] == '*')) + { + warning (0, "%s: linker input file unused because linking not done", + outfiles[i]); + if (access (outfiles[i], F_OK) < 0) + /* This is can be an indication the user specifed an errorneous + separated option value, (or used the wrong prefix for an + option). */ + error ("%s: linker input file not found: %m", outfiles[i]); + } } + } } /* The end of "main". */ -- 2.39.2