How to build a cross-compiler for i386-apple-darwin target from x86_64-apple-darwin host?
Asked Answered
L

1

8

Problem overview

I am trying to learn how to build a cross-compiler, specifically a version of GNU gcc 4.7.2 on a 64-bit Intel OS X host that targets i386-apple-darwin (32-bit Intel). However, I keep getting stuck on the same error:

...
checking for i386-apple-darwin-gcc... /Users/alexpreynolds/Developer/third-party/build-gcc/./gcc/xgcc -B/Users/alexpreynolds/Developer/third-party/build-gcc/./gcc/ -B/Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/bin/ -B/Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/lib/ -isystem /Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/include -isystem /Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/sys-include
checking for suffix of object files... configure: error: in `/Users/alexpreynolds/Developer/third-party/build-gcc/i386-apple-darwin/libgcc':
configure: error: cannot compute suffix of object files: cannot compile
See `config.log' for more details.
make[1]: *** [configure-target-libgcc] Error 1
make: *** [all] Error 2

Build process

I first build a version of GNU binutils which targets i386-apple-darwin, which is accessible to the GNU gcc build folder:

$ cd third-party
$ wget http://ftp.gnu.org/gnu/binutils/binutils-2.23.2.tar.bz2
$ tar xjvf binutils-2.23.2.tar.bz2
$ mkdir i386-apple-darwin
$ mkdir build-binutils && cd build-binutils
$ ../binutils-2.23.2/configure --target=i386-apple-darwin --prefix="/Users/alexpreynolds/Developer/third-party/i386-apple-gcc"
$ make all && make install
...

Then I try to build a targeted version of gcc as follows:

$ cd ..
$ pwd
/Users/alexpreynolds/Developer/third-party
$ wget http://ftp.gnu.org/pub/gnu/gcc/gcc-4.7.2/gcc-4.7.2.tar.bz2
$ tar xvjf gcc-4.7.2.tar.bz2
$ cd gcc-4.7.2
$ ./contrib/download_prerequisites
... /* this grabs GMP, MPC and MPFR dependencies */
$ cd ..
$ mkdir build-gcc && cd build-gcc
$ ../gcc-4.7.2/configure --target=i386-apple-darwin --prefix="/Users/alexpreynolds/Developer/Bedops/bedops/third-party/i386-apple-gcc"
$ make
...

After about 30 minutes of compiling stuff, it gets stuck with the error message shown above.

Error details

To track down the source of the error, here is a snippet of build-gcc/i386-apple-darwin/libgcc/config.log:

configure:3344: /Users/alexpreynolds/Developer/third-party/build-gcc/./gcc/xgcc -B/Users/alexpreynolds/Developer/third-party/build-gcc/./gcc/ -B/Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/bin/ -B/Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/lib/ -isystem /Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/include -isystem /Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/sys-include    -V >&5
xgcc: error: unrecognized command line option '-V'
xgcc: fatal error: no input files
compilation terminated.
configure:3355: $? = 1
configure:3344: /Users/alexpreynolds/Developer/third-party/build-gcc/./gcc/xgcc -B/Users/alexpreynolds/Developer/third-party/build-gcc/./gcc/ -B/Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/bin/ -B/Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/lib/ -isystem /Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/include -isystem /Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/sys-include    -qversion >&5
xgcc: error: unrecognized command line option '-qversion'
xgcc: fatal error: no input files
compilation terminated.
configure:3355: $? = 1
configure:3371: /Users/alexpreynolds/Developer/third-party/build-gcc/./gcc/xgcc -B/Users/alexpreynolds/Developer/third-party/build-gcc/./gcc/ -B/Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/bin/ -B/Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/lib/ -isystem /Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/include -isystem /Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/sys-include    -o conftest -g -O2   conftest.c  >&5
Assembler messages:
Fatal error: invalid listing option `r'
configure:3374: $? = 1
configure:3562: checking for suffix of object files
configure:3584: /Users/alexpreynolds/Developer/third-party/build-gcc/./gcc/xgcc -B/Users/alexpreynolds/Developer/third-party/build-gcc/./gcc/ -B/Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/bin/ -B/Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/lib/ -isystem /Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/include -isystem /Users/alexpreynolds/Developer/third-party/i386-apple-gcc/i386-apple-darwin/sys-include    -c -g -O2  conftest.c >&5
Assembler messages:
Fatal error: invalid listing option `r'
configure:3588: $? = 1
configure: failed program was:
| /* confdefs.h */
| #define PACKAGE_NAME "GNU C Runtime Library"
| #define PACKAGE_TARNAME "libgcc"
| #define PACKAGE_VERSION "1.0"
| #define PACKAGE_STRING "GNU C Runtime Library 1.0"
| #define PACKAGE_BUGREPORT ""
| #define PACKAGE_URL "http://www.gnu.org/software/libgcc/"
| /* end confdefs.h.  */
|
| int
| main ()
| {
|
|   ;
|   return 0;
| }
configure:3602: error: in `/Users/alexpreynolds/Developer/third-party/build-gcc/i386-apple-darwin/libgcc':
configure:3605: error: cannot compute suffix of object files: cannot compile
See `config.log' for more details.

