On Fri, Aug 30, 2024 at 12:34 AM <[email protected]> wrote:
>
> From: Deepthi Hemraj <[email protected]>
>
> 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 <[email protected]>
> Signed-off-by: Topi Kuutela <[email protected]>
> Signed-off-by: Deepthi Hemraj <[email protected]>
> ---
> 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
>