You are not logged in.

#1 2019-11-14 19:30:37

bepaald
Member
Registered: 2016-10-08
Posts: 16

Cross compiling to windows

Hi!

So I have a project written in c++ (https://github.com/bepaald/signalbackup-tools). I have been trying, and failing to create a windows executable from this code.

The program is a command line program, and depends only on sqlite3, crypto++ and the standard libraries. The program compiles fine with g++, clang++ and mingw without any errors or warnings. In it's simplest form, just "g++ -std=c++2a */*.cc main.cc -lsqlite3 -lcryptopp" would produce a working linux program. Similarly, I have installed mingw-w64-gcc as well as mingw-w64-crypto++ and mingw-w64-sqlite to get a windows build. Calling:

$ x86_64-w64-mingw32-g++ -static-libgcc -static-libstdc++ -static -std=c++2a -L/usr/x86_64-w64-mingw32 */*.cc *.cc -lcryptopp -lsqlite3

creates an executable without any errors or warnings. However, running the exe, either through wine or in a Windows 10 VM doesn't seem to work. The program runs for a bit, but stops somewhere during its execution without any errors or unexpected output, just suddenly I'm back at the prompt (I also tried this at a friend once on an actual on-hardware Windows installation, with the exact same results).

I have very limited windows skills, have no idea how to debug something like this. Anyone with more experience cross-compiling? Any clue what could be going wrong?

Thanks!

Offline

#2 2019-11-20 19:22:06

NeoZelux
Member
Registered: 2012-10-28
Posts: 45

Re: Cross compiling to windows

hey the first week or so of trying to cross compile for windows on my laptop was a nightmare for me, being awake all night and whatnot. I'll try to share some of the troubleshooting steps that helped me get through it.



What I like to do now is make a small test suite of software that tries to run various things ie hello world, play sine wave, open a window etc.
But where I like to start is the most basic main function that's not even hello world but returns a specific value like 69 or something.
you can see the return value of exit codes with something like

echo %ERRORLEVEL%


From there I'd incrementally test larger programs until i see which one doesn't run.
So in addition if you're trying to test different libraries I would go through each step testing one library at a time,
you also have to make sure that you actually use some code that makes the executable ~need~ the library you are testing.

If you only link to it but mingw-w64 sees that you don't actually ever call any code that needs it I believe it omits the reference to the dll
in the executable.

I found the dependency walker "depends"  very useful in this undertaking, and you can verify what I just claimed with it too.
It's probably a good idea to code minimally when testing each library works, rather than testing all the stuff the library offers I'm suggesting
you test one solitary thing the library does and maybe print the result or return it as an exit code to verify it ran successfully, then progress from there.

another useful thing I'd like to mention is the windows api function

int MessageBox(
  HWND    hWnd,
  LPCTSTR lpText,
  LPCTSTR lpCaption,
  UINT    uType
);


last i checked windows has documentation for it here https://docs.microsoft.com/en-us/window … messagebox
I like to use it to display exceptions that abort the whole program because i find it working when all other things in the software fail (text libraries, graphics etc)
It makes those popup error/notification windows with the ok button show up.


Another thing that got me, the default max stack size on my win laptop is like 3 or 4 MB and on my linux desktop is ~8MB, and I had some naughty functions that ran at startup that didn't segfault on linux but would segfault on windows.


gdb will probably catch something like that so if you can get it easily on your target platform it's something to consider.


Another disparity to mention is the sizes of integers on different platforms. This can really lead to messed up stuff like dividing by 0 in the middle of normal expressions that would be fine if all the types were 64bit but maybe on windows or some android they are 32bit and multipyling a and b overflows to a negative number etc.


So you could revisit a lot of the expressions that involve multiplication at some point and make sure that the multipy operations convert to a type big enough to hold the result.

example



long posA,posB,portion ;
...
position1 = posA * portion  /1000 + posB * portion /1000 ; // long is 32bit on some platforms and 64bit on others, if posA * portion results in 5billion that will overflow on the 32bit platforms even though 5billion /1000 can be stored as a 32bit integer

And Lastly, logging is a great practice for big programs. If you log to a file and flush the output near suspect lines of code you could see at what point the program failed that way as well.
I consider logging a good mechanism when you have all the other rudimentary facilities working, at the least you'd probably want to write some kind of test "hello logging" program
to make sure your logging mechanism works right.


I've tested this stuff mostly on windows 10 and a little bit on win 7.

I may have misunderstood which phase of testing you're at when I first started typing this, but even if you got past the hello world-y parts of cross compiling I think alot of it still applies.

Offline

#3 2020-01-13 07:34:40

bepaald
Member
Registered: 2016-10-08
Posts: 16

Re: Cross compiling to windows

Thanks! I've taken the time to more or less pick the program apart and test tiny little bits of it as tiny standalone programs. All sqlite stuff seems to work fine, but when I got to crypto++, I;ve found that it causes a segmentation fault on Windows. The segmentation fault occurs when a CryptoPP object is destroyed, the following is a minimal example:

[~/programming/cryptopp/bin] $ cat main.cc
#include <cryptopp/aes.h>
#include <cryptopp/ccm.h>
#include <iostream>

int main()
{
  int const cipher_size = 32;
  unsigned char cipher[cipher_size] = {0};
  int const iv_size = 16;
  unsigned char iv[iv_size] = {0};

  {
    CryptoPP::CTR_Mode<CryptoPP::AES>::Encryption e(cipher, cipher_size, iv);
  } // <-- e is destroyed

  std::cout << "This line is not printed on Windows :(" << std::endl;

  return 0;
}
[~/programming/cryptopp/bin] $ g++ -Wall -Wextra -O3 -std=c++2a main.cc -o cryptopptest -lcryptopp
[~/programming/cryptopp/bin] $ ./cryptopptest
This line is not printed on Windows :(
[~/programming/cryptopp/bin] $ x86_64-w64-mingw32-g++ -I/usr/x86_64-w64-mingw32/include/ -I/usr/x86_64-w64-mingw32/include/cryptopp/ -Wall -Wextra -O3 -std=c++2a -static-libgcc -static-libstdc++ -static -L/usr/x86_64-w64-mingw32/lib/ -L/usr/x86_64-w64-mingw32/ -o cryptopptest.exe main.cc -lcryptopp
[~/programming/cryptopp/bin] $ wine cryptopptest.exe
This line is not printed on Windows :(
[~/programming/cryptopp/bin] $

As can be seen, the program compiles fine with g++ and x86_64-w64-mingw32-g++, and the native linux version runs fine. The windows binary runs fine when run with wine on linux, but when running on Windows:

Z:\>.\cryptopptest.exe

Z:\>

This is the same binary that runs fine using wine on linux. And it should be noted that the run above takes a minute or so, while it should run and exit successfully in under a second. When I take a slightly less minimal example, where I actually use the encryption object, I find that it works fine: I can actually supply it with data and get correctly encrypted data back, it only fails when the destructor is called.

Now, I don't know what to do next: is the problem with crypto++ (upstream), or with mingw-w64-crypto++ (the aur package), or with mingw (upstream or aur), or with Windows? Or is there no bug anywhere, but am I doing something wrong?

Last edited by bepaald (2020-01-13 07:38:50)

Offline

Board footer

Powered by FluxBB