The compiler I'm bootstrapping from

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/opt/local/libexec/gcc/x86_64-apple-darwin12/4.7.2/lto-wrapper
Target: x86_64-apple-darwin12
Configured with: ../gcc-4.7.2/configure --prefix=/opt/local --build=x86_64-apple-darwin12 --enable-languages=c,c++,objc,obj-c++,lto,fortran,java --libdir=/opt/local/lib/gcc47 --includedir=/opt/local/include/gcc47 --infodir=/opt/local/share/info --mandir=/opt/local/share/man --datarootdir=/opt/local/share/gcc-4.7 --with-libiconv-prefix=/opt/local --with-local-prefix=/opt/local --with-system-zlib --disable-nls --program-suffix=-mp-4.7 --with-gxx-include-dir=/opt/local/include/gcc47/c++/ --with-gmp=/opt/local --with-mpfr=/opt/local --with-mpc=/opt/local --with-ppl=/opt/local --with-cloog=/opt/local --enable-cloog-backend=isl --disable-cloog-version-check --enable-stage1-checking --enable-lto --enable-libstdcxx-time --with-as=/opt/local/bin/as --with-ld=/opt/local/bin/ld --with-ar=/opt/local/bin/ar --with-bugurl=https://trac.macports.org/newticket --disable-ppl-version-check --with-pkgversion='MacPorts gcc47 4.7.2_2+universal'
Thread model: posix
gcc version 4.7.2 (MacPorts gcc47 4.7.2_2+universal) 

Question

Is there a step I missed in setting up the build environment of coreutils or gcc such that this error results?

What else can I troubleshoot in trying to resolve this error? My Google searches on this error message are not bearing much fruit.


Changes

I changed the build script as follows:

#!/bin/bash -x

WORK_DIR=$PWD
INSTALL_DIR=$WORK_DIR/gcc-4.7.2-darwin
MIN_OSX_VERSION=10.5

if [ -d "$INSTALL_DIR" ]; then
    echo "Directory $INSTALL_DIR already exists";
    exit 1
fi

BINUTILS=binutils-2.23.2
TARBALL_BINUTILS=$BINUTILS.tar.bz2
URL_BINUTILS=http://ftp.gnu.org/gnu/binutils/$TARBALL_BINUTILS
SOURCE_BINUTILS_DIR=$WORK_DIR/$BINUTILS
BUILD_BINUTILS_DIR=$WORK_DIR/build-$BINUTILS

GCC=gcc-4.7.2
TARBALL_GCC=$GCC.tar.bz2
URL_GCC=http://ftp.gnu.org/pub/gnu/gcc/$GCC/$TARBALL_GCC
SOURCE_GCC_DIR=$WORK_DIR/$GCC
BUILD_GCC_DIR=$WORK_DIR/build-$GCC

