Message ID | 20240830073341.572756-1-Deepthi.Hemraj@windriver.com |
---|---|
State | New |
Headers | show |
Series | [v2] GCC Driver : Enable very long gcc command-line option | expand |
On Fri, Aug 30, 2024 at 12:34 AM <Deepthi.Hemraj@windriver.com> wrote: > > From: Deepthi Hemraj <Deepthi.Hemraj@windriver.com> > > For excessively long environment variables i.e >128KB > Store the arguments in a temporary file and collect them back together in collect2. > > This commit patches for COLLECT_GCC_OPTIONS issue: > GCC should not limit the length of command line passed to collect2. > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111527 > > The Linux kernel has the following limits on shell commands: > I. Total number of bytes used to specify arguments must be under 128KB. > II. Each environment variable passed to an executable must be under 128 KiB > > In order to circumvent these limitations, many build tools support > response-files, i.e. files that contain the arguments for the executed > command. These are typically passed using @<filepath> syntax. > > Gcc uses the COLLECT_GCC_OPTIONS environment variable to transfer the > expanded command line to collect2. With many options, this exceeds the limit II. > > GCC : Added Testcase for PR111527 > > TC1 : If the command line argument less than 128kb, gcc should use > COLLECT_GCC_OPTION to communicate and compile fine. > TC2 : If the command line argument in the range of 128kb to 2mb, > gcc should copy arguments in a file and use FILE_GCC_OPTIONS > to communicate and compile fine. > TC3 : If the command line argument greater thean 2mb, gcc shuld > fail the compile and report error. (Expected FAIL) > > Signed-off-by: sunil dora <sunilkumar.dora@windriver.com> > Signed-off-by: Topi Kuutela <topi.kuutela@nokia.com> > Signed-off-by: Deepthi Hemraj <Deepthi.Hemraj@windriver.com> > --- > gcc/collect2.cc | 39 ++++++++++++++++++-- > gcc/gcc.cc | 37 +++++++++++++++++-- > gcc/testsuite/gcc.dg/longcmd/longcmd.exp | 16 +++++++++ > gcc/testsuite/gcc.dg/longcmd/pr111527-1.c | 44 +++++++++++++++++++++++ > gcc/testsuite/gcc.dg/longcmd/pr111527-2.c | 9 +++++ > gcc/testsuite/gcc.dg/longcmd/pr111527-3.c | 10 ++++++ > gcc/testsuite/gcc.dg/longcmd/pr111527-4.c | 10 ++++++ > 7 files changed, 159 insertions(+), 6 deletions(-) > create mode 100644 gcc/testsuite/gcc.dg/longcmd/longcmd.exp > create mode 100644 gcc/testsuite/gcc.dg/longcmd/pr111527-1.c > create mode 100644 gcc/testsuite/gcc.dg/longcmd/pr111527-2.c > create mode 100644 gcc/testsuite/gcc.dg/longcmd/pr111527-3.c > create mode 100644 gcc/testsuite/gcc.dg/longcmd/pr111527-4.c > > diff --git a/gcc/collect2.cc b/gcc/collect2.cc > index 902014a9cc1..1f56963b1ce 100644 > --- a/gcc/collect2.cc > +++ b/gcc/collect2.cc > @@ -376,6 +376,39 @@ typedef int scanfilter; > > static void scan_prog_file (const char *, scanpass, scanfilter); > > +char* getenv_extended (const char* var_name) > +{ > + int file_size; > + char* buf = NULL; > + const char* prefix = "/tmp"; > + > + char* string = getenv (var_name); > + if (strncmp (var_name, prefix, strlen(prefix)) == 0) This is not what was meant by saying using the same env and supporting response files. Instead what Richard meant was use `@file` as the option that gets passed via COLLECT_GCC_OPTIONS and then if you see `@` expand the options like what is done for the normal command line. Hard coding "/tmp" here is wrong because TMPDIR might not be set to "/tmp" and even more with -save-temps, the response file should stay around afterwards and be in the working directory rather than TMPDIR. Thanks, Andrew Pinski > + { > + FILE *fptr; > + fptr = fopen (string, "r"); > + if (fptr == NULL) > + return (0); > + /* Copy contents from temporary file to buffer */ > + if (fseek (fptr, 0, SEEK_END) == -1) > + return (0); > + file_size = ftell (fptr); > + rewind (fptr); > + buf = (char *) xmalloc (file_size + 1); > + if (buf == NULL) > + return (0); > + if (fread ((void *) buf, file_size, 1, fptr) <= 0) > + { > + free (buf); > + fatal_error (input_location, "fread failed"); > + return (0); > + } > + buf[file_size] = '\0'; > + return buf; > + } > + return string; > +} > + > > /* Delete tempfiles and exit function. */ > > @@ -1004,7 +1037,7 @@ main (int argc, char **argv) > /* Now pick up any flags we want early from COLLECT_GCC_OPTIONS > The LTO options are passed here as are other options that might > be unsuitable for ld (e.g. -save-temps). */ > - p = getenv ("COLLECT_GCC_OPTIONS"); > + p = getenv_extended ("COLLECT_GCC_OPTIONS"); > while (p && *p) > { > const char *q = extract_string (&p); > @@ -1200,7 +1233,7 @@ main (int argc, char **argv) > AIX support needs to know if -shared has been specified before > parsing commandline arguments. */ > > - p = getenv ("COLLECT_GCC_OPTIONS"); > + p = getenv_extended ("COLLECT_GCC_OPTIONS"); > while (p && *p) > { > const char *q = extract_string (&p); > @@ -1594,7 +1627,7 @@ main (int argc, char **argv) > fprintf (stderr, "o_file = %s\n", > (o_file ? o_file : "not found")); > > - ptr = getenv ("COLLECT_GCC_OPTIONS"); > + ptr = getenv_extended ("COLLECT_GCC_OPTIONS"); > if (ptr) > fprintf (stderr, "COLLECT_GCC_OPTIONS = %s\n", ptr); > > diff --git a/gcc/gcc.cc b/gcc/gcc.cc > index d07a8e172a4..3d7fd8dff5b 100644 > --- a/gcc/gcc.cc > +++ b/gcc/gcc.cc > @@ -2953,12 +2953,43 @@ add_to_obstack (char *path, void *data) > return NULL; > } > > -/* Add or change the value of an environment variable, outputting the > - change to standard error if in verbose mode. */ > +/* Add or change the value of an environment variable, > + * outputting the change to standard error if in verbose mode. */ > static void > xputenv (const char *string) > { > - env.xput (string); > + static const size_t MAX_ENV_VAR_LEN = 128*1024; > + const char *prefix = "COLLECT_GCC_OPTIONS="; > + size_t prefix_len = strlen (prefix); > + FILE *fptr; > + > + const size_t string_length = strlen (string); > + if ( string_length < MAX_ENV_VAR_LEN ) > + { > + env.xput (string); > + return; > + } > + /* For excessively long environment variables i.e >128KB > + Store the arguments in a temporary file and collect > + them back together in collect2 */ > + if (strncmp (string, prefix, prefix_len) == 0) > + { > + const char *data_to_write = string + prefix_len; > + char *temp_file = make_at_file (); > + fptr = fopen (temp_file, "w"); > + if (fptr == NULL) > + { > + fatal_error (input_location, "Cannot open temporary file"); > + return; > + } > + /* Copy contents into temporary file */ > + fwrite (data_to_write, sizeof(char), strlen(data_to_write), fptr); > + char *env_val = (char *) xmalloc (strlen (temp_file) + 21); > + sprintf (env_val, "COLLECT_GCC_OPTIONS=%s", temp_file); > + env.xput (env_val); > + record_temp_file (temp_file, !save_temps_flag, !save_temps_flag); > + fclose (fptr); > + } > } > > /* Build a list of search directories from PATHS. > diff --git a/gcc/testsuite/gcc.dg/longcmd/longcmd.exp b/gcc/testsuite/gcc.dg/longcmd/longcmd.exp > new file mode 100644 > index 00000000000..bac8b6d6fff > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/longcmd/longcmd.exp > @@ -0,0 +1,16 @@ > +# GCC testsuite that uses the `dg.exp' driver. > +# Load support procs. > +load_lib gcc-dg.exp > + > +# If a testcase doesn't have special options, use these. > +global DEFAULT_CFLAGS > +if ![info exists DEFAULT_CFLAGS] then { > + set DEFAULT_CFLAGS "" > +} > + > +dg-init > + > +dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \ > + "" $DEFAULT_CFLAGS > + > +dg-finish > diff --git a/gcc/testsuite/gcc.dg/longcmd/pr111527-1.c b/gcc/testsuite/gcc.dg/longcmd/pr111527-1.c > new file mode 100644 > index 00000000000..a5373f57790 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/longcmd/pr111527-1.c > @@ -0,0 +1,44 @@ > +/* { dg-do run } */ > + > +#include <stdio.h> > +#include <string.h> > +#include <unistd.h> > +#include <stdlib.h> > + > +#define ARGV_LIMIT_SIZE 128 * 1024 > +#define SYSTEM_LIMIT_SIZE 2 * 1024 * 1024 > +#define STR_TO_WRITE "-DTEST " > +void create_large_response_file () > +{ > + FILE *fp1, *fp2; > + char buffer[1024]; > + > + strcpy (buffer, STR_TO_WRITE); > + > + fp1 = fopen ("options_128kb_to_2mb.txt", "wb"); > + if (fp1 == NULL) > + { > + abort (); > + } > + while (ftell (fp1) < (ARGV_LIMIT_SIZE + 10)) > + { > + fwrite (buffer, strlen (buffer), 1, fp1); > + } > + fclose (fp1); > + > + fp2 = fopen ("options_greater_then_2mb.txt", "wb"); > + if (fp2 == NULL) > + { > + abort (); > + } > + while (ftell (fp2) < (SYSTEM_LIMIT_SIZE +10)) > + { > + fwrite (buffer, strlen (buffer), 1, fp2); > + } > + fclose (fp2); > +} > + > +int main() > +{ > + create_large_response_file (); > +} > diff --git a/gcc/testsuite/gcc.dg/longcmd/pr111527-2.c b/gcc/testsuite/gcc.dg/longcmd/pr111527-2.c > new file mode 100644 > index 00000000000..397d70f7b03 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/longcmd/pr111527-2.c > @@ -0,0 +1,9 @@ > +/* { dg-do run } */ > + > +#include <stdio.h> > + > +int main() > +{ > + printf("Hello World\n"); > +} > +/* { dg-final { output-exists { target *-*-* } } } */ > diff --git a/gcc/testsuite/gcc.dg/longcmd/pr111527-3.c b/gcc/testsuite/gcc.dg/longcmd/pr111527-3.c > new file mode 100644 > index 00000000000..394e54b1074 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/longcmd/pr111527-3.c > @@ -0,0 +1,10 @@ > +/* { dg-do run } */ > +/* { dg-options "@options_128kb_to_2mb.txt" } */ > + > +#include <stdio.h> > + > +int main() > +{ > + printf("Hello world\n"); > +} > +/* { dg-final { output-exists { target *-*-* } } } */ > diff --git a/gcc/testsuite/gcc.dg/longcmd/pr111527-4.c b/gcc/testsuite/gcc.dg/longcmd/pr111527-4.c > new file mode 100644 > index 00000000000..eaa427f24a4 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/longcmd/pr111527-4.c > @@ -0,0 +1,10 @@ > +/* { dg-do compile } */ > +/* { dg-options "@options_greater_then_2mb.txt" } */ > +/* { dg-excess-errors "warnings about argument list too long" } */ > + > +#include <stdio.h> > + > +int main() > +{ > + /* { xfail *-*-* } */ > +} > -- > 2.43.0 >
Hi Andrew, Thank you for your feedback. Initially, we attempted to address the issue by utilizing GCC’s response files. However, we discovered that the COLLECT_GCC_OPTIONS variable already contains the expanded contents of the response files. As a result, using response files only mitigates the multiplication factor but does not bypass the 128KB limit. I have included the response file usage logs and the complete history in the Bugzilla report for your reference: Bugzilla Link<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111527#c19>. Following your suggestion, I have updated the logic to avoid hardcoding /tmp. Please find the revised version of patch at the following link: https://gcc.gnu.org/pipermail/gcc-patches/2024-September/662519.html Thanks, Sunil Dora
On Fri, Sep 6, 2024, 9:38 AM Dora, Sunil Kumar < SunilKumar.Dora@windriver.com> wrote: > Hi Andrew, > > Thank you for your feedback. Initially, we attempted to address the issue > by utilizing GCC’s response files. However, we discovered that the > COLLECT_GCC_OPTIONS variable already contains the expanded contents of > the response files. > > As a result, using response files only mitigates the multiplication factor > but does not bypass the 128KB limit. > I think you missed understood me fully. What I was saying instead of creating a string inside set_collect_gcc_options, create the response file and pass that via COLLECT_GCC_OPTIONS with the @file format. And then inside collect2.cc when using COLLECT_GCC_OPTIONS/extract_string instead read in the response file options if there was an @file instead of those 2 loops. This requires more than what you did. Oh and should be less memory hungry and maybe slightly faster. Thanks, Andrew I have included the response file usage logs and the complete history in > the Bugzilla report for your reference: Bugzilla Link > <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111527#c19>. > Following your suggestion, I have updated the logic to avoid hardcoding > /tmp. > Please find the revised version of patch at the following link: > > https://gcc.gnu.org/pipermail/gcc-patches/2024-September/662519.html > > Thanks, > Sunil Dora > ------------------------------ > *From:* Andrew Pinski <pinskia@gmail.com> > *Sent:* Friday, August 30, 2024 8:05 PM > *To:* Hemraj, Deepthi <Deepthi.Hemraj@windriver.com> > *Cc:* gcc-patches@gcc.gnu.org <gcc-patches@gcc.gnu.org>; rguenther@suse.de > <rguenther@suse.de>; jeffreyalaw@gmail.com <jeffreyalaw@gmail.com>; > josmyers@redhat.com <josmyers@redhat.com>; MacLeod, Randy < > Randy.MacLeod@windriver.com>; Gowda, Naveen <Naveen.Gowda@windriver.com>; > Dora, Sunil Kumar <SunilKumar.Dora@windriver.com> > *Subject:* Re: [PATCH v2] GCC Driver : Enable very long gcc command-line > option > > CAUTION: This email comes from a non Wind River email account! > Do not click links or open attachments unless you recognize the sender and > know the content is safe. > > On Fri, Aug 30, 2024 at 12:34 AM <Deepthi.Hemraj@windriver.com> wrote: > > > > From: Deepthi Hemraj <Deepthi.Hemraj@windriver.com> > > > > For excessively long environment variables i.e >128KB > > Store the arguments in a temporary file and collect them back together > in collect2. > > > > This commit patches for COLLECT_GCC_OPTIONS issue: > > GCC should not limit the length of command line passed to collect2. > > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111527 > > > > The Linux kernel has the following limits on shell commands: > > I. Total number of bytes used to specify arguments must be under 128KB. > > II. Each environment variable passed to an executable must be under 128 > KiB > > > > In order to circumvent these limitations, many build tools support > > response-files, i.e. files that contain the arguments for the executed > > command. These are typically passed using @<filepath> syntax. > > > > Gcc uses the COLLECT_GCC_OPTIONS environment variable to transfer the > > expanded command line to collect2. With many options, this exceeds the > limit II. > > > > GCC : Added Testcase for PR111527 > > > > TC1 : If the command line argument less than 128kb, gcc should use > > COLLECT_GCC_OPTION to communicate and compile fine. > > TC2 : If the command line argument in the range of 128kb to 2mb, > > gcc should copy arguments in a file and use FILE_GCC_OPTIONS > > to communicate and compile fine. > > TC3 : If the command line argument greater thean 2mb, gcc shuld > > fail the compile and report error. (Expected FAIL) > > > > Signed-off-by: sunil dora <sunilkumar.dora@windriver.com> > > Signed-off-by: Topi Kuutela <topi.kuutela@nokia.com> > > Signed-off-by: Deepthi Hemraj <Deepthi.Hemraj@windriver.com> > > --- > > gcc/collect2.cc | 39 ++++++++++++++++++-- > > gcc/gcc.cc | 37 +++++++++++++++++-- > > gcc/testsuite/gcc.dg/longcmd/longcmd.exp | 16 +++++++++ > > gcc/testsuite/gcc.dg/longcmd/pr111527-1.c | 44 +++++++++++++++++++++++ > > gcc/testsuite/gcc.dg/longcmd/pr111527-2.c | 9 +++++ > > gcc/testsuite/gcc.dg/longcmd/pr111527-3.c | 10 ++++++ > > gcc/testsuite/gcc.dg/longcmd/pr111527-4.c | 10 ++++++ > > 7 files changed, 159 insertions(+), 6 deletions(-) > > create mode 100644 gcc/testsuite/gcc.dg/longcmd/longcmd.exp > > create mode 100644 gcc/testsuite/gcc.dg/longcmd/pr111527-1.c > > create mode 100644 gcc/testsuite/gcc.dg/longcmd/pr111527-2.c > > create mode 100644 gcc/testsuite/gcc.dg/longcmd/pr111527-3.c > > create mode 100644 gcc/testsuite/gcc.dg/longcmd/pr111527-4.c > > > > diff --git a/gcc/collect2.cc b/gcc/collect2.cc > > index 902014a9cc1..1f56963b1ce 100644 > > --- a/gcc/collect2.cc > > +++ b/gcc/collect2.cc > > @@ -376,6 +376,39 @@ typedef int scanfilter; > > > > static void scan_prog_file (const char *, scanpass, scanfilter); > > > > +char* getenv_extended (const char* var_name) > > +{ > > + int file_size; > > + char* buf = NULL; > > + const char* prefix = "/tmp"; > > + > > + char* string = getenv (var_name); > > + if (strncmp (var_name, prefix, strlen(prefix)) == 0) > > This is not what was meant by saying using the same env and supporting > response files. > Instead what Richard meant was use `@file` as the option that gets > passed via COLLECT_GCC_OPTIONS and then if you see `@` expand the > options like what is done for the normal command line. > Hard coding "/tmp" here is wrong because TMPDIR might not be set to > "/tmp" and even more with -save-temps, the response file should stay > around afterwards and be in the working directory rather than TMPDIR. > > Thanks, > Andrew Pinski > > > + { > > + FILE *fptr; > > + fptr = fopen (string, "r"); > > + if (fptr == NULL) > > + return (0); > > + /* Copy contents from temporary file to buffer */ > > + if (fseek (fptr, 0, SEEK_END) == -1) > > + return (0); > > + file_size = ftell (fptr); > > + rewind (fptr); > > + buf = (char *) xmalloc (file_size + 1); > > + if (buf == NULL) > > + return (0); > > + if (fread ((void *) buf, file_size, 1, fptr) <= 0) > > + { > > + free (buf); > > + fatal_error (input_location, "fread failed"); > > + return (0); > > + } > > + buf[file_size] = '\0'; > > + return buf; > > + } > > + return string; > > +} > > + > > > > /* Delete tempfiles and exit function. */ > > > > @@ -1004,7 +1037,7 @@ main (int argc, char **argv) > > /* Now pick up any flags we want early from COLLECT_GCC_OPTIONS > > The LTO options are passed here as are other options that might > > be unsuitable for ld (e.g. -save-temps). */ > > - p = getenv ("COLLECT_GCC_OPTIONS"); > > + p = getenv_extended ("COLLECT_GCC_OPTIONS"); > > while (p && *p) > > { > > const char *q = extract_string (&p); > > @@ -1200,7 +1233,7 @@ main (int argc, char **argv) > > AIX support needs to know if -shared has been specified before > > parsing commandline arguments. */ > > > > - p = getenv ("COLLECT_GCC_OPTIONS"); > > + p = getenv_extended ("COLLECT_GCC_OPTIONS"); > > while (p && *p) > > { > > const char *q = extract_string (&p); > > @@ -1594,7 +1627,7 @@ main (int argc, char **argv) > > fprintf (stderr, "o_file = %s\n", > > (o_file ? o_file : "not found")); > > > > - ptr = getenv ("COLLECT_GCC_OPTIONS"); > > + ptr = getenv_extended ("COLLECT_GCC_OPTIONS"); > > if (ptr) > > fprintf (stderr, "COLLECT_GCC_OPTIONS = %s\n", ptr); > > > > diff --git a/gcc/gcc.cc b/gcc/gcc.cc > > index d07a8e172a4..3d7fd8dff5b 100644 > > --- a/gcc/gcc.cc > > +++ b/gcc/gcc.cc > > @@ -2953,12 +2953,43 @@ add_to_obstack (char *path, void *data) > > return NULL; > > } > > > > -/* Add or change the value of an environment variable, outputting the > > - change to standard error if in verbose mode. */ > > +/* Add or change the value of an environment variable, > > + * outputting the change to standard error if in verbose mode. */ > > static void > > xputenv (const char *string) > > { > > - env.xput (string); > > + static const size_t MAX_ENV_VAR_LEN = 128*1024; > > + const char *prefix = "COLLECT_GCC_OPTIONS="; > > + size_t prefix_len = strlen (prefix); > > + FILE *fptr; > > + > > + const size_t string_length = strlen (string); > > + if ( string_length < MAX_ENV_VAR_LEN ) > > + { > > + env.xput (string); > > + return; > > + } > > + /* For excessively long environment variables i.e >128KB > > + Store the arguments in a temporary file and collect > > + them back together in collect2 */ > > + if (strncmp (string, prefix, prefix_len) == 0) > > + { > > + const char *data_to_write = string + prefix_len; > > + char *temp_file = make_at_file (); > > + fptr = fopen (temp_file, "w"); > > + if (fptr == NULL) > > + { > > + fatal_error (input_location, "Cannot open temporary file"); > > + return; > > + } > > + /* Copy contents into temporary file */ > > + fwrite (data_to_write, sizeof(char), strlen(data_to_write), fptr); > > + char *env_val = (char *) xmalloc (strlen (temp_file) + 21); > > + sprintf (env_val, "COLLECT_GCC_OPTIONS=%s", temp_file); > > + env.xput (env_val); > > + record_temp_file (temp_file, !save_temps_flag, !save_temps_flag); > > + fclose (fptr); > > + } > > } > > > > /* Build a list of search directories from PATHS. > > diff --git a/gcc/testsuite/gcc.dg/longcmd/longcmd.exp > b/gcc/testsuite/gcc.dg/longcmd/longcmd.exp > > new file mode 100644 > > index 00000000000..bac8b6d6fff > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/longcmd/longcmd.exp > > @@ -0,0 +1,16 @@ > > +# GCC testsuite that uses the `dg.exp' driver. > > +# Load support procs. > > +load_lib gcc-dg.exp > > + > > +# If a testcase doesn't have special options, use these. > > +global DEFAULT_CFLAGS > > +if ![info exists DEFAULT_CFLAGS] then { > > + set DEFAULT_CFLAGS "" > > +} > > + > > +dg-init > > + > > +dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \ > > + "" $DEFAULT_CFLAGS > > + > > +dg-finish > > diff --git a/gcc/testsuite/gcc.dg/longcmd/pr111527-1.c > b/gcc/testsuite/gcc.dg/longcmd/pr111527-1.c > > new file mode 100644 > > index 00000000000..a5373f57790 > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/longcmd/pr111527-1.c > > @@ -0,0 +1,44 @@ > > +/* { dg-do run } */ > > + > > +#include <stdio.h> > > +#include <string.h> > > +#include <unistd.h> > > +#include <stdlib.h> > > + > > +#define ARGV_LIMIT_SIZE 128 * 1024 > > +#define SYSTEM_LIMIT_SIZE 2 * 1024 * 1024 > > +#define STR_TO_WRITE "-DTEST " > > +void create_large_response_file () > > +{ > > + FILE *fp1, *fp2; > > + char buffer[1024]; > > + > > + strcpy (buffer, STR_TO_WRITE); > > + > > + fp1 = fopen ("options_128kb_to_2mb.txt", "wb"); > > + if (fp1 == NULL) > > + { > > + abort (); > > + } > > + while (ftell (fp1) < (ARGV_LIMIT_SIZE + 10)) > > + { > > + fwrite (buffer, strlen (buffer), 1, fp1); > > + } > > + fclose (fp1); > > + > > + fp2 = fopen ("options_greater_then_2mb.txt", "wb"); > > + if (fp2 == NULL) > > + { > > + abort (); > > + } > > + while (ftell (fp2) < (SYSTEM_LIMIT_SIZE +10)) > > + { > > + fwrite (buffer, strlen (buffer), 1, fp2); > > + } > > + fclose (fp2); > > +} > > + > > +int main() > > +{ > > + create_large_response_file (); > > +} > > diff --git a/gcc/testsuite/gcc.dg/longcmd/pr111527-2.c > b/gcc/testsuite/gcc.dg/longcmd/pr111527-2.c > > new file mode 100644 > > index 00000000000..397d70f7b03 > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/longcmd/pr111527-2.c > > @@ -0,0 +1,9 @@ > > +/* { dg-do run } */ > > + > > +#include <stdio.h> > > + > > +int main() > > +{ > > + printf("Hello World\n"); > > +} > > +/* { dg-final { output-exists { target *-*-* } } } */ > > diff --git a/gcc/testsuite/gcc.dg/longcmd/pr111527-3.c > b/gcc/testsuite/gcc.dg/longcmd/pr111527-3.c > > new file mode 100644 > > index 00000000000..394e54b1074 > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/longcmd/pr111527-3.c > > @@ -0,0 +1,10 @@ > > +/* { dg-do run } */ > > +/* { dg-options "@options_128kb_to_2mb.txt" } */ > > + > > +#include <stdio.h> > > + > > +int main() > > +{ > > + printf("Hello world\n"); > > +} > > +/* { dg-final { output-exists { target *-*-* } } } */ > > diff --git a/gcc/testsuite/gcc.dg/longcmd/pr111527-4.c > b/gcc/testsuite/gcc.dg/longcmd/pr111527-4.c > > new file mode 100644 > > index 00000000000..eaa427f24a4 > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/longcmd/pr111527-4.c > > @@ -0,0 +1,10 @@ > > +/* { dg-do compile } */ > > +/* { dg-options "@options_greater_then_2mb.txt" } */ > > +/* { dg-excess-errors "warnings about argument list too long" } */ > > + > > +#include <stdio.h> > > + > > +int main() > > +{ > > + /* { xfail *-*-* } */ > > +} > > -- > > 2.43.0 > > >
diff --git a/gcc/collect2.cc b/gcc/collect2.cc index 902014a9cc1..1f56963b1ce 100644 --- a/gcc/collect2.cc +++ b/gcc/collect2.cc @@ -376,6 +376,39 @@ typedef int scanfilter; static void scan_prog_file (const char *, scanpass, scanfilter); +char* getenv_extended (const char* var_name) +{ + int file_size; + char* buf = NULL; + const char* prefix = "/tmp"; + + char* string = getenv (var_name); + if (strncmp (var_name, prefix, strlen(prefix)) == 0) + { + FILE *fptr; + fptr = fopen (string, "r"); + if (fptr == NULL) + return (0); + /* Copy contents from temporary file to buffer */ + if (fseek (fptr, 0, SEEK_END) == -1) + return (0); + file_size = ftell (fptr); + rewind (fptr); + buf = (char *) xmalloc (file_size + 1); + if (buf == NULL) + return (0); + if (fread ((void *) buf, file_size, 1, fptr) <= 0) + { + free (buf); + fatal_error (input_location, "fread failed"); + return (0); + } + buf[file_size] = '\0'; + return buf; + } + return string; +} + /* Delete tempfiles and exit function. */ @@ -1004,7 +1037,7 @@ main (int argc, char **argv) /* Now pick up any flags we want early from COLLECT_GCC_OPTIONS The LTO options are passed here as are other options that might be unsuitable for ld (e.g. -save-temps). */ - p = getenv ("COLLECT_GCC_OPTIONS"); + p = getenv_extended ("COLLECT_GCC_OPTIONS"); while (p && *p) { const char *q = extract_string (&p); @@ -1200,7 +1233,7 @@ main (int argc, char **argv) AIX support needs to know if -shared has been specified before parsing commandline arguments. */ - p = getenv ("COLLECT_GCC_OPTIONS"); + p = getenv_extended ("COLLECT_GCC_OPTIONS"); while (p && *p) { const char *q = extract_string (&p); @@ -1594,7 +1627,7 @@ main (int argc, char **argv) fprintf (stderr, "o_file = %s\n", (o_file ? o_file : "not found")); - ptr = getenv ("COLLECT_GCC_OPTIONS"); + ptr = getenv_extended ("COLLECT_GCC_OPTIONS"); if (ptr) fprintf (stderr, "COLLECT_GCC_OPTIONS = %s\n", ptr); diff --git a/gcc/gcc.cc b/gcc/gcc.cc index d07a8e172a4..3d7fd8dff5b 100644 --- a/gcc/gcc.cc +++ b/gcc/gcc.cc @@ -2953,12 +2953,43 @@ add_to_obstack (char *path, void *data) return NULL; } -/* Add or change the value of an environment variable, outputting the - change to standard error if in verbose mode. */ +/* Add or change the value of an environment variable, + * outputting the change to standard error if in verbose mode. */ static void xputenv (const char *string) { - env.xput (string); + static const size_t MAX_ENV_VAR_LEN = 128*1024; + const char *prefix = "COLLECT_GCC_OPTIONS="; + size_t prefix_len = strlen (prefix); + FILE *fptr; + + const size_t string_length = strlen (string); + if ( string_length < MAX_ENV_VAR_LEN ) + { + env.xput (string); + return; + } + /* For excessively long environment variables i.e >128KB + Store the arguments in a temporary file and collect + them back together in collect2 */ + if (strncmp (string, prefix, prefix_len) == 0) + { + const char *data_to_write = string + prefix_len; + char *temp_file = make_at_file (); + fptr = fopen (temp_file, "w"); + if (fptr == NULL) + { + fatal_error (input_location, "Cannot open temporary file"); + return; + } + /* Copy contents into temporary file */ + fwrite (data_to_write, sizeof(char), strlen(data_to_write), fptr); + char *env_val = (char *) xmalloc (strlen (temp_file) + 21); + sprintf (env_val, "COLLECT_GCC_OPTIONS=%s", temp_file); + env.xput (env_val); + record_temp_file (temp_file, !save_temps_flag, !save_temps_flag); + fclose (fptr); + } } /* Build a list of search directories from PATHS. diff --git a/gcc/testsuite/gcc.dg/longcmd/longcmd.exp b/gcc/testsuite/gcc.dg/longcmd/longcmd.exp new file mode 100644 index 00000000000..bac8b6d6fff --- /dev/null +++ b/gcc/testsuite/gcc.dg/longcmd/longcmd.exp @@ -0,0 +1,16 @@ +# GCC testsuite that uses the `dg.exp' driver. +# Load support procs. +load_lib gcc-dg.exp + +# If a testcase doesn't have special options, use these. +global DEFAULT_CFLAGS +if ![info exists DEFAULT_CFLAGS] then { + set DEFAULT_CFLAGS "" +} + +dg-init + +dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \ + "" $DEFAULT_CFLAGS + +dg-finish diff --git a/gcc/testsuite/gcc.dg/longcmd/pr111527-1.c b/gcc/testsuite/gcc.dg/longcmd/pr111527-1.c new file mode 100644 index 00000000000..a5373f57790 --- /dev/null +++ b/gcc/testsuite/gcc.dg/longcmd/pr111527-1.c @@ -0,0 +1,44 @@ +/* { dg-do run } */ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> + +#define ARGV_LIMIT_SIZE 128 * 1024 +#define SYSTEM_LIMIT_SIZE 2 * 1024 * 1024 +#define STR_TO_WRITE "-DTEST " +void create_large_response_file () +{ + FILE *fp1, *fp2; + char buffer[1024]; + + strcpy (buffer, STR_TO_WRITE); + + fp1 = fopen ("options_128kb_to_2mb.txt", "wb"); + if (fp1 == NULL) + { + abort (); + } + while (ftell (fp1) < (ARGV_LIMIT_SIZE + 10)) + { + fwrite (buffer, strlen (buffer), 1, fp1); + } + fclose (fp1); + + fp2 = fopen ("options_greater_then_2mb.txt", "wb"); + if (fp2 == NULL) + { + abort (); + } + while (ftell (fp2) < (SYSTEM_LIMIT_SIZE +10)) + { + fwrite (buffer, strlen (buffer), 1, fp2); + } + fclose (fp2); +} + +int main() +{ + create_large_response_file (); +} diff --git a/gcc/testsuite/gcc.dg/longcmd/pr111527-2.c b/gcc/testsuite/gcc.dg/longcmd/pr111527-2.c new file mode 100644 index 00000000000..397d70f7b03 --- /dev/null +++ b/gcc/testsuite/gcc.dg/longcmd/pr111527-2.c @@ -0,0 +1,9 @@ +/* { dg-do run } */ + +#include <stdio.h> + +int main() +{ + printf("Hello World\n"); +} +/* { dg-final { output-exists { target *-*-* } } } */ diff --git a/gcc/testsuite/gcc.dg/longcmd/pr111527-3.c b/gcc/testsuite/gcc.dg/longcmd/pr111527-3.c new file mode 100644 index 00000000000..394e54b1074 --- /dev/null +++ b/gcc/testsuite/gcc.dg/longcmd/pr111527-3.c @@ -0,0 +1,10 @@ +/* { dg-do run } */ +/* { dg-options "@options_128kb_to_2mb.txt" } */ + +#include <stdio.h> + +int main() +{ + printf("Hello world\n"); +} +/* { dg-final { output-exists { target *-*-* } } } */ diff --git a/gcc/testsuite/gcc.dg/longcmd/pr111527-4.c b/gcc/testsuite/gcc.dg/longcmd/pr111527-4.c new file mode 100644 index 00000000000..eaa427f24a4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/longcmd/pr111527-4.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-options "@options_greater_then_2mb.txt" } */ +/* { dg-excess-errors "warnings about argument list too long" } */ + +#include <stdio.h> + +int main() +{ + /* { xfail *-*-* } */ +}