Building 32Bit and 64Bit application uitilizing mingw-w64 and cmake - cmake

I need to build a shared library on windows (dll) in 32bit and 64bit.
The build is controlled with cmake, the compiler is mingw64
This is my cmake file:
#64Bit Build (works fine)
ADD_LIBRARY(mylib SHARED mylib.c)
SET_TARGET_PROPERTIES(mylib PROPERTIES PREFIX "")
target_link_options(mylib PUBLIC -Wl,--exclude-all-symbols -s -Wl,--gc-sections -Wl,-Map=output.map )
#32Bit Build (linking issues)
ADD_LIBRARY(mylib32 SHARED mylib.c)
SET_TARGET_PROPERTIES(mylib32 PROPERTIES PREFIX "" COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
target_link_options(mylib32 PUBLIC --verbose -Wl,--exclude-all-symbols -s -Wl,--gc-sections -Wl,-Map=output.map)
I get a lot of complaints during linking due to 32Bit incomatibility of many linked libraries.
I can reduce the amount of complaints, when I am specifically add 32Bit libraries to the search path like this:
target_link_options(mylib32 PUBLIC --verbose -Wl,--exclude-all-symbols -s -Wl,--gc-sections -Wl,-Map=output.map -L/msys64/mingw32/lib -L/msys64/mingw32/i686-w64-mingw32/lib -L/msys64/mingw32/lib/gcc/i686-w64-mingw32/10.3.0)
But there are still some incompatibilities left: like the dllcrt.a which is always linked from the 64Bit mingw path,
which seems kind of hard coded into gcc/ld.
I even tried to use an empty spec fiel, which gave nor results:
target_link_options(mylib32 PUBLIC -specs=myspe --verbose -Wl,--exclude-all-symbols -s -Wl,--gc-sections -Wl,-Map=output.map -L/msys64/mingw32/lib -L/msys64/mingw32/i686-w64-mingw32/lib -L/msys64/mingw32/lib/gcc/i686-w64-mingw32/10.3.0)
Typical error message I get:
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: i386:x86-64 architecture of input file `C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.3.0/../../../../x86_64-w64-mingw32/lib/dllcrt2.o' is incompatible with i386 output

I just figured out a solution for my case:
the crt* stuff is startup code, which is not needed in the case of libraries.
So I added the -nostartfiles flag, and my library links fine.
I guess in case off an application one can probably link the 32bit startfiles manualy as a workaround.
SET_TARGET_PROPERTIES(amprio_seednkey32 PROPERTIES PREFIX "" COMPILE_FLAGS "-m32 -nostartfiles" LINK_FLAGS "-m32 -nostartfiles")
Anyhow, this all feels like a dirty Workaround, I still wonder what is the best way to link 32Bit and 64Bit using mingw-w64.

Related

Modern CMake Cross Compiling to AArch64 with Sysroots

Consider the following example project_(CMakeLists.txt):
cmake_minimum_required(VERSION 3.1)
project(CCL LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
find_package(PkgConfig REQUIRED)
find_package(ZLIB)
find_package(PNG)
find_library(MATH_LIBRARY m)
pkg_search_module(OpenEXR OpenEXR)
add_executable(main main.cpp)
if (MATH_LIBRARY)
target_link_libraries(main PUBLIC ${MATH_LIBRARIES})
endif()
Main.cpp:
#include <iostream>
#include <cmath>
int main(void)
{
std::cout << "Hello, sin()" << std::sin(30) << std::endl;
return 0;
}
I want to compile this project with the following CMake toolchain
(aarch64-toolchain.cmake):
# Cross-compilation system information.
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
# The sysroot contains all the libraries we might need to link against and
# possibly headers we need for compilation.
set(CMAKE_SYSROOT /var/lib/schroot/chroots/ubuntu-focal-arm64)
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_LIBRARY_ARCHITECTURE aarch64-linux-gnu)
# Install path when SYSROOT is read-only.
# set(CMAKE_STAGING_PREFIX aarch64-staging)
# Set the compilers for C, C++ and Fortran.
set(GCC_TRIPLE "aarch64-linux-gnu")
set(CMAKE_C_COMPILER ${GCC_TRIPLE}-gcc-10 CACHE FILEPATH "C compiler")
set(CMAKE_CXX_COMPILER ${GCC_TRIPLE}-g++-10 CACHE FILEPATH "C++ compiler")
set(CMAKE_Fortran_COMPILER ${GCC_TRIPLE}-gfortran CACHE FILEPATH "Fortran compiler")
# Automatically use the cross-wrapper for pkg-config when available.
set(PKG_CONFIG_EXECUTABLE aarch64-linux-gnu-pkg-config CACHE FILEPATH "pkg-config executable")
# Set the architecture-specific compiler flags.
set(ARCH_FLAGS "-mcpu=cortex-a53+crc+simd")
set(CMAKE_C_FLAGS_INIT ${ARCH_FLAGS})
set(CMAKE_CXX_FLAGS_INIT ${ARCH_FLAGS})
set(CMAKE_Fortran_FLAGS_INIT ${ARCH_FLAGS})
# Don't look for programs in the sysroot (these are ARM programs, they won't run
# on the build machine).
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Only look for libraries, headers and packages in the sysroot, don't look on
# the build machine.
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE arm64)
Where the sysroot (/var/lib/schroot/chroots/ubuntu-focal-arm64) was setup using:
name=ubuntu-focal
mk-sbuild --arch=arm64 --skip-proposed --skip-updates --skip-security --name=${name} focal
su - $USER
mk-sbuild --arch=arm64 --skip-proposed --skip-updates --skip-security --name=${name} focal
Configuration-wise, this works fine, however when I try to build this project
find_library 'correctly' finds the wrong libm.so library:
$ cmake -DCMAKE_TOOLCHAIN_FILE=../aarch-toolchain.cmake ..
$ make
Scanning dependencies of target main
[ 50%] Building CXX object CMakeFiles/main.dir/main.cpp.o
make[2]: *** No rule to make target '/var/lib/schroot/chroots/ubuntu-focal-arm64/usr/lib/aarch64-linux-gnu/libm.so', needed by 'main'. Stop.
make[1]: *** [CMakeFiles/Makefile2:76: CMakeFiles/main.dir/all] Error 2
make: *** [Makefile:84: all] Error 2
Looking into the sysroot itself, the library is correctly found, but obviously,
it is a symlink to a file local to the sysroot, not the host:
$ ls -hal /var/lib/schroot/chroots/ubuntu-focal-arm64/usr/lib/aarch64-linux-gnu/
lrwxrwxrwx 1 root root 32 Apr 14 2020 libm.so -> /lib/aarch64-linux-gnu/libm.so.6
It seems to me that CMake is picking up the wrong library (find_library should
really be using the compiler libraries first), so I guess that my toolchain-file
is written incorrectly somehow. How should it be changed to correctly find the
math library, without changing the sysroot or project itself?
(Also, please note that this is an example project to illustrate the problem. I still need to be able to search the sysroot for packages.)
Edits
As requested in the comments, the end state should be a proper aarch64 binary
("main"). Essentially, the build commands below should succeed both with and
without the toolchain, and should yield a functional binary (although, the
aarch64 one naturally only work inside the sysroot).
$ mkdir -p host-build && cd host-build && cmake .. && make
$ mkdir -p device-build && cd device-build && cmake -DCMAKE_TOOLCHAIN_FILE=../aarch64-toolchain.cmake .. && make
TLDR: There is no easy way, as far as I know.
Your toolchain file is fine, it's the symlinks that are bad and they should ideally be relative and not absolute if you wish to use it for building. Below I mention a few options and explain the issue. This is all based on my experience with cross-compiling. Perhaps someone knows more.
There isn't anything wrong with your toolchainfile. From my perspective it is configured correctly. This is an issue as you pointed out with the symlinks being relative to your created rootfs and not absolute to the host machine but they are presented as absolute.
IIRC there is not much you can do here.
I'll give you a few options that I know of from personal experience:
Forget cross-compiling and using QEMU chroot into the environment and build there. This is the easiest option.
Get all the required libraries on your host machine (outside of the sysroot) and compile using them - You just have to make sure that you have the exact same libraries as the target machine. Your toolchain file would be then reconfigured to look for libraries among the ones here.
EDIT: As #josch pointed out in the comments under his answer I should expand on this point to make it more clear to users. However because he already mentioned the most important steps to take when applying this, I will refer the reader to his answer. I will however provide a link to debians official HowTo regarding Multi-arch
The other option would mean fixing your projects CMakeLists to more adapt to the cross-compiling. This would require you to specify the absolute paths to the correct libraries. There is a drawback as you need to always point to the newest library. You could for example create another .cmake file that helps you configure the correct paths and because target_link_libraries() allows you to specify absolute paths, you could use the newly created custom targets.
The last option would be to fix the symlinks (ideally by making them relative). This is in my opinion the preferred way. In the end the rootfs that you will be using for building will be just that, i.e. used for building.
As far as I know (I might be wrong here) but the mks-build is just a debian tool that is originally used for packaging .deb files and as such wasn't made to be complimentary to CMake. And if it was then the debian team probably has the toolchain files you are looking for (i.e. I would look for cross-compiling and package guides regarding .deb packages - if they exist they will be included).
CMAKE_SYSROOT, CMAKE_FIND_ROOT_PATH only add a prefix to specific find_ commands/functions. They pretty much can't change anything about the target system or the linker behavior (which is the issue here). Which means they are correctly configured. It's the fake system that is wrongly configured.
You wrote to the debian-cross mailing list so I assume that your question is Debian specific? If so, maybe tag your question as such. Here is how you cross compile any Debian package for amd64 (aarch64) using sbuild:
sbuild --host=arm64 your_package.dsc
sbuild will take care of setting up your chroot as required and call the build tools with the correct arguments. You do not even need to create a special build chroot for that and can just re-use your existing chroots (sbuild will know what to do with them).
If instead, you just want to cross-compile some software using CMake on Debian, the process is as simple as installing a cross-compiler and pointing CMake to it:
$ sudo apt install g++-aarch64-linux-gnu
$ cmake -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ .
$ make VERBOSE=1
...
/usr/bin/aarch64-linux-gnu-g++ -rdynamic CMakeFiles/main.dir/main.cpp.o -o main
...
$ file main
main: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1 ...
That's it. No need for a sysroot.
If you haven't set up arm64 as a foreign architecture on your system, you need to run this once:
dpkg --add-architecture arm64
apt-get update

How to static linking to glibc in cmake

I'm trying to build a package from Fedora that can run on a RedHat 6 machine. So I need to build and static linking with some library that does not exist in RedHat machine.
I found that I can you -static-libgcc or -static-libstdc++ to link with static version of standard library but I don't know how to do with glibc.
How can I link to static library of glibc with CMake?
I know the question mentions glibc but for C++, since -static-libgcc and -static-libstdc++ are linker options, the correct way to set them in CMake is with target_link_libraries().
So you would set it like this, where MyLibrary is the name of your project:
target_link_libraries(MyLibrary -static-libgcc -static-libstdc++)
Given this, if you want complete static linking of glibc you would likewise pass the -static flag.
target_link_libraries(MyLibrary -static)
If you want more of a global setting:
set(BUILD_SHARED_LIBS OFF)
set(CMAKE_EXE_LINKER_FLAGS "-static")
However, bear in mind that glibc is not designed to be statically linked, and without a great amount of additional work, you won't wind up with a truly static package. Your use case of building "a package from Fedora that can run on a RedHat 6 machine" will not readily work by statically linking glibc.

poco netssl dll dependencies

I'm building a dll to use PocoNetSSL to get some data via an HTTPS endpoint. I need to call that dll via C# in Unity which runs an old version of Mono.
I am using mingw-w64 shell to build my dll. There is a package of the Poco libraries available via the package manager pacman and I am using that.
$ pacman -Qs 'poco'
local/mingw-w64-x86_64-poco 1.6.0-2
POrtable COmponents C++ Libraries (mingw-w64)
I can build an executable and it builds fine & runs fine hitting the https endpoint. I have openssl installed somewhere or it may have come with mingw.
My problem is that I cannot open the dll with LoadLibrary. I get a null pointer and I'm guessing it's a dependency problem. Here's my build commands and a snapshot of dependency walker. Is there anything I am missing here. I think I should be able to do this but maybe not?
sburke#sburke-pc MINGW64 ~/sandbox/hitaws
$ scons
scons: Reading SConscript files ...
msys
scons: done reading SConscript files.
scons: Building targets ...
g++ -o gdoaws.os -c -Wall -DPOCO_WIN32_UTF8 -I/mingw64/include gdoaws.cpp
g++ -o gdoaws.dll -Wl,-no-undefined -shared -Wl,--out-implib=libgdoaws.dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive gdoaws.os -Wl,--no-whole-archive -L/mingw64/lib -lPocoNetSSL.dll -lPocoNet.dll -lPocoUtil.dll -lPocoFoundation.dll
scons: done building targets.
Assuming your loading a dynamic library code is correct your app is very likely trying to load different version of openssl dlls than Poco was built against.
In msys2 you can check a dynamic library dependencies by:
ldd /mingw64/bin/libPocoNetSSL.dll
which depends on:
LIBEAY32.dll => /mingw64/bin/LIBEAY32.dll
SSLEAY32.dll => /mingw64/bin/SSLEAY32.dll
Is this what you see in dependency walker?
The best workround for this problem is to copy above dlls to the folder where your executable is and always distribute them with your software.

How to static link ncurses on AIX using xlc

I'm trying to use the ncurses library on AIX 7.1 to make use of panels which aren't included in the curses library that is standard on AIX. I have the ncurses library installed. The compile,link, and execute work fine with:
xlc ngoodbye.c -lncurses
The actual ncurses library is libncurses.a, which I understand is a static library. However, when I move the executable to another AIX host and execute I get:
Dependent module libncurses.a(libncurses.so.5) could not be loaded. Could not load module libncurses.a(libncurses.so.5). System error: No such file or directory.
How can I link the ncurses library so that the program will execute on other hosts where the ncurses library isn't installed? Note I'm using xlc on AIX, not gcc.
I've tried -bstatic but get link errors at compile time. Note that I'm not a developer so my experience in this area is limited. Thanks.
Both static and shared libraries in AIX are built as position independent (PIC). So even a "shared" library can be statically bound to an executable. You were on the right track with -bstatic, you just need to switch back to dynamic binding for the rest of the libraries you're linking to.
So try this for your final link:
xlc -o myexe myexe.o <other objects as needed> -bstatic -lncurses -bdynamic -lm <and other other libraries as needed>
I do this all the time to make sure that my production environment matches my development one.
Normally ".a" does mean a static library. However, in adapting the initial report (in 2008) describing the AIX 5 shared library configuration there was some miscommunication and ".a" was used for both static and shared libraries. That was finally corrected last year (see changelog).
AIX 4, by the way, used a much more complicated scheme, so shared libraries for ncurses were first implemented on AIX 5.
Packagers prefer shared libraries. So what you have is a shared library named libncurses.a (legal, but not conventional). This is not created with the archiver ar, but using the loader ld. To see that they are different, you can try
ar tv libncurses.a
(with the appropriate directory). Likely ar will say something like
ar: 0707-108 File libncurses.a is not an archive file.
while file may give a more informative message:
libncurses.a: executable (RISC System/6000) or object module not stripped
You can however build ncurses from source. In that case (no matter what version), the default builds static libraries. You need not install those into the system area, but can configure ncurses using the --prefix option to install into a different directory.
As suggested in another answer, there is a workaround using the -bdynamic and -bstatic options of AIX's ld (loader), e.g., changing
xlc -o foo foo.c -lncurses
to
xlc -o foo foo.c -bstatic -lncurses -ldynamic
However, this is partly dependent upon the loader's search path and the name of the archive. If the archive is named libncurses.a, the command works as given. If it is named libncurses.so (as in current sources), then this command is needed to link against the shared library:
xlc -o foo foo.c -brtl -lncurses
But this command (which one might suppose to provide the static linkage using the libncurses.so file) does not succeed:
xlc -o foo foo.c -brtl -bstatic -lncurses -bdynamic

Building SDL2_image as a CMake external project

I've been trying to create a CMake-based build-system for a project that is supposed to use SDL2_image library. I do not want to force user to install any libraries to the system to be able to build the project, so I took advantage of the CMake's ability to download and build dependencies (freetype, SDL2 and SDL2_image) from source code as External Projects.
Everything is fine with freetype and SDL2 (which both include CMakeLists.txt files out of the box), but I've ran out of ideas how to make it work for SDL2_image. CMake's external projects support custom configuration and building settings which I used in different variants with no success.
The CMake file itself can be found here, but the problematic part is this:
# SDL_image library
ExternalProject_Add(sdl2_image_project
URL https://www.libsdl.org/projects/SDL_image/release/SDL2_image-2.0.0.tar.gz
DEPENDS sdl2_project
PREFIX ${LIBS_DIR}/SDL2_image
CONFIGURE_COMMAND LDFLAGS=-L${SDL2_BIN} CFLAGS=-I${SDL2_SRC}/include SDL2_CONFIG=${SDL2_BIN}/sdl2-config <SOURCE_DIR>/configure --prefix=<INSTALL_DIR> --enable-shared=no
BUILD_COMMAND make
INSTALL_COMMAND ""
)
An error occurs while building sdl2_image_project. Some trivial research discovered that the error is generated by the undefined references to parts of libdl. Here is a tiny part of the hole error:
libtool: link: gcc -I/home/snikitin/_src/img_glypher/libs/SDL2/src/sdl2_project/include -I/usr/local/include/SDL2 -D_REENTRANT -o showimage showimage.o -Wl,-rpath -Wl,/usr/local/lib -pthread -L/home/snikitin/_src/img_glypher/libs/SDL2/src/sdl2_project-build ./.libs/libSDL2_image.a -L/usr/local/lib -lSDL2 -pthread
/home/snikitin/_src/img_glypher/libs/SDL2/src/sdl2_project-build/libSDL2.a(SDL_dynapi.c.o): In function `get_sdlapi_entry':
/home/snikitin/_src/img_glypher/libs/SDL2/src/sdl2_project/src/dynapi/SDL_dynapi.c:227: undefined reference to `dlopen'
I think the problem takes place due to the fact that linker tries to create a shared version of SDL2_image library while linking it to a static libSDL2.a. The thing is - if this is right - SDL2 building step creates both static and shared versions of itself so one would assume that linker would use libSDL2-2.0.so instead (I do not actually need a shared library - just the static one, but I do not know how to prevent the build system from trying to create it apart from passing --enable-shared=no to SDL2_image configure script, which does not help in this case).
After a lot of googling I've discovered that the possible source of the problem is that sdl2-config (which is called to get some flags for compiler during SDL_image building) may be called with wrong arguments and produces wrong cflags which confuse everything else. But I'm not sure that is the case and also I do not know how to influence sdl2_config call from CMake (configure --help does not seem to unveil any useful options for this situation).
I am running Ubuntu 14.04 x64 if it matters in any way. Would appreciate any advice!
Looks like you need to link some libraries like m and dl. It can be fixed by providing
custom sdl2-config file. Copy sdl2-config from extracted archive and substitute --libs result:
--libs)
echo -L${exec_prefix}/lib -Wl,-rpath,${libdir} -pthread -lSDL2 -lm -ldl
;;
Note that order is important (that's why just modifying LIBS not works for me).
Now this file can be used in your ExternalProject_Add command instead of SDL2_CONFIG=${SDL2_BIN}/sdl2-config:
...
... CFLAGS=-I${SDL2_SRC}/include SDL2_CONFIG=${CMAKE_CURRENT_LIST_DIR}/sdl2-config <SOURCE_DIR>/configure
...