curl -O $URL_BINUTILS
mkdir -p $SOURCE_BINUTILS_DIR
tar -xjvf $TARBALL_BINUTILS
rm $TARBALL_BINUTILS
mkdir -p $BUILD_BINUTILS_DIR
cd $BUILD_BINUTILS_DIR
$SOURCE_BINUTILS_DIR/configure --prefix=$INSTALL_DIR
make -j $(sysctl -n hw.availcpu) all CFLAGS='-Wno-error' && make install

find $INSTALL_DIR -name '*strip*' -delete

cd $WORK_DIR

curl -O $URL_GCC
mkdir -p $SOURCE_GCC_DIR
tar -xvjf $TARBALL_GCC
rm $TARBALL_GCC
cd $SOURCE_GCC_DIR
sed -i '' -e 's/wget/curl -O/g' $SOURCE_GCC_DIR/contrib/download_prerequisites
$SOURCE_GCC_DIR/contrib/download_prerequisites
cd $WORK_DIR
mkdir -p $BUILD_GCC_DIR
cd $BUILD_GCC_DIR
$SOURCE_GCC_DIR/configure --prefix=$INSTALL_DIR --enable-languages=c,c++
make -j $(sysctl -n hw.availcpu) CFLAGS="-mmacosx-version-min=$MIN_OSX_VERSION" && make install

find $INSTALL_DIR -name '*.a' -print0 | xargs -0 -n1 ranlib

rm -Rf $BUILD_GCC_DIR
rm -Rf $BUILD_BINUTILS_DIR
rm -Rf $SOURCE_GCC_DIR
rm -Rf $SOURCE_BINUTILS_DIR

The ranlib step was necessary due to numerous errors during the process of building a library.

To build an i386 binary, I had to do the following with my Makefile:

ARCH                 = i386
...
GCCDIR               = ${PARTY3}/gcc-4.7.2-darwin
GCCBINDIR            = ${GCCDIR}/bin
GCCLIBDIR            = ${GCCDIR}/lib
GCCINCDIR            = ${GCCDIR}/include
...
LIBCPP               = libgcc_eh.a
LIBCXX               = libstdc++.a
...
INCLUDES             = -I${GCCINCDIR} ...
...
CC                   = ${GCCBINDIR}/gcc
CXX                  = ${GCCBINDIR}/g++
...
GCCCPPLIBPATH        = ${GCCLIBDIR}/gcc/x86_64-apple-darwin12.3.0/4.7.2/${ARCH}/${LIBCPP}
GCCCXXLIBPATH        = ${GCCLIBDIR}/${ARCH}/${LIBCXX}
...
FLAGS                = ${SFLAGS} ${NONSTATICFLAGS} ${OBJS} -L${GCCLIBDIR} ...
...
run: dependencies
    mkdir -p ${BIN} && ${CXX} -o ${BIN}/${PROG}_${ARCH} ${FLAGS} -static-libgcc -mmacosx-version-min=${MIN_OSX_VERSION} -arch ${ARCH} ... ${GCCCPPLIBPATH} ${GCCCXXLIBPATH} ${SOURCE1}

Summary of Makefile changes:

  1. I had to add the -static-libgcc flag and leave out -static-libstdc++, which had no effect (except to cause the build to fail, until I ran ranlib on the static libraries, which fixed the build failure but had no effect on -static-libstdc++).

  2. I added ${GCCCPPLIBPATH} and ${GCCCXXLIBPATH} to the run target, which each point to the static C anc C++ libraries libgcc_eh.a and libstdc++.a, respectively.

This allowed me to compile a working i386 (32-bit) binary, but the same GCC installation failed to build a working x86_64 (64-bit) binary.

Lialiabilities answered 27/3, 2013 at 18:24 Comment(1)
I haven't built that target of compiler, but those options xgcc is failing with look like they're for e.g. IBM's xlc, I.e. is the configure script is confused about what compiler it is using? I would start investigation by tracing back what configure line generated the error, and trying to understand how that configure line was generated.Naominaor
A
4

