There’s a recurring question in my life which never seems to find a proper answer:
“Why?”
Thanks to a certain someone on Twitter, I got the itch to get my hands on a PowerPC Mac and while browsing the various wikis and websites about vintage macs, I already had in mind what I was looking for: something small and all-in-one, because I genuinely don’t have any space left in my room and I can’t start putting random stuff around the house.
This meant that I was forced to get a laptop, and a small one at that.
After looking at the differences in specs (and, most importantly, prices) between Powerbooks and iBooks, I decided to try and get my hands on the latest iBook produced (which was from 2005) as it had decent specs, especially being a 12″.
A very rare stroke of luck made me find exactly that, right here near my city, just a 1 hour drive.
The iBook I got was not only in perfect condition, but the battery still managed to hold a respectable charge of 1 hour.
The Mac OS X Tiger 10.4.11 installation was clean and ready to go, with all the updates already done, so it was time to get our hands dirty.
First of all… I’m a lazy bastard and didn’t want to spend a week compiling all the necessary libraries and dependencies to build MDFourier, so what to do?
On modern macOS we have Homebrew which is a package manager similar to Aptitude in Debian/Ubuntu, but is there something similar for PowerPC Macs?
Yes!
Introducing Tigerbrew, a fork of Homebrew made specifically for PowerPC Macs.
Great.
To have it up and running we first have to download and install the latest XCode for Tiger which is XCode 2.5.
We can get it from Apple, but to do that we need an Apple Developer account, which I always refused to make, so I grabbed a copy from the Internet Archive and called it a day.
For those who don’t want to get into trouble, the SHA1 checksum of the DMG is 30884704b0a4b098f02ccbb753958cd5331b8982
.
After that, it was just a matter of following Tigerbrew’s instructions, install the required libraries, git clone
the MDFourier repo and run make
after doing some very minor adjustments in the Makefile
(-Wpedantic
wasn’t recognized in the ancient version of gcc
that shipped with XCode 2.5 and some other minor stuff).
That’s the gist of it and we met our first goal: making the CLI version work.
Of course this produced a binary dynamically linked to our libraries, so we can’t really share it with other people (unless we force those other people to install those libraries as well).
So next up on the list is to make a binary with statically linked libraries and, although I’ve already written an article about it, I need to expand on it a bit because here’s where the real trouble began.
Mac OS X Tiger is old.
Really old.
Ancient.
Gaze at the horror of the GCC and bash versions shipped with the system:
The same applies to the libraries the OS comes with, which means that in order to make a “modern” tool like Tigerbrew work properly, some compromises had to be made.
The biggest offender was installing updated libraries of the ones shipped with the OS as this poses a huge problem: you can’t replace the original libraries because you risk breaking something in the OS, so you have to install them alongside the older ones.
And this is where the pain begins.
When you start statically linking libraries, they are expecting to find certain functions in order to work, but since the OS itself provides some of them, you end up in a situation where the referenced library could end up missing some symbols (or they might be there, but with different names – same result, because the linker doesn’t know).
The first result of trying to make a static binary was a complete disaster.
The compilation failed as I was presented with a huge list of missing symbols, so it was time to start debugging and trying to understand what was happening.
After days of research I finally found out what (one of) the issue was: for reasons unknown, the version of plotutils
installed with Tigerbrew had X11 support enabled by default, even if the default version shouldn’t have (it’s an option).
Now all those missing symbols starting with “X” made sense to me and I remembered that X11 was an optional package in Mac OS X Tiger, so it was time to download the Tiger installation DVD from the Macintosh Garden and install the missing X11 components which put the related libraries in /usr/X11R6
and, thankfully, the static libraries were included as well.
Next step was adding /usr/X11R6
to the Makefile’s library path and add all the necessary X11 libraries to resolve all the missing symbols.
So I did that and yet… there were still two missing symbols:
_inflateReset2
_inflateValidate
After wrapping my head around it for an entire week, I finally realized that “inflate” must be referring to something related to (de)compression, but why?
libz
is part of the OS, why is it missing those two functions?
Because Tigerbrew had installed its own version.
And why has it installed its own version?
Because libpng
required it.
That’s what you get for taking shortcuts instead of doing things the proper way™.
Anyway, enough is enough.
I downloaded the source of libpng
from the official website and compiled it myself forcing it to use the libz
provided by the OS and not the one installed by Tigerbrew.
Too bad that the gcc
version provided with XCode 2.5 is so old that it wasn’t able to compile libpng
correctly, so I had to install a newer compiler from Tigerbrew.
After an hour it finally finished and the last missing symbols were resolved, no more errors, the mdfourier static binary was now fully working and relied only on OS-provided libraries.
After sharing it with other members of the MDFourier group and some vintage mac enthusiasts who generously offered their help, I confirmed that everything was working as intended.
Now that we’ve been able to produce a static binary of mdfourier it’s time to work on that pile of hacks I made known as the MDFourier macOS GUI.
I won’t go into too much detail because the word count for this article at this point is already over 1000, so I’ll just stick with what drove me absolutely mad.
Most of the macOS GUI’s inner workings revolve around bash scripts and the bash
included in Mac OS X Tiger is prehistoric.
As a consequence almost nothing worked, because in 11 years the syntax of certain functions has changed several times (not to mention some functions just didn’t exist, period), which meant that I’d either have to:
- Rewrite everything from scratch not even knowing if I’d ever be able to, due to some missing functions
- Try to somehow use a newer bash interpreter
In the end I decided to partly rewrite just the main launcher so it could work with the stock bash
and then use it to inject a newer one (courtesy of TenFourFox).
I just had to think of a graceful and elegant way to achieve this.
The most obvious way would be to just replace the bash
that comes with Tiger. After all it is old and unsafe, so why not?
Because it might end up breaking something in the OS or other apps that rely on that particular version and its quirks.
Besides, I find it really impolite to force something on the user, which is why I also discarded the idea of slamming it into /usr/local/bin
which is the correct place to install user-provided binaries in *nix systems: after all, why should someone suddenly find a new bash interpreter in their Mac if he/she doesn’t want to?
The proper way to approach this in macOS is to put whatever the application needs to run in ~/Library/Application Support/MDFourier
which is were all the various mdfourier/mdwave/etc. binaries are stored in order for the macOS MDFourier GUI app to work.
But unfortunately there’s another issue: spaces in the path are not admitted in the shebang so we can’t put our new bash interpreter there.
In desperation, I came up with a last minute solution which I’m not particularly fond of.
There’s another place which is meant to store “support files” needed for an application to either keep track of its current state or save temporary files needed for its execution: /tmp
.
This is the standard folder in *nix systems for temporary files and it’s where I decided to copy the new bash interpreter to ensure the correct execution of the application.
Now the launcher, which had to be modified to use Tiger’s ancient bash
, has a new responsibility: check if the new bash interpreter is present in /tmp
and, if it isn’t (it shouldn’t), then copy it there.
And then we can finally launch the rest of the bash scripts and the various wrappers by using the #!/tmp/bashNew
shebang to point those scripts at the new bash interpreter.
Finally, when the user has finished using MDFourier, as the application gets closed, the main script will delete the new interpreter from the /tmp
folder to leave it in a clean state.
Some more minor adjustments to the other scripts and it’s a wrap.
This whole ordeal took me way more than anticipated, I think a month or so.
I’d like to thank all the people that helped out testing and debugging various issues, both from the MDFourier group and vintage Mac enthusiasts on Twitter.
As a whole, I’d rate this experience a solid 2/10 – would not recommend.
Thank you for reading!