From 085fdd72007734520ced2a06ff8b80fcc8968b52 Mon Sep 17 00:00:00 2001 From: Dani Sarfati Date: Sat, 8 Aug 2026 09:19:24 -0400 Subject: [PATCH] rustdesk-ppc-agent: new port, and the minicargo fix it needs A RustDesk agent -- the controlled side -- for PowerPC Macs: screen, keyboard, mouse and clipboard served to a standard RustDesk client, over a direct connection or a rendezvous server. There is no GUI client here. The protocol is reimplemented against RustDesk's own .proto files rather than ported from upstream's tokio-based tree, which is what keeps the graph at 32 crates and inside the subset of Rust mrustc handles. Built and run with mrustc at d0fffe5b plus this tree's 0001-0025 and the 0026 added here, against rustc 1.90 libstd with debug assertions on. The whole crate graph builds -- protobuf, sodiumoxide, sha2, libc, five C shims and 36 MB of generated C -- and the resulting binary serves a full display probe on a Power Mac G5 and G4: framebuffer capture, ARGB conversion, VP8 encode, clipboard. One crate needs patching. protobuf 3.0.0-alpha.2's BufReadIter reserves capacity and then indexes past the end of a zero-length slice, on a length taken mrustc needs 0026: minicargo parses a semver `+build` suffix but not `-prerelease`, so CARGO_PKG_VERSION reaches protobuf's build script as plain 3.0.0 -- it emits VERSION_3_0_0 while the crate's own generated code references VERSION_3_0_0_ALPHA_2, and the name fails to resolve. Submitted upstream as well; it applies above the CARGO_PKG_* block 0012 adds. revision bumped accordingly. Also confirms 0002 subsumes the niche-enum alignment fix I had been carrying locally: protobuf's ReflectValueRef, the type that needed it, now compiles with no sizeof_assert failures. --- lang/mrustc/Portfile | 3 +- ...ve-the-semver-pre-release-suffix-in-.patch | 94 ++++++++++++ net/rustdesk-ppc-agent/Portfile | 143 ++++++++++++++++++ .../files/protobuf-3.0.0-alpha.2.patch | 37 +++++ 4 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 lang/mrustc/files/0026-minicargo-Preserve-the-semver-pre-release-suffix-in-.patch create mode 100644 net/rustdesk-ppc-agent/Portfile create mode 100644 net/rustdesk-ppc-agent/files/protobuf-3.0.0-alpha.2.patch diff --git a/lang/mrustc/Portfile b/lang/mrustc/Portfile index f67ae0049..930dd5b8a 100644 --- a/lang/mrustc/Portfile +++ b/lang/mrustc/Portfile @@ -19,7 +19,7 @@ set rust_version_major_underscore [join [lrange [split ${rust_version} .-] 0 1] # Subport mrustc-rust has its own versioning version ${rust_version_major}-20260802 -revision 0 +revision 1 epoch 1 categories lang devel @@ -164,6 +164,7 @@ pre-patch { system -W ${workpath}/${name}-${github.version} "patch -p1 < [shellescape ${filespath}/0023-hir_typeck-find-trait-object-methods-through-custom-.patch]" system -W ${workpath}/${name}-${github.version} "patch -p1 < [shellescape ${filespath}/0024-hir_typeck-bind-path-ivars-against-non-generic-impls.patch]" system -W ${workpath}/${name}-${github.version} "patch -p1 < [shellescape ${filespath}/0025-toml-decode-u-U-escapes-to-real-UTF-8.patch]" + system -W ${workpath}/${name}-${github.version} "patch -p1 < [shellescape ${filespath}/0026-minicargo-Preserve-the-semver-pre-release-suffix-in-.patch]" # Patch the Rust source code with the patch included with mrustc: system -W ${workpath}/rustc-${rust_version}-src "patch -p0 < [shellescape ${worksrcpath}/rustc-${rust_version}-src.patch]" diff --git a/lang/mrustc/files/0026-minicargo-Preserve-the-semver-pre-release-suffix-in-.patch b/lang/mrustc/files/0026-minicargo-Preserve-the-semver-pre-release-suffix-in-.patch new file mode 100644 index 000000000..d4cd68111 --- /dev/null +++ b/lang/mrustc/files/0026-minicargo-Preserve-the-semver-pre-release-suffix-in-.patch @@ -0,0 +1,94 @@ +From 0ef4515caa7185727a069de5996cd07c7a5c3c74 Mon Sep 17 00:00:00 2001 +From: Dani Sarfati +Date: Fri, 7 Aug 2026 18:59:59 -0400 +Subject: [PATCH] minicargo: Preserve the semver pre-release suffix in + CARGO_PKG_VERSION + +`PackageVersion::from_string` parsed the `+build` suffix but not `-prerelease`, +so `3.0.0-alpha.2` silently became plain `3.0.0`. Build scripts that derive an +identifier from CARGO_PKG_VERSION then generate the wrong name: protobuf's +build.rs emits `VERSION_3_0_0` while its codegen'd .rs files reference +`VERSION_3_0_0_ALPHA_2`, which fails to resolve. + +The new `prerelease` field is deliberately not part of `cmp`/`operator==` -- +pre-release ordering rules are subtle and changing resolution behaviour is a +separate concern. It is exposed only through `to_string_full()`, used for +CARGO_PKG_VERSION; `operator<<` is unchanged so crate tags and output filenames +do not churn. +--- + tools/minicargo/build.cpp | 3 ++- + tools/minicargo/manifest.cpp | 10 +++++++++- + tools/minicargo/manifest.h | 15 ++++++++++++++- + 3 files changed, 25 insertions(+), 3 deletions(-) + +diff --git a/tools/minicargo/build.cpp b/tools/minicargo/build.cpp +index 3b1dd2a9..f6141bfd 100644 +--- a/tools/minicargo/build.cpp ++++ b/tools/minicargo/build.cpp +@@ -743,7 +743,8 @@ namespace { + { + env.push_back("CARGO_MANIFEST_DIR", manifest.directory().to_absolute()); + env.push_back("CARGO_PKG_NAME", manifest.name()); +- env.push_back("CARGO_PKG_VERSION", ::format(manifest.version())); ++ // Full form: build scripts derive identifiers from this ++ env.push_back("CARGO_PKG_VERSION", manifest.version().to_string_full()); + env.push_back("CARGO_PKG_VERSION_MAJOR", ::format(manifest.version().major)); + env.push_back("CARGO_PKG_VERSION_MINOR", ::format(manifest.version().minor)); + env.push_back("CARGO_PKG_VERSION_PATCH", ::format(manifest.version().patch)); +diff --git a/tools/minicargo/manifest.cpp b/tools/minicargo/manifest.cpp +index b3b80644..8696ac56 100644 +--- a/tools/minicargo/manifest.cpp ++++ b/tools/minicargo/manifest.cpp +@@ -1560,7 +1560,15 @@ PackageVersion PackageVersion::from_string(const ::std::string& s) + rv.patch = 0; + rv.patch_set = false; + } +- if(iss.peek() == '+') { ++ // Semver is `-prerelease` then `+build` ++ if(iss.peek() == '-') { ++ iss.get(); ++ ::std::getline(iss, rv.prerelease, '+'); ++ if(!iss.eof()) { // getline ate the '+' ++ iss >> rv.free_text; ++ } ++ } ++ else if(iss.peek() == '+') { + iss.get(); + iss >> rv.free_text; + } +diff --git a/tools/minicargo/manifest.h b/tools/minicargo/manifest.h +index 545ae38a..e7583a2e 100644 +--- a/tools/minicargo/manifest.h ++++ b/tools/minicargo/manifest.h +@@ -36,7 +36,8 @@ struct PackageVersion + unsigned minor; + unsigned patch; + bool patch_set; +- std::string free_text; ++ std::string free_text; // Semver `+build` ++ std::string prerelease; // Semver `-prerelease`; not part of ordering + + explicit PackageVersion() + : major(0), minor(0), patch(0), patch_set(false) {} +@@ -114,6 +115,18 @@ struct PackageVersion + } + return os; + } ++ ++ /// Full version string, for CARGO_PKG_VERSION (`operator<<` feeds crate tags) ++ std::string to_string_full() const { ++ std::string rv = std::to_string(major) + "." + std::to_string(minor); ++ if(patch_set) ++ rv += "." + std::to_string(patch); ++ if(!prerelease.empty()) ++ rv += "-" + prerelease; ++ if(!free_text.empty()) ++ rv += "+" + free_text; ++ return rv; ++ } + }; + struct PackageVersionSpec + { +-- +2.43.0 + diff --git a/net/rustdesk-ppc-agent/Portfile b/net/rustdesk-ppc-agent/Portfile new file mode 100644 index 000000000..7eef08744 --- /dev/null +++ b/net/rustdesk-ppc-agent/Portfile @@ -0,0 +1,143 @@ +# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4 + +PortSystem 1.0 +PortGroup github 1.0 +PortGroup cargo 1.0 + +# The agent is a subdirectory of a RustDesk fork rather than a repository of its +# own, so three things are set by hand: +# - the tag is prefixed `ppc-agent-`, to keep it clear of upstream RustDesk's +# own 1.x tags on the same fork; +# - `name` is set, because github.setup would otherwise call this port +# `rustdesk`, which it is not; +# - `worksrcdir` descends into the subdirectory. GitHub names an archive +# directory `-`, so tag `ppc-agent-0.1.0` of repo `rustdesk` +# unpacks to `rustdesk-ppc-agent-0.1.0/`. +github.setup danifunker rustdesk 0.1.0 ppc-agent- +name rustdesk-ppc-agent +distname rustdesk-ppc-agent-0.1.0 +worksrcdir ${distname}/rustdesk-ppc-agent +revision 0 +categories net +license GPL-3 +maintainers {@danifunker gmail.com:danifunkervogt} \ + openmaintainer + +# 32-bit only: the capture path is CGDisplayBaseAddress and the input path is +# CGPostMouseEvent, and the whole thing is built and tested as ppc. +supported_archs ppc +installs_libs no + +description RustDesk agent (controlled side) for PowerPC Mac OS X + +long_description A minimal RustDesk agent for PowerPC Macs: it serves screen, \ + keyboard, mouse and clipboard to a standard RustDesk client, \ + over a direct connection or a rendezvous server. This is the \ + controlled side only -- there is no GUI client. The protocol \ + is reimplemented against RustDesk's own .proto files rather \ + than ported from upstream's tokio-based tree, which is what \ + keeps the dependency graph at ~30 crates and inside the \ + subset of Rust mrustc handles. + +github.tarball_from archive + +checksums ${distname}${extract.suffix} \ + rmd160 4b56a05c1e2d67c134cffec6ed19d989f388e848 \ + sha256 9dbc60b8acfa92dbffcc7b87ee87dea69593042f8a686665ec9a0b25b0cbdbcc \ + size 758541 + +# libvpx VP8 encode (the agent links it statically) +# libsodium the RustDesk handshake (via sodiumoxide) +# zstd, zlib clipboard decompression from modern peers +depends_lib-append port:libvpx \ + port:libsodium \ + port:zstd \ + port:zlib + +# AltiVec is not optional for this port: both libvpx's encoder paths and the +# agent's own C shims use it, so a G3 cannot run the result. See the upstream +# BACKLOG section 13 for the measurements. + +cargo.offline_cmd + +# protobuf 3.0.0-alpha.2's BufReadIter::read_exact_to_vec reserves capacity and +# then indexes past the end of a zero-length slice -- UB on a length taken +# straight off the wire. It survives a release build but aborts under the debug +# assertions this toolchain builds libstd with. +patch.dir ${workpath} +patchfiles protobuf-3.0.0-alpha.2.patch + +# SODIUM_LIB_DIR libsodium-sys looks here rather than running pkg-config +# PPC_LIBS_DIR build.rs turns this into -L for the statically linked libs +build.env-append SODIUM_LIB_DIR=${prefix}/lib \ + PPC_LIBS_DIR=${prefix}/lib + +cargo.crates \ + block-buffer 0.10.4 3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71 \ + cc 1.4.0 5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9 \ + cfg-if 1.0.4 9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801 \ + cpufeatures 0.2.17 59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280 \ + crypto-common 0.1.7 78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a \ + digest 0.10.7 9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292 \ + ed25519 1.5.3 91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7 \ + find-msvc-tools 0.1.9 5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582 \ + generic-array 0.14.7 85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a \ + libc 0.2.174 1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776 \ + libsodium-sys 0.2.7 6b779387cd56adfbc02ea4a668e704f729be8d6a6abd2c27ca5ee537849a92fd \ + log 0.4.33 0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad \ + pkg-config 0.3.33 19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e \ + proc-macro2 1.0.107 985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9 \ + protobuf 3.0.0-alpha.2 9d5ef59c35c7472ce5e1b6c5924b87585143d1fc2cf39eae0009bba6c4df62f1 \ + quote 1.0.47 1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001 \ + same-file 1.0.6 93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502 \ + serde 1.0.229 4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba \ + serde_core 1.0.229 67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48 \ + serde_derive 1.0.229 e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348 \ + sha2 0.10.9 a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283 \ + shlex 2.0.1 f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba \ + signature 1.6.4 74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c \ + sodiumoxide 0.2.7 e26be3acb6c2d9a7aac28482586a7856436af4cfe7100031d219de2d2ecb0028 \ + syn 3.0.3 53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3 \ + typenum 1.20.1 b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20 \ + unicode-ident 1.0.24 e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75 \ + version_check 0.9.5 0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a \ + walkdir 2.5.0 29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b \ + winapi-util 0.1.11 c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22 \ + windows-link 0.2.1 f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5 \ + windows-sys 0.61.2 ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc + +destroot { + xinstall -m 0755 \ + ${worksrcpath}/target/[cargo.rust_platform]/release/rustdesk-agent \ + ${destroot}${prefix}/bin/ + + # A launchd *agent* template, not a startupitem. The agent has to run inside + # the logged-in user's Aqua session: PasteboardCreate returns -4960 from an + # ssh login or a detached screen, so a LaunchDaemon serves a session with no + # working clipboard. Installed as a template because it is per-user and the + # binary path has to be substituted in. + xinstall -d ${destroot}${prefix}/share/${name} + xinstall -m 0644 ${worksrcpath}/deploy/com.rustdesk.ppc-agent.plist.in \ + ${destroot}${prefix}/share/${name}/ + xinstall -m 0644 ${worksrcpath}/README.md \ + ${destroot}${prefix}/share/${name}/ +} + +notes " +To run the agent at login, install the LaunchAgent template into your own\ + account and substitute the paths: + + mkdir -p ~/Library/LaunchAgents + sed -e 's|@BIN@|${prefix}/bin/rustdesk-agent|' \\ + -e 's|@LOG@|'\$HOME'/rustdesk-ppc-agent/agent.log|' \\ + -e 's|@PORT@|21118|' \\ + ${prefix}/share/${name}/com.rustdesk.ppc-agent.plist.in \\ + > ~/Library/LaunchAgents/com.rustdesk.ppc-agent.plist + launchctl load ~/Library/LaunchAgents/com.rustdesk.ppc-agent.plist + +It must be a LaunchAgent rather than a LaunchDaemon: the clipboard only works\ + inside the logged-in Aqua session. This starts the agent at *login*, not at\ + boot, so a Mac meant to be reachable unattended needs automatic login enabled. + +This is the controlled side only. Connect to it with a standard RustDesk client. +" diff --git a/net/rustdesk-ppc-agent/files/protobuf-3.0.0-alpha.2.patch b/net/rustdesk-ppc-agent/files/protobuf-3.0.0-alpha.2.patch new file mode 100644 index 000000000..0183fb044 --- /dev/null +++ b/net/rustdesk-ppc-agent/files/protobuf-3.0.0-alpha.2.patch @@ -0,0 +1,37 @@ +protobuf 3.0.0-alpha.2: fix out-of-bounds indexing in the wire-parse path + +`BufReadIter::read_exact_to_vec` calls `reserve_exact(count)` and then +`get_unchecked_mut(..count)`. `reserve_exact` grows *capacity*, not length, so +at that point `target.len()` is 0 and the range is past the end of the slice. +It happens to work in a release build because the allocation is real, but it is +undefined behaviour, and `count` comes straight off the wire. + +libstd as built by the mrustc port has debug assertions enabled, so std's +precondition check fires and aborts: + + unsafe precondition(s) violated: slice::get_unchecked_mut requires that the + range is within the slice + +Replaced with a safe `resize`, which costs one memset of a buffer that is about +to be overwritten anyway. + +--- .home/.cargo/macports/protobuf-3.0.0-alpha.2/src/buf_read_iter.rs ++++ .home/.cargo/macports/protobuf-3.0.0-alpha.2/src/buf_read_iter.rs +@@ -352,12 +352,11 @@ + } + } + } else { +- target.reserve_exact(count); +- +- unsafe { +- self.read_exact(&mut target.get_unchecked_mut(..count))?; +- target.set_len(count); +- } ++ // `reserve_exact` grows capacity, not length, so the original ++ // `get_unchecked_mut(..count)` here indexed past the end of a ++ // zero-length slice - UB on a wire-controlled length. ++ target.resize(count, 0); ++ self.read_exact(&mut target[..count])?; + } + + debug_assert_eq!(count, target.len());