After thinking about this a little, it occurs to me that by far the easiest solution would be to compile GCC for x86_64, and then to just pass -m32 when you wish to build for i386. Is that not sufficient for your needs for some reason? Building in this manner is dramatically simpler, and doesn't require any of the madness described below. Doing the following sequence of events results in a working compiler that will spit out i386 binaries when requested:

WORK_DIR=$PWD
INSTALL_DIR=$WORK_DIR/install

curl -O http://ftp.gnu.org/gnu/binutils/binutils-2.23.2.tar.bz2
tar xjvf binutils-2.23.2.tar.bz2
mkdir build-binutils && cd build-binutils
../binutils-2.23.2/configure --prefix=$INSTALL_DIR
make -j $(sysctl -n hw.availcpu) all CFLAGS='-Wno-error' && make install

find $INSTALL_DIR -name '*strip*' -delete

cd $WORK_DIR

curl -O http://ftp.gnu.org/pub/gnu/gcc/gcc-4.7.2/gcc-4.7.2.tar.bz2
tar xvjf gcc-4.7.2.tar.bz2
cd gcc-4.7.2
sed -i '' -e 's/wget/curl -O/g' ./contrib/download_prerequisites
./contrib/download_prerequisites
cd ..
mkdir build-gcc && cd build-gcc
../gcc-4.7.2/configure --prefix=$INSTALL_DIR --enable-languages=c,c++
make -j $(sysctl -n hw.availcpu) && make install

Once this completes you can see it in action like so:

mrowe@falcon:~/tmp$ cat > test.cpp
#include <iostream>

int main(int argc, char **argv) {
    std::cerr << "Built with GCC 4.7" << std::endl;
    return 0;
}

^D
mrowe@falcon:~/tmp$ ./install/bin/g++ -o test test.cpp 
mrowe@falcon:~/tmp$ file test
test: Mach-O 64-bit executable x86_64
mrowe@falcon:~/tmp$ ./install/bin/g++ -m32 -static-libgcc -static-libstdc++ -o test test.cpp 
mrowe@falcon:~/tmp$ file test
test: Mach-O executable i386
mrowe@falcon:~/tmp$ otool -L ./test
./test:
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1192.0.0)
mrowe@falcon:~/tmp$ ./test 
Built with GCC 4.7
mrowe@falcon:~/tmp$ 

If you see warnings like:

ignoring file …/4.7.2/i386/libgcc.a, file was built for archive which is not the architecture being linked (i386)

You can fix them by running ranlib on the static libraries:

find $INSTALL_DIR -name '*.a' -print0 | xargs -0 -n1 ranlib

Below is my attempt at helping you along the path you were attempting to follow. I don't think it's the ideal approach and it is fraught with problems.

The error you reported seeing is due to your GCC build trying to use the GNU assembler from binutils when the generated assembly is intended to be assembled with the GNU assembler from Apple's cctools. The dialect of assembly that is supported differs between the two as Apple's assembler diverged from the upstream GNU assembler many years ago.

All of the instructions I was able to find for building cctools were out of date so I experimented until I got something working. I did something close to the following:

curl -O http://opensource.apple.com/tarballs/cctools/cctools-829.tar.gz
tar xvzf cctools-829.tar.gz
cd cctools-829

You'll then need to patch two files in order to build without a dependency on LLVM:

diff -ru cctools-829/libstuff/lto.c cctools-829.modified/libstuff/lto.c
--- cctools-829/libstuff/lto.c  1969-12-31 16:00:00.000000000 -0800
+++ cctools-829.modified/libstuff/lto.c 2013-04-05 03:18:34.000000000 -0700
@@ -376,4 +376,18 @@
        lto_dispose(mod);
 }

+#else 
+
+#include <stdlib.h>
+
+__private_extern__
+int
+is_llvm_bitcode(
+                struct ofile *ofile,
+                char *addr,
+                size_t size)
+{
+    return 0;
+}
+
 #endif /* LTO_SUPPORT */
