diff --git a/v2/content/posts/2023/rust-on-ppc-classic-mac-os/ParamText.jpg b/v2/content/posts/2023/rust-on-ppc-classic-mac-os/ParamText.jpg
new file mode 100644
index 0000000..e82fec8
Binary files /dev/null and b/v2/content/posts/2023/rust-on-ppc-classic-mac-os/ParamText.jpg differ
diff --git a/v2/content/posts/2023/rust-on-ppc-classic-mac-os/dialog-sample.png b/v2/content/posts/2023/rust-on-ppc-classic-mac-os/dialog-sample.png
new file mode 100644
index 0000000..bbfda2a
Binary files /dev/null and b/v2/content/posts/2023/rust-on-ppc-classic-mac-os/dialog-sample.png differ
diff --git a/v2/content/posts/2023/rust-on-ppc-classic-mac-os/index.md b/v2/content/posts/2023/rust-on-ppc-classic-mac-os/index.md
new file mode 100644
index 0000000..ce3af9c
--- /dev/null
+++ b/v2/content/posts/2023/rust-on-ppc-classic-mac-os/index.md
@@ -0,0 +1,451 @@
++++
+title = "Trying to Run Rust on Classic Mac OS"
+date = 2023-02-27T10:06:28+10:00
+
+#[extra]
+#updated = 2023-01-11T21:11:28+10:00
++++
+
+I recently acquired a Power Macintosh 9500/150 and after cleaning it up and
+building a [BlueSCSI] to replace the failed hard drive it's now in a
+semi-operational state. This weekend I thought I'd see if I could build a Mac
+app for it that called some Rust code. This post details my trials and
+tribulations.
+
+
+
+I started by building [Retro68], which is a modernish GCC based toolchain
+that allows cross-compiling applications for 68K and PPC Macs. With Retro68
+built I set up a VM in [SheepShaver] running Mac OS 8.1. Using the LaunchAAPL
+and LaunchAAPLServer tools that come with Retro68 I was able to build the
+sample applications and launch them in the emulated Mac.
+
+With the basic workflow working I set about creating a Rust project that built
+a static library with one very basic exported function. It just returns a
+static [Pascal string] when called.
+
+```rust
+#![no_std]
+#![feature(lang_items)]
+
+use core::panic::PanicInfo;
+
+static MSG: &[u8] = b"\x04Rust";
+
+#[no_mangle]
+pub unsafe extern "C" fn hello_rust() -> *const u8 {
+ MSG.as_ptr()
+}
+
+#[panic_handler]
+fn panic(_panic: &PanicInfo<'_>) -> ! {
+ loop {}
+}
+
+#[lang = "eh_personality"]
+extern "C" fn eh_personality() {}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_msg_is_pascal_string() {
+ assert_eq!(MSG[0], MSG[1..].len().try_into().unwrap());
+ }
+}
+```
+
+Classic Mac OS is not a target that Rust knows about so I created a custom
+target JSON definition named `powerpc-apple-macos.json` based on prior work by
+kmeisthax in [this GitHub discussion](https://github.com/autc04/Retro68/discussions/123#discussioncomment-597268):
+
+```json
+{
+ "arch": "powerpc",
+ "data-layout": "E-m:a-p:32:32-i64:64-n32",
+ "executables": true,
+ "llvm-target": "powerpc-unknown-none",
+ "max-atomic-width": 32,
+ "os": "macosclassic",
+ "vendor": "apple",
+ "target-endian": "big",
+ "target-pointer-width": "32",
+ "linker": "powerpc-apple-macos-gcc",
+ "linker-flavor": "gcc",
+ "linker-is-gnu": true
+}
+```
+
+I was able to build the static library with this cargo invocation:
+
+```
+cargo +nightly build --release -Z build-std=core --target powerpc-apple-macos.json
+```
+
+It's using nightly because it's using unstable features to build `core` and the
+`eh_personality` lang item in the code.
+
+This successfully compiles and produces
+`target/powerpc-apple-macos/release/libclassic_mac_rust.a`
+
+I used the [Dialog sample] from Retro68 as the basis of my Mac app. Here it is
+running prior to Rust integration:
+
+{{ figure(image="posts/2023/rust-on-ppc-classic-mac-os/dialog-sample.png", link="posts/2023/rust-on-ppc-classic-mac-os/dialog-sample.png", alt="Screenshot of SheepShaver running Mac OS 8.1. It shows some Finder windows with a frontmost dialog that has a text label, text field, radio buttons, check box and Quit button.", caption="Dialog Sample") }}
+
+This is my tweaked version of the C file:
+
+```c
+/*
+ Copyright 2015 Wolfgang Thaller.
+
+ This file is part of Retro68.
+
+ Retro68 is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Retro68 is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Retro68. If not, see .
+*/
+
+#include
+#include
+#include
+
+#ifndef TARGET_API_MAC_CARBON
+ /* NOTE: this is checking whether the Dialogs.h we use *knows* about Carbon,
+ not whether we are actually compiling for Cabon.
+ If Dialogs.h is older, we add a define to be able to use the new name
+ for NewUserItemUPP, which used to be NewUserItemProc. */
+
+#define NewUserItemUPP NewUserItemProc
+#endif
+
+extern ConstStringPtr hello_rust(void);
+
+pascal void ButtonFrameProc(DialogRef dlg, DialogItemIndex itemNo)
+{
+ DialogItemType type;
+ Handle itemH;
+ Rect box;
+
+ GetDialogItem(dlg, 1, &type, &itemH, &box);
+ InsetRect(&box, -4, -4);
+ PenSize(3,3);
+ FrameRoundRect(&box,16,16);
+}
+
+int main(void)
+{
+#if !TARGET_API_MAC_CARBON
+ InitGraf(&qd.thePort);
+ InitFonts();
+ InitWindows();
+ InitMenus();
+ TEInit();
+ InitDialogs(NULL);
+#endif
+ DialogPtr dlg = GetNewDialog(128,0,(WindowPtr)-1);
+ InitCursor();
+ SelectDialogItemText(dlg,4,0,32767);
+
+ ConstStr255Param param1 = hello_rust();
+
+ ParamText(param1, "\p", "\p", "\p");
+
+ DialogItemType type;
+ Handle itemH;
+ Rect box;
+
+ GetDialogItem(dlg, 2, &type, &itemH, &box);
+ SetDialogItem(dlg, 2, type, (Handle) NewUserItemUPP(&ButtonFrameProc), &box);
+
+ ControlHandle cb, radio1, radio2;
+ GetDialogItem(dlg, 5, &type, &itemH, &box);
+ cb = (ControlHandle)itemH;
+ GetDialogItem(dlg, 6, &type, &itemH, &box);
+ radio1 = (ControlHandle)itemH;
+ GetDialogItem(dlg, 7, &type, &itemH, &box);
+ radio2 = (ControlHandle)itemH;
+ SetControlValue(radio1, 1);
+
+ short item;
+ do {
+ ModalDialog(NULL, &item);
+
+ if(item >= 5 && item <= 7)
+ {
+ if(item == 5)
+ SetControlValue(cb, !GetControlValue(cb));
+ if(item == 6 || item == 7)
+ {
+ SetControlValue(radio1, item == 6);
+ SetControlValue(radio2, item == 7);
+ }
+ }
+ } while(item != 1);
+
+ FlushEvents(everyEvent, -1);
+ return 0;
+}
+```
+
+And this is the resource file (`dialog.r`):
+
+```
+/*
+ Copyright 2015 Wolfgang Thaller.
+
+ This file is part of Retro68.
+
+ Retro68 is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Retro68 is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Retro68. If not, see .
+*/
+
+#include "Dialogs.r"
+
+resource 'DLOG' (128) {
+ { 50, 100, 240, 420 },
+ dBoxProc,
+ visible,
+ noGoAway,
+ 0,
+ 128,
+ "",
+ centerMainScreen
+};
+
+resource 'DITL' (128) {
+ {
+ { 190-10-20, 320-10-80, 190-10, 320-10 },
+ Button { enabled, "Quit" };
+
+ { 190-10-20-5, 320-10-80-5, 190-10+5, 320-10+5 },
+ UserItem { enabled };
+
+ { 10, 10, 30, 310 },
+ StaticText { enabled, "Hello ^0" };
+
+ { 40, 10, 56, 310 },
+ EditText { enabled, "Edit Text Item" };
+
+ { 70, 10, 86, 310 },
+ CheckBox { enabled, "Check Box" };
+
+ { 90, 10, 106, 310 },
+ RadioButton { enabled, "Radio 1" };
+
+ { 110, 10, 126, 310 },
+ RadioButton { enabled, "Radio 2" };
+ }
+};
+
+#include "Processes.r"
+
+resource 'SIZE' (-1) {
+ reserved,
+ acceptSuspendResumeEvents,
+ reserved,
+ canBackground,
+ doesActivateOnFGSwitch,
+ backgroundAndForeground,
+ dontGetFrontClicks,
+ ignoreChildDiedEvents,
+ is32BitCompatible,
+#ifdef TARGET_API_MAC_CARBON
+ isHighLevelEventAware,
+#else
+ notHighLevelEventAware,
+#endif
+ onlyLocalHLEvents,
+ notStationeryAware,
+ dontUseTextEditServices,
+ reserved,
+ reserved,
+ reserved,
+#ifdef TARGET_API_MAC_CARBON
+ 500 * 1024, // Carbon apparently needs additional memory.
+ 500 * 1024
+#else
+ 100 * 1024,
+ 100 * 1024
+#endif
+};
+```
+
+The main differences are:
+
+* `extern` declaration for the Rust function
+* Using the string returned from `hello_rust` to set `ParamText`
+* Changing the StaticText control's text to "Hello ^0" in order to make use of
+ the `ParamText`
+* Adding `target_link_libraries(Dialog ${CMAKE_SOURCE_DIR}/target/powerpc-apple-macos/release/libclassic_mac_rust.a)`
+ to `CMakeLists.txt` to have CMake link with the Rust library.
+
+{{ figure(image="posts/2023/rust-on-ppc-classic-mac-os/ParamText.jpg", link="posts/2023/rust-on-ppc-classic-mac-os/ParamText.jpg", alt="Photo of ParamText documentation from my copy of Inside Macintosh Volume Ⅰ", caption="ParamText documentation from my copy of Inside Macintosh Volume Ⅰ") }}
+
+Now when building the project we get…
+
+```
+ninja: Entering directory `cmake-build-retro68ppc'
+[1/4] Linking C executable Dialog.xcoff
+FAILED: Dialog.xcoff
+: && /home/wmoore/Source/github.com/autc04/Retro68-build/toolchain/bin/powerpc-apple-macos-gcc -Wl,-gc-sections CMakeFiles/Dialog.dir/dialog.obj -o Dialog.xcoff /home/wmoore/Projects/classic-mac-rust/target/powerpc-apple-macos/release/libclassic_mac_rust.a && :
+/home/wmoore/Source/github.com/autc04/Retro68-build/toolchain/lib/gcc/powerpc-apple-macos/9.1.0/../../../../powerpc-apple-macos/bin/ld:/home/wmoore/Projects/classic-mac-rust/target/powerpc-apple-macos/release/libclassic_mac_rust.a: file format not recognized; treating as linker script
+/home/wmoore/Source/github.com/autc04/Retro68-build/toolchain/lib/gcc/powerpc-apple-macos/9.1.0/../../../../powerpc-apple-macos/bin/ld:/home/wmoore/Projects/classic-mac-rust/target/powerpc-apple-macos/release/libclassic_mac_rust.a:1: syntax error
+collect2: error: ld returned 1 exit status
+ninja: build stopped: subcommand failed.
+```
+
+It doesn't like `libclassic_mac_rust.a`. Some investigation shows that the objects in the library
+are in ELF format. `powerpc-apple-macos-objcopy --info` shows that Retro68 does not handle
+ELF:
+
+```
+BFD header file version (GNU Binutils) 2.31.1
+xcoff-powermac
+ (header big endian, data big endian)
+ powerpc:common
+ rs6000:6000
+srec
+ (header endianness unknown, data endianness unknown)
+ powerpc:common
+ rs6000:6000
+symbolsrec
+ (header endianness unknown, data endianness unknown)
+ powerpc:common
+ rs6000:6000
+verilog
+ (header endianness unknown, data endianness unknown)
+ powerpc:common
+ rs6000:6000
+tekhex
+ (header endianness unknown, data endianness unknown)
+ powerpc:common
+ rs6000:6000
+binary
+ (header endianness unknown, data endianness unknown)
+ powerpc:common
+ rs6000:6000
+ihex
+ (header endianness unknown, data endianness unknown)
+ powerpc:common
+ rs6000:6000
+
+ xcoff-powermac srec symbolsrec verilog tekhex binary ihex
+powerpc:common xcoff-powermac srec symbolsrec verilog tekhex binary ihex
+ rs6000:6000 xcoff-powermac srec symbolsrec verilog tekhex binary ihex
+```
+
+It looks like it really only supports `xcoff-powermac`, which was derived from
+rs6000 AIX. At this point I tried to find a way to convert my ELF objects to
+XCOFF. I eventually stumbled across
+[this thread on the Haiku forum](https://discuss.haiku-os.org/t/xcoff-pef/12445/15)
+that mentions that `powerpc-linux-gnu-binutils` on Debian knows about
+`aixcoff-rs6000`. So I fired up a Debian docker container and tried converting
+my `.a`, and it worked:
+
+```
+docker run --rm -it -v $(pwd):/src debian:testing
+apt update
+apt install powerpc-linux-gnu-binutils
+powerpc-linux-gnu-objcopy -O aixcoff-rs6000 /src/target/powerpc-apple-macos/release/libclassic_mac_rust.a /src/target/powerpc-apple-macos/release/libclassic_mac_rust.obj
+```
+
+Examining the objects in the new archive showed that they were now in the same
+format as the objects generated by Retro68. I updated the `CMakeLists.txt` to
+point at the new library and tried building again:
+
+```
+/home/wmoore/Source/github.com/autc04/Retro68-build/toolchain/lib/gcc/powerpc-apple-macos/9.1.0/../../../../powerpc-apple-macos/bin/ld: /home/wmoore/Projects/classic-mac-rust/target/powerpc-apple-macos/release/libclassic_mac_rust.obj(classic_mac_rust-80e61781bab75910.classic_mac_rust.9ba2ce33-cgu.0.rcgu.o): class 2 symbol `hello_rust' has no aux entries
+```
+
+Now we get further. It can read the `.a` now and even sees the `hello_rust`
+symbol but it
+[looks like it's looking for an aux entry to determine the symbol type](https://github.com/autc04/Retro68/blob/5f882506013a0a8a4335350197a1b7c91763494e/binutils/bfd/xcofflink.c#L1461-L1478)
+but not finding one. AUX entries are an
+[XCOFF](https://www.ibm.com/docs/en/aix/7.2?topic=formats-xcoff-object-file-format)
+thing.
+
+One other thing I tried was setting the `llvm-target` in the custom target JSON
+to `powerpc-ibm-aix`. Due to the heritage of PPC Mac OS the ABI is the same
+(Apple used the AIX toolchain, which is why object files use XCOFF even though
+executables use PEF). This target would be ideal as it would use the right ABI
+and emit XCOFF by default.
+
+Unfortunately it runs into unimplemented parts of LLVM's XCOFF implementation:
+
+> LLVM ERROR: relocation for paired relocatable term is not yet supported
+
+Rust uses a fork/snapshot of LLVM but the
+[issue is still present in LLVM master](https://github.com/rust-lang/llvm-project/blob/5ef9f9948fca7cb39dd6c1935ca4e819fb7a0db2/llvm/lib/MC/XCOFFObjectWriter.cpp).
+[This post on writing a Mac OS 9 application in Swift][swift] goes down a
+similar path using the AIX target and also mentions patching the Swift compiler
+to avoid the unsupported parts of LLVMs XCOFF implementation. That's an avenue
+for future experimentation.
+
+### rustc\_codegen\_gcc
+
+At this point I decided to try a different approach.
+[rustc\_codegen\_gcc](https://github.com/rust-lang/rustc_codegen_gcc) is a
+codegen plugin that uses [libgccjit] for code generation instead of LLVM. The
+motivation of the project is promising for my use case:
+
+> The primary goal of this project is to be able to compile Rust code on
+> platforms unsupported by LLVM.
+
+I found the instructions for using `rustc_codegen_gcc` a bit difficult to
+follow, especially when trying to build a cross-compiler.
+
+I eventually managed to rebuild Retro68 with `libgccjit` enabled and then coax
+`rustc_codegen_gcc` to use it. Unsurprisingly that quickly failed as Retro68 is
+based on GCC 9.1 and `rustc_codegen_gcc` is building against GCC master and
+there were many missing symbols.
+
+Undeterred I noted that there is a WIP GCC 12.2 branch in the Retro68 repo so I
+built that and tweaked `rustc_codegen_gcc` to disable the `master` cargo
+feature that should in theory allow it to build against a GCC release. This did
+in fact allow me to get a bit further but I ran into more issues in the step
+that attempts to build `compiler-rt` and `core`. Eventually I gave up on this
+route too. I was probably too far off the well tested configuration of x86,
+against GCC master.
+
+Future work here is to trying building a `powerpc-ibm-aix` libgccjit from GCC
+master and see if that works.
+
+### Wrap Up
+
+[Bastian on Twitter](https://twitter.com/turbolent/status/1617231570573873152)
+has had some success compiling Rust to Web Assembly, Web Assembly to C89, C89
+to Mac OS 9 binary, which is definitely cool but I would still love to be able
+to generate native PPC code directly from `rustc` somehow.
+
+This is where I have parked this project for now. I actually only discovered
+the post on building a Mac OS 9 application with Swift while writing this post.
+There are perhaps some ideas in there that I could explore further.
+
+[swift]: https://belkadan.com/blog/2020/04/Swift-on-Mac-OS-9/
+[BlueSCSI]: https://github.com/erichelgeson/BlueSCSI
+[Retro68]: https://github.com/autc04/Retro68
+[SheepShaver]: https://sheepshaver.cebix.net/
+[Dialog sample]: https://github.com/autc04/Retro68/tree/5f882506013a0a8a4335350197a1b7c91763494e/Samples/Dialog
+[Pascal string]: https://en.wikipedia.org/wiki/String_(computer_science)#Length-prefixed
+[libgccjit]: https://gcc.gnu.org/onlinedocs/jit/