Building GCC for Solaris 2.5.1 (i386) in 10 Easy Steps
Having installed Solaris 2.5.1 in VirtualBox, I happened to recall the unfortunate fact that the Solaris of that era doesn't ship with a C compiler. Of course, you could always buy Sun's compiler, but in this day and age I haven't been able to find a copy, and I doubt Oracle would be willing to sell it to me. Thus, I set about to build one. Not the simplest task, as evidenced by the length of this post. Thank God Sun ships the C library, headers, and basic binary tools at least. :P
In this article, I cover the steps necessary to build the toolchain and build / package software under Solaris.
Let's get started...
Patches
Several patches are necessary in order to get the toolchain built and working. They can be downloaded here. The sources were otherwise unmodified.
Conventions
At each step, some commands will have to be run on your host, which I
assume will be Linux, and some commands will have to be run on Solaris.
The commands run on Linux will always begin with a bash-style user@host
string before the prompt. The commands which should be run on Solaris
will simply start with the prompt, as you'd expect.
The Solaris box has the hostname solaris251
, and our host has
the hostname cid
.
The steps outlined here should also work for sparc Solaris 2.5.1 also,
as long as you're using the proper target
.
Initial Steps
You'll need to have the Solaris development packages installed. They should be included with your install media. Ensure you have at least the following packages installed. In Solaris 2.5.1 installing the "Development" system should include these packages.
- SUNWhea
- SUNWarc
- SUNWbtool
- SUNWlibm
Make a directory to hold the toolchain. Run the following command as
root
:
root@cid # mkdir -p /opt/cross-solaris-2.5.1
Now, you'll need to figure out your host tuple. On my machine, this is
x86_64-pc-linux-gnu
. To figure this out, just ask GCC what it is:
tim@cid $ gcc -v 2>&1 | grep 'Target:' Target: x86_64-pc-linux-gnu
We'll be passing the host tuple to configure
so that autoconf knows
what type of system will host the toolchain.
NOTE: Commands preceeded by a # should be run as root
.
Step 1: Get the Solaris headers and libc
You'll need to get the system headers and libraries. First, make an archive of them:
tim@cid $ telnet solaris251 Trying solaris251... Connected to solaris251. Escape character is '^]'. UNIX(r) System V Release 4.0 (solaris251) login: tim Password: Last login: Thu Jul 16 02:29:14 from 10.100.0.2 Sun Microsystems Inc. SunOS 5.5.1 Generic May 1996 $ su Password: # tar -cf dev.tar /usr/ccs/lib/.[ao] /usr/include /usr/lib/.a /usr/lib/.so # exit $ exit Connection closed by foreign host.
Second, we need to download and extract the archive:
tim@cid $ ftp solaris251 Connected to solaris251 (10.100.0.3). 220 solaris251 FTP server (UNIX(r) System V Release 4.0) ready. Name (solaris251:tim): tim 500 'AUTH SSL': command not understood. SSL not available 331 Password required for tim. Password: 230 User tim logged in. ftp> bin 200 Type set to I. ftp> get dev.tar local: dev.tar remote: dev.tar 200 PORT command successful. 150 Binary data connection for dev.tar (10.100.0.2,60471) (11768320 bytes). 226 Binary Transfer complete. 11768320 bytes received in 0.488 secs (2.3e+04 Kbytes/sec) ftp> quit 221 Goodbye.
Extract it (using a tar
that ignores leading slashes, like GNU tar),
tim@cid $ su Password: root@cid # tar -C /opt/cross-solaris-2.5.1 -xf dev.tar root@cid # exit
Step 2: Setup PATH
In order to prevent repeatedly settings the path, as we'll often be
switching back and fourth from our normal user to root
on the Solaris
box, let's go ahead and add the paths we need to our .profile
.
tim@cid $ telnet solaris251 user: ... $ su Password: # touch /.profile # echo "PATH=/usr/local/bin:/usr/ccs/bin:/usr/sbin" >> /.profile # echo "export PATH" >> /.profile # exit $ touch $HOME/.profile $ echo "PATH=/usr/local/bin:/usr/ccs/bin:/usr/bin" >> $HOME/.profile $ echo "export PATH" >> $HOME/.profile
Step 3: Binutils 2.25
First, you'll need to download the binutils source.
You can build it as follows:
tim@cid $ tar -xjf binutils-2.25.tar.bz2 tim@cid $ patch -p0 < binutils-2.25.patch tim@cid $ mkdir binutils-build && cd binutils-build tim@cid $ ../binutils-2.25/configure --prefix=/opt/cross-solaris-2.5.1 \ --with-sysroot=/opt/cross-solaris-2.5.1 \ --build=x86_64-pc-linux-gnu --target=i386-pc-solaris2.5.1 \ --disable-gold --disable-libstdcxx --disable-libssp --disable-nls tim@cid $ make tim@cid $ su Password: root@cid # make install root@cid # exit tim@cid $ cd .. && rm -rf binutils-build
Step 4: Cross-GCC 3.4.6
Next, we'll build a copy of GCC that can generate code for our Solaris box. You'll need to download the GCC source.
tim@cid $ export PATH=$PATH:/opt/cross-solaris-2.5.1/bin tim@cid $ tar -xjf gcc-3.4.6.tar.bz2 tim@cid $ patch -p0 < gcc-3.4.patch ; cd .. tim@cid $ mkdir gcc-build && cd gcc-build tim@cid $ ../gcc-3.4.6/configure \ --prefix=/opt/cross-solaris-2.5.1 --with-sysroot=/opt/cross-solaris-2.5.1 \ --build=x8664-pc-linux-gnu --host=x8664-pc-linux-gnu\ --target=i386-pc-solaris2.5.1 --with-gnu-ld --with-gnu-as\ --enable-languages=c --enable-threads=posix --disable-multilib\ --disable-nls --disable-shared tim@cid $ make tim@cid $ sed -ie 's@/usr/ccs@../ccs@g' gcc/specs tim@cid $ su Password: root@cid # export PATH=$PATH:/opt/cross-solaris-2.5.1/bin root@cid # make install root@cid # exit tim@cid $ cd .. && rm -rf gcc-build
Now, let's test our compiler:
tim@cid $ i386-pc-solaris2.5.1-gcc -v Reading specs from /opt/cross-solaris-2.5.1/lib/gcc/i386-pc-solaris2.5.1/3.4.6/specs Configured with: ../gcc-3.4.6/configure --prefix=/opt/cross-solaris-2.5.1 --with-sysroot=/opt/cross-solaris-2.5.1 --build=x8664-pc-linux-gnu --host=x8664-pc-linux-gnu --target=i386-pc-solaris2.5.1 --with-gnu-ld --with-gnu-as --enable-languages=c --enable-threads=posix --disable-multilib --disable-nls --disable-shared Thread model: posix gcc version 3.4.6
Next, we'll to cross-compile this simple test program.
#include <stdio.h>
int main(int argc, char *argv[]) { puts("Hello, world!"); return 0; }
Here's how we build it, and test it on our Solaris box:
tim@cid $ i386-pc-solaris2.5.1-gcc -O2 -Wl,--no-check-sections -static -o hello hello.c tim@cid $ file hello hello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped tim@cid $ ftp solaris251 ... ftp> bin 200 Type set to I. ftp> put hello local: hello remote: hello 200 PORT command successful. 150 Binary data connection for hello (10.100.0.2,44065). 226 Transfer complete. 58964 bytes sent in 0.000199 secs (2.9e+05 Kbytes/sec) ftp> quit 221 Goodbye. tim@cid $ telnet solaris251 ... UNIX(r) System V Release 4.0 (solaris251) login: tim Password: Last login: Thu Jul 16 04:19:21 from 10.100.0.2 Sun Microsystems Inc. SunOS 5.5.1 Generic May 1996 $ chmod +x hello $ ./hello Hello, world! $ exit
Step 5: Bootstrap GCC 3.4.6
Now we get to the tricky part. Cross-compiling GCC itself to produce a bootstrap compiler for building GCC on Solaris. We'll use the linker provided by Solaris, but we'll need the GNU assembler.
tim@cid $ mkdir cross-gcc-build && cd cross-gcc-build tim@cid $ CFLAGS="-Wl,--no-check-sections -static" ../gcc-3.4.6/configure \ --prefix=/opt/cross-solaris-2.5.1/opt/gcc \ --with-sysroot=/opt/cross-solaris-2.5.1 \ --build=x86_64-pc-linux-gnu --host=i386-pc-solaris2.5.1 \ --target=i386-pc-solaris2.5.1 --with-gnu-as \ --with-ld=/usr/ccs/bin/ld --with-as=/opt/gcc/bin/as \ --enable-languages=c --enable-threads=posix --disable-multilib \ --disable-nls --disable-shared tim@cid $ make tim@cid $ su Password: root@cid # export PATH=$PATH:/opt/cross-solaris-2.5.1/bin root@cid # make install
Step 6: Cross-compile binutils 2.25 (gas only)
The assembler Solaris has sucks, and happens to lack some instructions used by gcc when optimizing. Thus, we'll need to make sure we have the GNU assembler handy.
tim@cid $ mkdir cross-binutils-build && cd cross-binutils-build tim@cid $ LDFLAGS="-Wl,--no-check-sections -Wl,-Bstatic" \ CFLAGS="-O2 -I/opt/cross-solaris-2.5.1/usr/include" \ ../binutils-2.25/configure --prefix=/opt/cross-solaris-2.5.1/opt/gcc \ --with-sysroot=/opt/cross-solaris-2.5.1 --build=x86_64-pc-linux-gnu \ --host=i386-pc-solaris2.5.1 --target=i386-pc-solaris2.5.1 \ --disable-nls --disable-libstdcxx --disable-plugins --disable-gold \ --disable-libssp tim@cid $ make tim@cid $ su Password: root@cid # export PATH=$PATH:/opt/cross-solaris-2.5.1/bin root@cid # make -C gas install
Step 7: Test the bootstrap compiler
If tbe above steps were successful, you now have a Solaris-hosted GCC in
/opt/cross-solaris-2.5.1/opt/gcc
! We'll need to tar this up, and
transfer it to the Solaris machine like so:
root@cid # cd /opt/cross-solaris-2.5.1 root@cid # tar -cf solaris-gcc-3.4.6.tar opt/gcc root@cid # ftp solaris251 ftp> bin ftp> put solaris-gcc-3.4.6.tar ftp> quit root@cid # exit tim@cid $ ftp solaris251 ftp> ascii ftp> put hello.c ftp> quit tim@cid $ telnet solaris251 user: ... $ su Password: # tar -xf solaris-gcc-3.4.6.tar # mv opt/gcc /opt/gcc && rm -rf opt # exit $ PATH=/opt/gcc/bin:$PATH $ export PATH $ gcc -v Reading specs from /opt/gcc/bin/../lib/gcc/i386-pc-solaris2.5.1/3.4.6/specs Configured with: ../gcc-3.4.6/configure --prefix=/opt/cross-solaris-2.5.1/opt/gcc --with-sysroot=/opt/cross-solaris-2.5.1 --build=x86_64-pc-linux-gnu --host=i386-pc-solaris2.5.1 --target=i386-pc-solaris2.5.1 --with-gnu-as --with-ld=/usr/ccs/bin/ld --with-as=/opt/gcc/bin/as --enable-languages=c --enable-threads=posix --disable-multilib --disable-nls --disable-shared Thread model: posix gcc version 3.4.6
Since we used --with-sysroot
during our cross-compile, we need to
create a symlink to mimic our setup on the Solaris box so that GCC can
find our headers and libraries.
$ su Password: # mkdir -p /opt && ln -sf / cross-solaris-2.5.1 # exit $
Now, let's build our hello world program:
$ gcc -O2 -o hello hello.c $ ./hello Hello, world!
Success! We can now compile C code on our target.
Step 8: Build GNU Make
Sun's make
may not be suitable for building GCC, thus we'll need to
use our almighty cross-compiled compiler to build
GNU Make first.
$ bzip2 -d make-4.1.tar.bz2 $ ftp solaris251 ftp> bin ftp> put make-4.1.tar ftp> quit $ telnet solaris251 user: ... $ PATH=/opt/gcc/bin:$PATH $ export PATH $ tar -xf make-4.1.tar $ cd make-4.1 $ ./configure --prefix=/usr/local --disable-nls $ make $ su Password: # . /.profile # make install # exit
Now, let's make sure we can execute it:
$ /usr/local/bin/make -v GNU Make 4.1 Built for i386-pc-solaris2.5.1 Copyright (C) 1988-2014 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
Step 9: Build GNU tar
The tar
provided on Solaris always gives me issues with the tarred
GCC source code. GNU tar
will get the job done.
$ tar -xjf tar-1.28.tar.bz2 $ patch -p0 < tar-1.28.patch $ tar -cf tar-1.28.tar tar-1.28 $ ftp solaris251 ftp> bin ftp> put tar-1.28.tar ftp> quit $ telnet solaris251 user: ... $ tar -xf tar-1.28.tar $ cd tar-1.28 $ PATH=/opt/gcc/bin:$PATH $ export PATH $ ./configure --prefix=/usr/local --without-xattrs --without-selinux \ --without-posix-acls $ make $ su Password: # . /.profile # make install # exit $ /usr/local/bin/tar --version tar (GNU tar) 1.28 Copyright (C) 2014 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Written by John Gilmore and Jay Fenlason. $
Step 10: Build GCC on Solaris
Now, let's build our original objective, now that all the other stuff is out of the way.
tim@cid $ tar -cf gcc-3.4.6.tar gcc-3.4.6 tim@cid $ ftp solaris251 ftp> bin ftp> put gcc-3.4.6.tar ftp> quit tim@cid $ telnet solaris251 user: ... $ /usr/local/bin/tar -xf gcc-3.4.6.tar
Copy the assembler:
$ su # cp /opt/gcc/bin/as /usr/local/bin/as # exit
and build GCC:
$ PATH=/opt/gcc/bin:$PATH $ export PATH $ mkdir gcc-build && cd gcc-build $ ../gcc-3.4.6/configure --prefix=/usr/local \ --with-gnu-as --with-ld=/usr/ccs/bin/ld --with-as=/usr/local/bin/as \ --enable-languages=c,c++ --enable-threads=posix --disable-multilib \ --disable-nls $ make $ su Password: # . /.profile # make install # exit $ cd ..
Now, let's test our compiler:
$ gcc -O2 -o hello hello.c $ ./hello Hello, World! $
Now we can purge the bootstrap compiler:
$ su Password: # rm -rf /opt/gcc # rm -f /opt/cross-solaris-2.5.1
Finally, we have a working compiler built in the target environment.
Bonus: Building gdb on Solaris
Let's install gdb also,
so that we can have something a bit better than adb
. Be sure to
generate a tar file just like we did for tar
.
Then upload and build it as follows:
tim@cid $ telnet solaris251 user: ... $ tar -xf gdb-7.9.tar ; cd gdb-7.9 $ MAKEINFO=false ./configure --prefix=/usr/local --disable-nls \ --disable-plugins --disable-gold --disable-libssp --enable-threads=posix $ make # . /.profile # make install # exit $ cd .. ; rm -rf gdb-7.9
Now, let's test it:
$ gcc -g -O2 -o hello hello.c $ gdb hello GNU gdb (GDB) 7.9 Copyright (C) 2015 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "i386-pc-solaris2.5.1". Type "show configuration" for configuration details. For bug reporting instructions, please see: http://www.gnu.org/software/gdb/bugs/. Find the GDB manual and other documentation resources online at: http://www.gnu.org/software/gdb/documentation/. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from hello...done. (gdb) r Starting program: /export/home/tim/hello Hello, world! Inferior 1 (process 26462) exited normally b main Breakpoint 1 at 0x804896e: file hello.c, line 4. (gdb) r Starting program: /export/home/tim/hello Breakpoint 1, main (argc=1, argv=0x8047e5c) at hello.c:4 4 { (gdb) s 5 puts("Hello, world!"); (gdb) s Hello, world! 7 } (gdb) s 0x08048881 in _start () (gdb) c Continuing. [Inferior 1 (process 26463) exited normally] (gdb) quit $
Looks like the bastard works. :P
Creating Solaris Packages
If you're interested in checking out the source code for the System V packaging tools, or perhaps interested in playing with them on Linux, check out heirloom-pkgtools, which was initially released by Sun as part of OpenSolaris.
For this example, I'll demonstrate building a package for GNU Make using the System V package tools on Solaris.
First, take a "snapshot" of your current /usr/local
.
$ find /usr/local -print | pkgproto > pre
Then, build your package normally, ensure it installs under the
/usr/local
prefix.
Next, take another "snapshot" of /usr/local
, and diff them like so:
$ echo "i pkginfo" > prototype $ find /usr/local -print | pkgproto > post $ diff pre post | grep '^>' | sed -e 's/^> //g' | grep -v '^d' >> prototype
Your prototype file will now look something like this:
i pkginfo f none /usr/local/bin/make 0755 root other f none /usr/local/include/gnumake.h 0644 root other f none /usr/local/share/info/make.info 0644 root other f none /usr/local/share/info/make.info-1 0644 root other f none /usr/local/share/info/make.info-2 0644 root other f none /usr/local/share/man/man1/make.1 0644 root other
We don't need the info files, or gnumake.h, so remove them. After cleaning it up, your file will now look like this:
i pkginfo f none /usr/local/bin/make 0755 root other f none /usr/local/share/man/man1/make.1 0644 root other
Now, you'll want to create a pkginfo file, and upload it. Here's the one I made for GNU Make 4.1:
PKG="GNUmake" NAME="GNU Make" ARCH="i386" VERSION="4.1" CATEGORY="utility" CLASSES="none" VENDOR="Tim Hentenaar" EMAIL="e@ma.il" HOTLINE="http://hentenaar.com"
Most of the pkginfo
fields should be self-explanatory. Next, we make
the package, and transfer it to a package file on our filesystem.
$ su Password: # pkgmk -r / ## Building pkgmap from package prototype file. ## Processing pkginfo file. WARNING: missing directory entry for WARNING: missing directory entry for ... WARNING: parameterset to "solaris251150720132610" ## Attempting to volumize 2 entries in pkgmap. part 1 -- 1850 blocks, 12 entries ## Packaging one part. /var/spool/pkg/GNUmake/pkgmap /var/spool/pkg/GNUmake/pkginfo /var/spool/pkg/GNUmake/root/usr/local/bin/make /var/spool/pkg/GNUmake/root/usr/local/share/man/man1/make.1 ## Validating control scripts. ## Packaging complete. # pkgtrans -s /var/spool/pkg /opt/packages/GNUmake.pkg GNUmake Transferring package instance # ls -la /opt/packages total 1828 drwxr-xr-x 2 root other 512 Jul 20 13:40 . drwxrwxr-x 6 root sys 512 Jul 20 13:38 .. -rw-r--r-- 1 root other 924672 Jul 20 13:40 GNUmake.pkg # rm -rf /var/spool/pkg/GNUmake
Now we have our package as a datastream (file) in /opt/packages/GNUmake`.pkg
. Let's test installing it:
# pkgadd -d /opt/packages/GNUmake.pkg The following packages are available: 1 GNUmake GNU Make (i386) 4.1 Select package(s) you wish to process (or 'all' to process all packages). (default: all) [?,??,q]: Processing package instance <GNUmake> from </opt/packages/GNUmake.pkg> GNU Make (i386) 4.1 Tim Hentenaar ## Processing package information. ## Processing system information. ## Verifying disk space requirements. ## Checking for conflicts with packages already installed. ## Checking for setuid/setgid programs. Installing GNU Make 4.1 - i386 as <GNUmake> ## Installing part 1 of 1. /usr/local/bin/make /usr/local <implied directory> /usr/local/bin <implied directory> /usr/local/share/man/man1/make.1 /usr/local/share <implied directory> /usr/local/share/man <implied directory> /usr/local/share/man/man1 <implied directory> [ verifying class <none> ] Installation of <GNUmake> was successful.
You'll likely see some warnings about conflicting files, however I had removed the files that would've been installed by the package before installing it just to show how the installation would appear were the package not installed.
Now we can download the package, and distribute it.
Removing Solaris Packages
You can remove an installed package by:
# pkgrm GNUmake The following package is currently installed: GNUmake GNU Make (i386) 4.1 Do you want to remove this package? y ## Removing installed package instance <GNUmake> ## Verifying package dependencies. ## Processing package information. ## Removing pathnames in class <none> /usr/local/share/man/man1/make.1 /usr/local/bin/make ## Updating system information. Removal of <GNUmake> was successful.