Menu

#1248 undefined reference to `memmove' with GCC 10

3.0 Series
closed-accepted
nobody
None
5
2020-06-28
2020-06-08
Tony
No

Versions: 3.04 & 3.05
System: Cross-compiling i686-w64-mingw32 from darwin/linux
Error:

i686-w64-mingw32.static-g++ -o build/urelease/stub_bzip2-x86-unicode/stub_bzip2.exe -s -mwindows -nostdlib -Wl,--exclude-libs,msvcrt.a -Wl,--file-alignment,512 -Wl,-Map,build/urelease/stub_bzip2-x86-unicode/stub_bzip2.map -B pei-i386 -T SCons/Config/linker_script -Wl,-e_NSISWinMainNOCRT -Wl,--subsystem,windows build/urelease/stub_bzip2-x86-unicode/bgbg.o build/urelease/stub_bzip2-x86-unicode/components.o build/urelease/stub_bzip2-x86-unicode/exec.o build/urelease/stub_bzip2-x86-unicode/fileform.o build/urelease/stub_bzip2-x86-unicode/Main.o build/urelease/stub_bzip2-x86-unicode/plugin.o build/urelease/stub_bzip2-x86-unicode/Ui.o build/urelease/stub_bzip2-x86-unicode/util.o build/urelease/stub_bzip2-x86-unicode/crc32.o build/urelease/stub_bzip2-x86-unicode/bzlib.o build/urelease/stub_bzip2-x86-unicode/decompress.o build/urelease/stub_bzip2-x86-unicode/huffman.o build/urelease/stub_bzip2-x86-unicode/resource.o -L/usr/local/lib -lkernel32 -luser32 -lgdi32 -lshell32 -ladvapi32 -lcomdlg32 -lcomctl32 -lole32 -luuid
build/urelease/stub_bzip2-x86-unicode/decompress.o:decompress.c:(.text+0x445): undefined reference to `memmove'
build/urelease/stub_bzip2-x86-unicode/decompress.o:decompress.c:(.text+0x942): undefined reference to `memmove'
build/urelease/stub_bzip2-x86-unicode/decompress.o:decompress.c:(.text+0x9a2): undefined reference to `memmove'
collect2: error: ld returned 1 exit status
scons: *** [build/urelease/stub_bzip2-x86-unicode/stub_bzip2.exe] Error 1
scons: building terminated because of errors.
make[1]: *** [build-only-nsis_i686-w64-mingw32.static] Error 2

GCC 10 has made changes to memmove:

To allow inline expansion of both memcpy and memmove, the existing movmem instruction patterns used for non-overlapping memory copies have been renamed to cpymem. The movmem name is now used for overlapping memory moves, consistent with the library functions memcpy and memmove.

and I can seeSCons/Config/gnu sets:

defenv['NODEFLIBS_FLAG'] = '-nostdlib -Wl,--exclude-libs,msvcrt.a'

along with some special handling for memcpy and memset, but didn't get very far with replicating those.

Discussion

  • Anders

    Anders - 2020-06-09

    If we could get inline expansion of memcpy that would be nice but it appears GCC is inserting a call to one of its own internal functions here which is the opposite of what we want.

    You could try this in decompress.c:

    After the #include "bzlib.h" line, add
    extern void NSISCALL mini_memcpy(void *out, const void *in, UINT_PTR cb);

    Two places in the file there is some code that does somthing that looks like xxx=yyy;//mini_memcpy(&sv, &(s->save), sizeof(sv));

    Remove the xxx=yyy;// part so you are just left with mini_memcpy(....

    This should remove at least two of the undefined reference errors. The last one I can't tell just by looking at the code, you could try commenting stuff out until the error goes away and tell me which line the problem is on.

     
  • Jason

    Jason - 2020-06-13

    I'm not using darwin, but I am trying Fedora 32 with gcc 10.

    Are you using 'XGCC_WIN32_PREFIX' at all? i686-w64-mingw32.static-g++ is not in the list of recognised compilers for nsis. Regular compiler without the '.static' part compiles fine.

    In SCons/Tools/crossmingw.py on line 169, try changing 'g++' to 'gcc' and see if that makes any difference. The stubs should compile but the plugins won't link properly.

    Maybe we could switch the stubs so that it only uses gcc to link instead of g++? (I'll have to test that out first).

     
    • Jason

      Jason - 2020-06-14

      A bit of research on i686-w64-mingw32.static-g++ shows that it's related to MXE, which I have never heard of before. Technically it should work, but the most likely answer is the environment or compiler is wrong.

       
      • Tony

        Tony - 2020-06-14

        it's related to MXE, which I have never heard of before

        Sorry, mxe is a cross-compiler (I'm a maintainer) which builds gcc and various other packages. Most other packages were fairly easy fixes with gcc10, the usual extra warnings and some other porting notes.

        The use of -nostdlib is a little beyond me ;)

        Are you using 'XGCC_WIN32_PREFIX' at all?

        Yes, we also do some sed replacements on 64-bit targets, and use a pinned version of scons-local (currently at 3.0.1). The nsis build rule is:

        https://github.com/mxe/mxe/blob/master/src/nsis.mk

        i686-w64-mingw32.static-g++ is not in the list of recognised compilers for nsis. Regular compiler without the '.static' part compiles fine.

        I'll try the changes earlier in the thread, and also removing the suffix. We've been using the static/shared suffixes since 2014 and it's working fine with gcc5 and gcc9 (probably gcc6|7|8 also, but I haven't tested those).

         
        • Jason

          Jason - 2020-06-14

          -nostdlib is used because we don't want the stubs to depend on C runtime libraries for the installers to run, they should be pure windows code only. Hence our own versions of memcpy and memset, and windows specific calls instead of C runtime calls.

          Ok, it's good that the .static compiler works fine in older versions. I would suggest leaving the compiler command line the way it is (leave it as g++). I don't have an OS to test this on, so there isn't much I can do to help. Darwin is Apple based, right? I have never touched an Apple device before.

           
          • Tony

            Tony - 2020-06-14

            Is -static-libgcc a possible way to avoid runtime dependencies?

             
            • Jason

              Jason - 2020-06-14

              The only downside to static linking the runtime is the size increase on the stubs. You could do that, but we would prefer not to do that (for compatibility reasons on older OS's, like Win 95 and Win XP).

              I'll try installing MXE on my Fedora VM, since it has GCC 10 on it already. If I can replicate the error, then I can troubleshoot it more effectively.

               
              • Tony

                Tony - 2020-06-14

                Darwin is Apple based, right?
                I'll try installing MXE on my Fedora VM

                Yes, though mxe should work on any unix like system. If you've got cycles
                to spare, you can review the requirements at:
                https://mxe.cc/#requirements-fedora

                you probably already have the basic reqs installed and there are some
                that aren't needed for nsis (e.g. gdk-pixbuf2-devel). The sanity check
                can be skipped with DONT_CHECK_REQUIREMENTS in make invocation

                I've just checked out the svn with:

                svn checkout https://svn.code.sf.net/p/nsis/code/ nsis-code
                

                from the same dir, run:

                git clone https://github.com/mxe/mxe.git
                cd mxe
                # for 16 cores, use JOBS=10 -j2
                make nsis JOBS=10 -j2 \
                    nsis_SOURCE_TREE=../nsis-code/NSIS/trunk \
                    DONT_CHECK_REQUIREMENTS=true \
                    MXE_USE_CCACHE=no \
                    MXE_PLUGIN_DIRS=plugins/gcc9 \
                    MXE_TARGETS=i686-w64-mingw32.static.gcc9
                
                mv ../nsis-code/NSIS/trunk/build ../nsis-code/NSIS/trunk/build.gcc9
                mv ../nsis-code/NSIS/trunk/config.log ../nsis-code/NSIS/trunk/config.log.gcc9
                

                I'm testing the next part now:

                make nsis JOBS=10 -j2 \
                    nsis_SOURCE_TREE=../nsis-code/NSIS/trunk \
                    DONT_CHECK_REQUIREMENTS=true \
                    MXE_USE_CCACHE=no \
                    MXE_PLUGIN_DIRS=plugins/gcc10 \
                    MXE_TARGETS=i686-w64-mingw32.static.gcc10
                

                edit: set nsis_SOURCE_TREE=/path/to/repo correctly for gcc10

                 

                Last edit: Tony 2020-06-14
                • Tony

                  Tony - 2020-06-14

                  Same original error with gcc10 on nsis-code/NSIS/trunk. The full log:

                  [log]      /path/to/mxe/log/nsis_i686-w64-mingw32.static.gcc10
                  

                  will show the various commands, the important line is the one after "# scons does various PATH manipulations", it's along the lines of cd {source_dir} && {scons} {options} install (the /usr/local/... tests are for FreeBSD and can be omitted).

                  mxe doesn't "install" itself, none of the compilers/tools are available by default. To reproduce the error in a terminal session, run:

                  # non-bash shells
                  export PATH=/path/to/mxe/usr/bin:$PATH
                  # bash (from the mxe directory)
                  source tools/mxe-activate
                  

                  then copy/paste the scons line from above.

                  Alternately, you can run something like:

                  cd '/path/to/nsis-code/NSIS/trunk' && \
                      PATH=/path/to/mxe/usr/bin:$PATH \
                      scons \
                      XGCC_W32_PREFIX='i686-w64-mingw32.static.gcc10-' \
                      SKIPUTILS='MakeLangId,Makensisw,NSIS Menu,zip2exe' \
                      NSIS_MAX_STRLEN=8192
                  

                  I don't know what the SKIPUTILS and NSIS_MAX_STRLEN do, but we're testing gcc9 vs gcc10 - so keep everything else the same.

                   
                  • Jason

                    Jason - 2020-06-14

                    SKIPUTILS just skips those items to be built. Those 4 in the list are all windows GUI programs, so they can't be run on non-windows anyway. Skipping them just speeds up the build. NSIS_MAX_STRLEN sets the internal string size of the variables in the nsis script, ie $1, $2, etc. Default is 1024, but for our purposes it doesn't matter what it's set to.

                    Oh, and my poor VM. I have a spare laptop that I can wipe and install fedora on, running it on proper hardware should be quicker. I will see how it goes (this takes much longer than I thought).

                     
                    • Tony

                      Tony - 2020-06-15

                      Oh, and my poor VM

                      Indeed!

                      Does SourceForge have a way of fetching a user's public ssh key, or do you have a github account?

                      https://github.com/tonytheodore.keys
                      

                      I can set you up with a temp login on our package building server. It's not the fastest, but it is the quickest way to replicate the issue.

                       
  • Marius Negrutiu

    Marius Negrutiu - 2020-06-15

    Hi guys.
    I've been struggling with this issue myself for a few days...
    Indeed, the gcc10 optimizer identifies code that moves memory and replaces it with msvcrt!memmove calls.
    My approach was to disable these optimizations in decompress.c by using the volatile qualifier on destination addresses.
    For instance, the line decompress.c:266

    while (v > 0) { pos[v] = pos[v-1]; v--; }
    

    ...would become...

    while (v > 0) { ((volatile UChar*)pos)[v] = pos[v-1]; v--; }
    

    See my full commit here.

     
    • Jason

      Jason - 2020-06-15

      My VM ran out of disk space, so I ended up installing Fedora on a spare laptop.

      I did manage to replicate the error that Tony ran into, and Marius did actually find the fix for it.

      So I compiled the source manually using Tonys instructions, compiled WelcomeFinish.nsi, as well as my own alltest.nsi using bzip2 compression, and ran the installers on my windows machine. Everything appears to run fine, even the uninstallers too.

      So that's one confirmed report from me.

      edit: I didn't want to download the whole nsis source tree, so I downloaded just the trunk and changed the rest of the paths to it.

      svn checkout https://svn.code.sf.net/p/nsis/code/NSIS/trunk nsis-code
      
       

      Last edit: Jason 2020-06-15
      • Tony

        Tony - 2020-06-15

        I can also confirm the patch from Marius fixes the gcc10 build, and causes no isssues with gcc5 or gcc9.

        Thanks!

        edit: That was tested on trunk, the patch also applies to the latest release v3.05. mxe is updated so this can be closed from my perspective.

         

        Last edit: Tony 2020-06-15
        • Jason

          Jason - 2020-06-16

          Instead of a typecast, could we just rewrite the destination like so?

          *(pos + v)
          

          I've already wiped my fedora install, so I can't test this straight away. I know the nsis devs don't particularly like typecasts, so doing this without a typecast would be better.

           
          • Marius Negrutiu

            Marius Negrutiu - 2020-06-16

            I'm afraid this wouldn't work.
            From compiler's perspective *(pos + v) is the same as pos[v]. You'll get the same linker error.
            It's the volatile keyword that turns off compiler optimizations and prevents memmove calls.

            Another approach would be to enclose the entire decompress.c into a pragma block like so:

            #pragma GCC push_options
            #pragma GCC optimize ("O0")
            
            ..existing code...
            
            #pragma GCC pop_options
            

            That would turn off optimizations on the entire file, and, get rid of typecasts...

             
            • Anders

              Anders - 2020-06-16

              I don't have a problem with the cast but volatile uchar needs to be a define so it only gets applied to GCC >= 10 because the Microsoft compiler adds a fence around volatile writes.

               
              • Jason

                Jason - 2020-06-20

                I found a reference to version checking here: https://sourceforge.net/p/predef/wiki/VersionNormalization/

                So we could do something like:

                #if defined(PREDEF_COMPILER_GNUC) && (PREDEF_COMPILER_GNUC >= PREDEF_VERSION(10, 1, 0))
                /* GNU C/C++ compiler version 10.1.0 or newer */
                #  define MEM_CAST(x) ((volatile UChar*)(x))
                #else
                #  define MEM_CAST(x) (x)
                #endif
                

                I have to double-check if mingw also defines GNUC etc.

                 
                • Tony

                  Tony - 2020-06-21

                  gcc/mingw pre-defines

                  % echo | i686-w64-mingw32.static.gcc10-cpp -E -dD | grep 'GNUC\|MINGW\|MS'
                  #define __GNUC__ 10
                  #define __GNUC_MINOR__ 1
                  #define __GNUC_PATCHLEVEL__ 0
                  #define __GNUC_STDC_INLINE__ 1
                  #define __MSVCRT__ 1
                  #define __MINGW32__ 1
                  

                  mingw windows.h defines

                  % echo "#include <windows.h>" | i686-w64-mingw32.static.gcc10-cpp -E -dD | grep 'GNUC\|MINGW.*VERSION'
                  #define __GNUC__ 10
                  #define __GNUC_MINOR__ 1
                  #define __GNUC_PATCHLEVEL__ 0
                  #define __GNUC_STDC_INLINE__ 1
                  #define __MINGW64_VERSION_MAJOR 7
                  #define __MINGW64_VERSION_MINOR 0
                  #define __MINGW64_VERSION_BUGFIX 0
                  #define __MINGW64_VERSION_RC 0
                  #define __MINGW64_VERSION_STR __MINGW64_STRINGIFY(__MINGW64_VERSION_MAJOR) "." __MINGW64_STRINGIFY(__MINGW64_VERSION_MINOR) "." __MINGW64_STRINGIFY(__MINGW64_VERSION_BUGFIX)
                  #define __MINGW64_VERSION_STATE "alpha"
                  #define __MINGW32_MAJOR_VERSION 3
                  #define __MINGW32_MINOR_VERSION 11
                  #define __MINGW_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
                  #define __MINGW_GNUC_PREREQ(major,minor) (__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)))
                  #define MINGW_DDRAW_VERSION 7
                  #define __GNUC_VA_LIST
                  #define OPENFILENAME_SIZE_VERSION_400 __MINGW_NAME_AW(OPENFILENAME_SIZE_VERSION_400)
                  
                   
                  • Jason

                    Jason - 2020-06-21

                    I checked an old mingw32 version from 2007 (4.2.1 on ubuntu 12.04), and it works on that. I also checked mingw-w64 on 18.04 and it shows 7.3.0.

                     
  • Marius Negrutiu

    Marius Negrutiu - 2020-06-19

    After some more research I think there's a better way to fix this...
    It turns out that -fno-tree-loop-distribute-patterns flag turns off the pattern recognition (such as memmove, memcpy, memset, etc.) and fixes our linker error. It also requires no changes to decompress.c.
    More details here (look for "-ftree-loop-distribute-patterns").
    Check out my full commit here.

     
    • Anders

      Anders - 2020-06-22

      While I like this, it fails with GCC 4.5.2 so we would have to test for it with FlagsConfigure in gnu.

       
      • Marius Negrutiu

        Marius Negrutiu - 2020-06-23

        Good point. I've committed the changes here.

         
  • Anders

    Anders - 2020-06-28
    • status: open --> closed-accepted
     

Log in to post a comment.