Copies from constant data, such as a memcpy from a string literal, are
expanded into stores of vector constants when store_by_pieces allows it.
This splits the string into pieces of vector width, the last one often
overlapping with the previous one, so the string literal may no longer
appear in the resulting binary.

This complicates scanning binaries for embedded absolute paths, which is
common practice in package managers.

The new option allows package managers to opt out of this optimization,
so they can reliably scan binaries for embedded absolute paths.

gcc/ChangeLog:

        * common.opt (fstore-constants-by-pieces): New option.
        * doc/invoke.texi (-fno-store-constants-by-pieces): Document.
        * expr.cc (can_store_by_pieces): Return false for copies from
        constant data with -fno-store-constants-by-pieces.

gcc/testsuite/ChangeLog:

        * gcc.target/i386/store-constants-by-pieces-1.c: New test.

Signed-off-by: Harmen Stoppels <[email protected]>
---

Hello,

In the Spack package manager we scan binaries for embedded absolute
paths, which only works if literal C-strings are preserved in the
binary. That is not the case when targeting x86_64, where a default
optimization breaks the string literals up into constants that fit in
vector registers. As I understand it, there is no guarantee that those
constants are stored contiguously (which could have the happy
side-effect of getting the literal back in the binary), but even if they
did, the very last chunk is special in that it overlaps with the
previous chunk when the literal's size is not a multiple of the vector
width.

As far as I can see, -mmove-max=128 is related, but that only lowers the
limit to 255 bytes, and also negatively affects ordinary memory copies,
so it does not really help.

Therefore, I'm proposing a compiler option that allows us to disable
this optimization, with the hope that we can reliably scan for
non-relocatable absolute paths that accidentally get embedded in
binaries.

I have not checked, but presumably we are not the only package manager
that has such audits after a build.

Best,

Harmen Stoppels

 gcc/common.opt                                 |  4 ++++
 gcc/doc/invoke.texi                            | 13 ++++++++++++-
 gcc/expr.cc                                    |  3 +++
 .../i386/store-constants-by-pieces-1.c         | 18 ++++++++++++++++++
 4 files changed, 37 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/store-constants-by-pieces-1.c

diff --git a/gcc/common.opt b/gcc/common.opt
index c40606570e9..14fd9453589 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1956,6 +1956,10 @@ fstrict-volatile-bitfields
 Common Var(flag_strict_volatile_bitfields) Init(-1) Optimization
 Force bitfield accesses to match their type width.
 
+fstore-constants-by-pieces
+Common Var(flag_store_constants_by_pieces) Init(1) Optimization
+Expand copies from constant data into stores of immediates.
+
 fstore-merging
 Common Var(flag_store_merging) Optimization
 Merge adjacent stores.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index ef9cb025c22..11927e2dd16 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -681,7 +681,8 @@ Objective-C and Objective-C++ Dialects}.
 -fsingle-precision-constant  -fsplit-ivs-in-unroller  -fsplit-loops
 -fspeculatively-call-stored-functions
 -fsplit-wide-types  -fsplit-wide-types-early  -fssa-backprop  -fssa-phiopt
--fstdarg-opt  -fstore-merging  -fstrict-aliasing -fipa-strict-aliasing
+-fstdarg-opt  -fstore-constants-by-pieces  -fstore-merging
+-fstrict-aliasing -fipa-strict-aliasing
 -fthread-jumps  -ftracer  -ftree-bit-ccp
 -ftree-builtin-call-dce  -ftree-ccp  -ftree-ch  -ftree-coalesce-vars
 -ftree-copy-prop  -ftree-cselim  -ftree-dce  -ftree-dominator-opts
@@ -15420,6 +15421,16 @@ merges contiguous stores of immediate values narrower 
than a word into fewer
 wider stores to reduce the number of instructions.  This is enabled by default
 at @option{-O2} and higher as well as @option{-Os}.
 
+@opindex fstore-constants-by-pieces
+@opindex fno-store-constants-by-pieces
+@item -fno-store-constants-by-pieces
+Do not expand copies from constant data, such as a @code{memcpy} from a
+string literal or the initialization of a character array, into stores of
+immediate values.  The data is copied from the constant in memory instead,
+so that it stays intact in the object file.  This is useful when binaries
+are scanned for embedded strings, such as absolute paths.  The default is
+@option{-fstore-constants-by-pieces}.
+
 @opindex ftree-ter
 @opindex fno-tree-ter
 @item -ftree-ter
diff --git a/gcc/expr.cc b/gcc/expr.cc
index c654387ab01..bd8119af61d 100644
--- a/gcc/expr.cc
+++ b/gcc/expr.cc
@@ -1772,6 +1772,9 @@ can_store_by_pieces (unsigned HOST_WIDE_INT len,
   if (len == 0)
     return true;
 
+  if (!memsetp && !flag_store_constants_by_pieces)
+    return false;
+
   if (!targetm.use_by_pieces_infrastructure_p (len, align,
                                               memsetp
                                                 ? SET_BY_PIECES
diff --git a/gcc/testsuite/gcc.target/i386/store-constants-by-pieces-1.c 
b/gcc/testsuite/gcc.target/i386/store-constants-by-pieces-1.c
new file mode 100644
index 00000000000..5feff67cb29
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/store-constants-by-pieces-1.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx2 -fno-store-constants-by-pieces" } */
+
+void
+foo (char *d)
+{
+  __builtin_memcpy (d, 
"/opt/memcpy/0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz/foo",
 88);
+}
+
+void
+bar (void (*f) (char *))
+{
+  char b[] = 
"/opt/array/0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz/bar";
+  f (b);
+}
+
+/* { dg-final { scan-assembler 
"/opt/memcpy/0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz/foo"
 } } */
+/* { dg-final { scan-assembler 
"/opt/array/0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz/bar"
 } } */
-- 
2.53.0


Reply via email to