Mac arm, universal library, how to ling against 2 openssl libs? - cmake

So my understanding is that on mac if I want to build universal library or executable I need to do :
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE INTERNAL "" FORCE)
Now this is great, but if say, this project needs to link against brew - openSSL or vcpkg - zlib...
Then I need to somehow link against 2 openSSL versions?
Since vcpkg builds for either arm or intel, I have now 2x vcpkg installed, 1 for each architecture.
I also have 2x brew installed for each architecture.
So my question is...
How can I
find_package(OpenSSL REQUIRED ${BREW_INTEL})
find_package(OpenSSL REQUIRED ${BREW_ARM})
find_package(zlib REQUIRED ${vcpkg_INTEL})
find_package(zlib REQUIRED ${vcpkg_ARM})
So that each Architecture targets correct sub library... version ?
Am I wrong with this? Right? how do I bite it?
Thanks!

Lacking a better option, I've used lipo to build a multiplatform library:
lipo /opt/homebrew/Cellar/openssl#1.1/1.1.1o/lib/libssl.a \
/usr/local/opt/openssl/lib/libssl.a \
-create -output ~/lib_all/libssl.a

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

Building lld on Apple Silicon

I am try to build lld from LLVM version 11 (I can't use any later or master version of LLVM for the time-being) and am currently configuring with cmake ~/Downloads/lld-11.0.0.src/ -DCMAKE_INSTALL_PREFIX=$HOME/bin/llvm -DCMAKE_PREFIX_PATH=$HOME/bin/llvm -DCMAKE_BUILD_TYPE=Release -G Ninja but ninja install then fails with 20 errors that I think are all standard library related (such as "unknown type name 'constexpr'" and "no template named 'underlying_type_t' in namespace 'std'; did you mean 'underlying_type'?").
The compiler cmake is using by default is Apple Clang 12 which came with my system (or was installed automatically). What other flags should I pass to cmake to get this working? Or do I need to use a different compiler? LLVM 11 configures and builds fine with the same cmake flags.
For anyone else experiencing similar issues: Building LLVM, lld and clang from the monorepo all in one go worked. I had to add -DLLVM_ENABLE_PROJECTS="clang;lld" to the cmake command.

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.

Anyone use GMP on Xeon Phi?

I compiled GMP with icc and -mmic option, but can't install on MIC. How should I install?
I wrote a demo program, compiled with icc. It says can't find gmp.h.
How should I install GMP library on MIC and where to place gmp.h?
Build GMP with Intel Compiler:
cd /home/
wget https://gmplib.org/download/gmp/gmp-6.0.0.tar.bz2
tar -xf gmp-6.0.0.tar.bz2
rm -f gmp-6.0.0.tar.bz2
cd gmp-6.0.0
mkdir mic
cd mic
../configure CC=icc CFLAGS="-mmic" --host=x86_64 --disable-assembly --prefix /home/gmp-6.0.0/mic/
make
make install
Use the Intel Compiler with environment variables for mic development:
GMP_INCDIR=/home/gmp-6.0.0/mic/include
GMP_LIBDIR=/home/gmp-6.0.0/mic/lib
Though I don't have any expertize on Xeon Phi or even ICC if you are running on Unix-like environment, then you might to try to step through normal configure/make procedure on GMP sources pointing on ICC compiler instead of default GCC in order to build static and/or shared library along with generated gmp.h header, that you can then link with your application. You might want to see GMP documentation on that. Here are some advices, that I stepped to trying to compile it for NVIDIA CUDA:
Supply ./configure with CC and CFLAGS variables to point to desired compiler and its whatever options that you want
Be sure that there is no ABI incompability between your host and Xeon Phi device, especially between 32 and 64 bit architecture.
Consider adding --disable-assembly option to generate "pure-C" build (I am not familiar with Xeon Phi assembly and if/how it is compatibile with x86)
Don't forget to run make check (possibly with -j parameter) after you compiled GMP in order to check if tests are passing, it's very important step if you want use it for some professional purpose.
The library is installed in OS by make install command, for default under /usr/local directory (you might add --prefix option if you want it somewhere else), specifically:
/usr/local/include for gmp.h header file
/usr/local/lib for static and/or shared libgmp binaries
You might also try to compile your application with mini-gmp package, which is contained within GMP sources (it's located under mini-gmp directory). It's a subset of mpz_* and mpn_* routines, not as sophisticated and fast as normal GMP (and it doesn't have as much serious tests coverage), but it could make the job done with small footprint (it's contained in one header and C-source file). For such option be sure to obtain most recent version of GMP (or even get it from their repository).

How to cross compile CMake for ARM with CMake

In short I'm trying to cross compile CMake with CMake, and I don't think I'm linking libraries correctly. What I want to do may not be possible, but I'd at least like to know why it isn't possible if that's the case.
System: The host is a Linux box with a Cavium ARM9 CPU. It's currently running version 2.6.24.4 of the Linux kernel and Debian 5.0 (Lenny). My workstation is a Core i5 running Ubuntu 12.04 LTS (Precise Pangolin).
My overall goal is to get ROS running on the Linux box. I have to compile from source rather than use apt since Debian 6.0 (Squeeze) binaries require thumb support that the Cavium does not give, and not many of the needed packages are available for Debian 5.0 (Lenny). I'd made progress installing the various libraries needed, but when I got to step 1.3.1 and tried to run CMake, I got the error
CMake 2.8 or higher is required. You are running version 2.6.0
Next I tried to download and build CMake 2.8.8 on the Linux box itself, but it was too much for the system. When that failed, I downloaded the toolchain suggested on the manufacturer's website and used the cross-compiling guide at [www.cmake.org/Wiki/CMake_Cross_Compiling] to build the CMake executables. Here is my toolchain file:
# This one is important
SET(CMAKE_SYSTEM_NAME Linux)
# Specify the cross compiler
SET(CMAKE_C_COMPILER /pathto/crosstool-linux-gcc-4.5.2-gclibc-2.9-oabi/arm-unknown-linux-gnu/bin/arm-unknown-linux-gnu-gcc)
SET(CMAKE_CXX_COMPILER /pathto/crosstool-linux-gcc-4.5.2-gclibc-2.9-oabi/arm-unknown-linux-gnu/bin/arm-unknown-linux-gnu-g++)
# Where is the target environment
SET(CMAKE_FIND_ROOT_PATH /pathto/crosstool-linux-gcc-4.5.2-gclibc-2.9-oabi/arm-unknown-linux-gnu /pathto/crosstool-linux-gcc-4.5.2-gclibc-2.9-oabi/arm-unknown-linux-gnu/arm-unknown-linux-gnu)
# Search for programs in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# For libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
However, use of the binary on the Linux box gives the error
cmake: /usr/lib/libstdc++.so.6: version `GLIBCXX_3.4.14' not found (required by cmake)
Sure enough, the library is not there:
prompt# strings /usr/lib/libstdc++.so.6 | grep GLIBC
GLIBCXX_3.4
GLIBCXX_3.4.1
GLIBCXX_3.4.2
GLIBCXX_3.4.3
GLIBCXX_3.4.4
GLIBCXX_3.4.5
GLIBCXX_3.4.6
GLIBCXX_3.4.7
GLIBCXX_3.4.8
GLIBCXX_3.4.9
GLIBCXX_3.4.10
GLIBC_2.3
GLIBC_2.0
GLIBC_2.3.2
GLIBC_2.1
GLIBC_2.1.3
GLIBC_2.2
GLIBCXX_FORCE_NEW
GLIBCXX_DEBUG_MESSAGE_LENGTH
I've never cross-compiled before, but I can see one of two scenarios happening: either the binary got created with a link to a higher version of glibcxx on the host machine or the manufacturer's toolchain is more modern than their image. I don't know how to check which is happening or if something else is happening that I don't know about.
My last effort involved trying to statically cross-compile CMake to hopefully get rid of the linking error with
cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain-technologic.cmake -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXE_LINKER_FLAGS_RELEASE="-static" ..
I got build errors, and that binary didn't work either. I got:
FATAL: kernel too old
Segmentation fault
I'd try installing glibcxx 3.4.14 on the Linux box, but it doesn't look like it's available for this processor.
I've tried searching for CMake dependencies or system requirements and can't find anything. I've also searched on how to build CMake, but most searches turn up how to build other things with CMake rather than building CMake itself.
I do cross-compile a lot for ARM9 devices using CMake, and indeed this looks like you're not linking to the same libs you have on your target device. You shouldn't need to build CMake yourself to get this done, since it does have good support for cross-compiling since version 2.6. Just make sure you set the CMAKE_FIND_ROOT_PATH variable to a path where you have an exact copy of the root filesystem you have on your target device (with libraries and binaries pre-compiled for the target processor). That should solve your problems.
As a sidenote, I like to use crosstool-ng for building my cross-compilers. It is a really nice tool which helps you to build them from scratch, so I try to match the compiler version and glibc to the ones originally used to build the root filesystem (I usually start with a ready made root filesystem from ARMedslack, since I use Slackware for my development box and ARMedslack for my ARM targets).