diff -ru cctools-829/misc/libtool.c cctools-829.modified/misc/libtool.c
--- cctools-829/misc/libtool.c  1969-12-31 16:00:00.000000000 -0800
+++ cctools-829.modified/misc/libtool.c 2013-04-05 03:20:21.000000000 -0700
@@ -1369,8 +1369,11 @@
                    }
                }
                 }
-                else if(ofiles[i].arch_type == OFILE_Mach_O ||
-                        ofiles[i].arch_type == OFILE_LLVM_BITCODE){
+                else if(ofiles[i].arch_type == OFILE_Mach_O
+#ifdef LTO_SUPPORT
+                            || ofiles[i].arch_type == OFILE_LLVM_BITCODE
+#endif
+                            ){
                if(cmd_flags.ranlib == TRUE){
                    error("for architecture: %s file: %s is not an "
                          "archive (no processing done on this file)",

And then build:

make install USE_DEPENDENCY_FILE=NO BUILD_DYLIBS=NO LTO= TRIE= DSTROOT=../build-cctools

And install the necessary parts of by hand:

cd ..
cp build-cctools/usr/bin/as i386-apple-gcc/bin/i386-apple-darwin-as
cp build-cctools/usr/bin/as i386-apple-gcc/i386-apple-darwin/bin/as
cp build-cctools/usr/bin/lipo i386-apple-gcc/bin/i386-apple-darwin-lipo
cp build-cctools/usr/bin/lipo i386-apple-gcc/i386-apple-darwin/bin/lipo
cp build-cctools/usr/bin/strip i386-apple-gcc/bin/i386-apple-darwin-strip
cp build-cctools/usr/bin/strip i386-apple-gcc/i386-apple-darwin/bin/strip

If we then attempt to build GCC once more we'll discover that the build process is upset because our freshly-built assembler is unable to target x86_64. Since that doesn't seem relevant to what you're trying to achieve you can add the --disable-multilib argument to GCC's configure script to prevent it from trying that.

At this point I ran in to errors while attempting to build libgcc. These errors are because a suitable linker cannot be found, so I guess that now you'd need to download and build ld64 too.

And it's at this point that I've run out of time to make it any further. Good luck!

Axenic answered 5/4, 2013 at 11:38 Comment(23)
Passing -m32 isn't sufficient because I am trying to build static, 32-bit versions of GNU's C and C++ libraries, which I can link into 32-bit binaries. The -m32 option does not do that.Lialiabilities
Perhaps I'm misunderstanding, but after building I end up with $PREFIX/lib/i386/libstdc++.a which would appear to be the 32-bit version of the GNU C++ library. All of the other GCC support libraries are also built for 32-bit as well as 64-bit.Axenic
I'll give your method a shot to see if I can replicate it on my end. Thanks for adding all this detail.Lialiabilities
Is your patchfile incomplete? I get the following error when I attempt to apply it: patching file misc/libtool.c - patch unexpectedly ends in middle of line - Hunk #1 FAILED at 1369. - 1 out of 1 hunk FAILED -- saving rejects to file misc/libtool.c.rejLialiabilities
I also tried a second, separate attempt, where I used llvm-gcc42 and added CFLAGS='-m32' to the ./configure statement. The make step failed about 20 minutes into compiling.Lialiabilities
Can you clarify why it is that you think the regular 64-bit build, without any special options, won't do what you want? As I mentioned above it defaults to building a compiler and support libraries that can target both 32- and 64-bit. It doesn't require building cctools since it will happily use your system versions. The only remotely non-standard thing I had to do when trying that approach was to delete the binutils version of strip from the install prefix prior to configuring GCC so that the system version would be used.Axenic
It's not that I don't think it will do what I want, it's that it won't. Or building it doesn't work using either the Xcode developer tools (LLVM GCC 4.2) or with MacPorts GCC 4.7.2. The make step will leave with a fatal error at some point during the build. In the description I gave above, it takes about an hour of compiling before it quits with a fatal error.Lialiabilities
I'd suggest trying your steps above without the --target=i386-apple-darwin option to the two configure scripts. That's precisely what I tried after messing around with cctools and ld64 for a while, and it worked without any of the hassle. Another hint that may be useful is to build with make -j $(sysctl -n hw.availcpu) to ensure that all of your cores will be used, and perhaps even configure GCC with --enable-languages=c,c++ so that it doesn't spend a ton of time on building Fortran and Java support.Axenic
Without specifying the target, I get 64-bit binaries, e.g.:$ lipo -info ../third-party/binutils/bin/ar Non-fat file: ../third-party/binutils/bin/ar is architecture: x86_64Lialiabilities
Targeting 32-bit requires only that the tools support generating 32-bit code, not that they themselves run as 32-bit. Check the output of ar --help to see the list of targets that it supports. It'll look something like supported targets: mach-o-x86-64 mach-o-i386 …, indicating that it can indeed target i386.Axenic
I just updated my answer to include the full steps I used to build GCC 4.7 and confirm that it can generate i386 binaries.Axenic
I was able to compile GCC 4.7.2, but, as I noticed before, the static libraries are 64-bit: lipo -info ../third-party/gcc-4.7.2/lib/libstdc++.a input file ../third-party/gcc-4.7.2/lib/libstdc++.a is not a fat file Non-fat file: ../third-party/gcc-4.7.2/lib/libstdc++.a is architecture: x86_64Lialiabilities
Try looking at lib/i386/libstdc++.a rather than lib/libstdc++.a.Axenic
Thanks for your help. Okay, I'm going to try a clean build of GCC 4.7.2 from a vanilla setup of 10.8 + Xcode developer tools (LLVM-GCC 4.2). If that compiles, then I'll try building an app using -m32, --static-libgcc and --static-libstdc++ flags. If lipo and otool -L confirm that the resulting binary is i386 and links to the static GCC C/C++ libraries, then I'll give you the points.Lialiabilities
This worked — thank you. However, I had to make some changes that I'm unsure are correct, or have side-effects I'm unsure of. I will write a follow-up in my question later tonight or tomorrow which explains what changes I had to make, in order to build a static i386 binary from a mix of C and C++ code.Lialiabilities
This almost worked. I can make i386 static binaries which appear to work properly, but even with the changes needed, x86_64 static binaries quit with an Abort trap: 6 error. If I remove the -static-libgcc and -static-libstdc++ flags, then I get a working 64-bit binary, but that binary unfortunately uses dynamic libraries. So I'm close, but as I can't make both 32- and 64-bit binaries, I'm stuck.Lialiabilities
What program is aborting for you when compiled for x86_64 with a static libgcc / libstdc++? The trivial program that I posted above runs just fine for me when built for either i386 and x86_64.Axenic
It may also be worth asking a new question for this x86_64 issue given that this question was initially about building a compiler that could target i386 only.Axenic
The code for the program I'm compiling is too large to post into this or another question. I had to make some changes to the build process (both to gcc and mostly my binary) just to get a i386 binary to compile. It takes about two hours to compile gcc alone. I'm wondering if I'm tilting at windmills...Lialiabilities
It seems like the static GCC runtime libraries on OS X aren't particularly well-supported. MacPorts and the like don't appear to even try making them work. I'd love to help further but I'm out of suggestions without the ability to debug the problem. I hope you do find some way to make it work!Axenic
I think I'm just going wave my white flag of surrender at Cupertino and bundle dylibs in an installer. Not as elegant as standalone binaries, and more fragile, but you win this one, Apple!Lialiabilities
I'd appreciate if you could mark my answer as accepted since we seem to have wrapped things up. It's worth noting that an installer isn't necessary for bundling dylibs unless you have particularly special requirements. In most cases you can just ship them inside the .app wrapper of your application.Axenic
These are command-line tools. An .app bundle is not a very convenient way to use them because open doesn't play too well with command-line arguments.Lialiabilities

© 2022 - 2024 — McMap. All rights reserved.