<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title>Jim Calabro</title>
  <id>https://calabro.io</id>
  <updated>2025-03-28T00:00:00Z</updated>
  <rights>Copyright © 2026 Jim Calabro. All rights reserved.</rights>
  <subtitle>My personal website including blog posts mostly on computing, but perhaps sometimes music, fitness, video games, etc.</subtitle>
  <link href="https://calabro.io"></link>
  <author>
    <name>Jim Calabro</name>
    <email>jim@calabro.io</email>
  </author>
  <entry>
    <title>Building Statically Linked Go Executables with CGO and Zig</title>
    <updated>2025-03-28T00:00:00Z</updated>
    <id>tag:calabro.io,2025-03-28:/zig-cgo</id>
    <content type="html">&#xA;&lt;!DOCTYPE html&gt;&#xA;&lt;html lang=&#34;en&#34;&gt;&#xA;    &lt;head&gt;&#xA;        &lt;meta charset=&#34;utf-8&#34;&gt;&#xA;        &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no&#34; /&gt;&#xA;        &lt;meta http-equiv=&#34;Cache-control&#34; content=&#34;public&#34;&gt;&#xA;        &lt;meta name=&#34;description&#34; content=&#34;Jim Calabro&#34;&gt;&#xA;        &lt;link rel=&#34;canonical&#34; rel=&#34;noreferrer&#34; href=&#34;https://www.calabro.io&#34; /&gt;&#xA;        &lt;link rel=&#34;icon&#34; type=&#34;image/svg&#34; href=&#34;/static/favicon.svg&#34;&gt;&#xA;        &lt;title&gt;Building Statically Linked Go Executables with CGO and Zig - Jim Calabro&lt;/title&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/lit.min.css&#34;&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/style.css&#34;&gt;&#xA;        &lt;link href=&#39;/static/Raleway.css&#39; rel=&#39;stylesheet&#39; type=&#39;text/css&#39; async defer&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; href=&#34;/static/font-awesome.min.css&#34;&gt;&#xA;        &#xA;        &lt;script async defer src=&#34;/static/font-awesome.min.js&#34; data-auto-replace-svg=&#34;nest&#34;&gt;&lt;/script&gt;&#xA;        &lt;script type=&#34;text/javascript&#34;&gt;&#xA;            const mobileWidth = 560;&#xA;&#xA;            function toggleMenu(event) {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    return;&#xA;                }&#xA;&#xA;                const links = document.querySelector(&#39;.links&#39;);&#xA;                const display = links.style[&#39;display&#39;];&#xA;                if (!display || display === &#39;none&#39;) {&#xA;                    links.style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    links.style.display = &#39;none&#39;;&#xA;                }&#xA;            }&#xA;&#xA;            addEventListener(&#39;resize&#39;, (event) =&gt; {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;none&#39;;&#xA;                }&#xA;            });&#xA;        &lt;/script&gt;&#xA;    &lt;/head&gt;&#xA;    &lt;body&gt;&#xA;        &lt;div class=&#34;row&#34;&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&lt;/div&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&#xA;                &lt;div class=&#34;collapsed-links&#34;&gt;&#xA;                    &lt;h3 class=&#34;name-header&#34;&gt;&lt;a href=&#34;/&#34; style=&#34;cursor: pointer&#34;&gt;Jim Calabro&lt;/a&gt;&lt;/h3&gt;&#xA;                    &lt;div onclick=&#34;toggleMenu(this)&#34;&gt;&lt;i class=&#34;hamburger-menu fa-solid fa-bars&#34;&gt;&lt;/i&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;                &lt;div class=&#34;links&#34;&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;/about&#34;&gt;About&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&#xA;                        &lt;a href=&#34;/rss&#34;&gt;RSS&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/atom&#34;&gt;Atom&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/json&#34;&gt;JSON&lt;/a&gt;&#xA;                    &lt;/div&gt;&#xA;                    &lt;div&gt;---&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://bsky.app/profile/calabro.io&#34; target=&#34;_blank&#34;&gt;Bluesky &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://github.com/jcalabro&#34; target=&#34;_blank&#34;&gt;GitHub &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.chess.com/member/jcalabro&#34; target=&#34;_blank&#34;&gt;Chess &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.last.fm/user/thekingofping&#34; target=&#34;_blank&#34;&gt;Last.fm &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&#xA;                &#xA;                &lt;div class=&#34;post-title&#34;&gt;&#xA;                    &lt;h4&gt;Building Statically Linked Go Executables with CGO and Zig&lt;/h4&gt;&#xA;                    &lt;i&gt;&lt;/i&gt;&#xA;                &lt;/div&gt;&#xA;                &#xA;                &#xA;&lt;p&gt;&#xA;    This is a short post about how to create a statically linked Go executable that calls in to CGO dependencies using Zig. The full code for this post is available in &lt;a href=&#34;https://github.com/jcalabro/zig-cgo-example&#34; target=&#34;_blank&#34;&gt;this repo&lt;/a&gt;.&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;    By default, if you&#39;re using CGO, the executable you generate dynamically links, but I frequently want to statically link to avoid runtime errors.&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;    First, let&#39;s create a zig library, with &lt;strong&gt;zig init&lt;/strong&gt;, then trim back the excess stuff it generates so we&#39;re left just with a simple static library. You can &lt;strong&gt;rm src/main.zig&lt;/strong&gt; since we&#39;re not creating a zig executable.&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;    Next, we can trim &lt;strong&gt;build.zig&lt;/strong&gt; to just:&#xA;&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// build.zig&#xA;const std = @import(&#34;std&#34;);&#xA;&#xA;pub fn build(b: *std.Build) void {&#xA;    const target = b.standardTargetOptions(.{});&#xA;    const optimize = b.standardOptimizeOption(.{});&#xA;&#xA;    const lib_mod = b.createModule(.{&#xA;        .root_source_file = b.path(&#34;src/root.zig&#34;),&#xA;        .target = target,&#xA;        .optimize = optimize,&#xA;    });&#xA;&#xA;    const lib = b.addLibrary(.{&#xA;        .linkage = .static,&#xA;        .name = &#34;cgo_static_linking&#34;,&#xA;        .root_module = lib_mod,&#xA;    });&#xA;    b.installArtifact(lib);&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&#xA;    We can leave the &lt;i&gt;build.zig.zon&lt;/i&gt; file alone.&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;    Now, let&#39;s actually write a simple library function that uses the C ABI in &lt;strong&gt;src/root.zig&lt;/strong&gt;:&#xA;&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// src/root.zig&#xA;const std = @import(&#34;std&#34;);&#xA;&#xA;pub export fn my_zig_function() void {&#xA;    std.debug.print(&#34;Hello from zig!\n&#34;, .{});&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&#xA;    And its corresponding C header file named &lt;strong&gt;zig_lib.h&lt;/strong&gt;:&#xA;&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// zig_lib.h&#xA;#pragma once&#xA;&#xA;void my_zig_function();&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&#xA;    That&#39;s it on the zig side! You can build the library now by simply running &lt;strong&gt;zig build&lt;/strong&gt;.&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;    Let&#39;s write the Go program that calls it.&#xA;&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;// main.go&#xA;package main&#xA;&#xA;/*&#xA;#cgo LDFLAGS: -L./zig-out/lib -lcgo_static_linking -static&#xA;#include &#34;zig_lib.h&#34;&#xA;*/&#xA;import &#34;C&#34;&#xA;import &#34;fmt&#34;&#xA;&#xA;func main() {&#xA;&#x9;fmt.Println(&#34;starting program&#34;)&#xA;&#x9;defer fmt.Println(&#34;done&#34;)&#xA;&#xA;&#x9;C.my_zig_function()&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&#xA;    We&#39;ll now build the Go executable and statically link it with this bash command:&#xA;&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;CC=&#34;zig cc -target x86_64-linux-musl&#34; \&#xA;CGO_ENABLED=1 \&#xA;CGO_LDFLAGS=&#34;-static&#34; \&#xA;GOOS=linux GOARCH=amd64 \&#xA;go build -a -ldflags &#39;-extldflags &#34;-static&#34;&#39; main.go&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&#xA;    Let&#39;s check our work:&#xA;&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;$ ./main&#xA;starting program&#xA;Hello from zig!&#xA;done&#xA;&#xA;$ ldd ./main&#xA;        not a dynamic executable&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&#xA;    Looks good to me!&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;    I&#39;m incredibly grateful that I&#39;m building at a time where our tools are getting extremely good. Go and Zig are both amazing!&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;    If you find this useful as I do, perhaps consider making a donation to the &lt;a href=&#34;https://ziglang.org/zsf/&#34; target=&#34;_blank&#34;&gt;Zig Software Foundation&lt;/a&gt;!&#xA;&lt;/p&gt;&#xA;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;    &lt;/body&gt;&#xA;&lt;/html&gt;&#xA;</content>
    <link href="https://calabro.io/zig-cgo" rel="alternate"></link>
    <summary type="html">A quick demo of how straight-forward it is to build a statically-linked Go executable that calls in to CGO dependencies with Zig</summary>
  </entry>
  <entry>
    <title>uscope Update #2</title>
    <updated>2025-03-20T00:00:00Z</updated>
    <id>tag:calabro.io,2025-03-20:/uscope-update-2</id>
    <content type="html">&#xA;&lt;!DOCTYPE html&gt;&#xA;&lt;html lang=&#34;en&#34;&gt;&#xA;    &lt;head&gt;&#xA;        &lt;meta charset=&#34;utf-8&#34;&gt;&#xA;        &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no&#34; /&gt;&#xA;        &lt;meta http-equiv=&#34;Cache-control&#34; content=&#34;public&#34;&gt;&#xA;        &lt;meta name=&#34;description&#34; content=&#34;Jim Calabro&#34;&gt;&#xA;        &lt;link rel=&#34;canonical&#34; rel=&#34;noreferrer&#34; href=&#34;https://www.calabro.io&#34; /&gt;&#xA;        &lt;link rel=&#34;icon&#34; type=&#34;image/svg&#34; href=&#34;/static/favicon.svg&#34;&gt;&#xA;        &lt;title&gt;uscope Update #2 - Jim Calabro&lt;/title&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/lit.min.css&#34;&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/style.css&#34;&gt;&#xA;        &lt;link href=&#39;/static/Raleway.css&#39; rel=&#39;stylesheet&#39; type=&#39;text/css&#39; async defer&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; href=&#34;/static/font-awesome.min.css&#34;&gt;&#xA;        &#xA;        &lt;script async defer src=&#34;/static/font-awesome.min.js&#34; data-auto-replace-svg=&#34;nest&#34;&gt;&lt;/script&gt;&#xA;        &lt;script type=&#34;text/javascript&#34;&gt;&#xA;            const mobileWidth = 560;&#xA;&#xA;            function toggleMenu(event) {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    return;&#xA;                }&#xA;&#xA;                const links = document.querySelector(&#39;.links&#39;);&#xA;                const display = links.style[&#39;display&#39;];&#xA;                if (!display || display === &#39;none&#39;) {&#xA;                    links.style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    links.style.display = &#39;none&#39;;&#xA;                }&#xA;            }&#xA;&#xA;            addEventListener(&#39;resize&#39;, (event) =&gt; {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;none&#39;;&#xA;                }&#xA;            });&#xA;        &lt;/script&gt;&#xA;    &lt;/head&gt;&#xA;    &lt;body&gt;&#xA;        &lt;div class=&#34;row&#34;&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&lt;/div&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&#xA;                &lt;div class=&#34;collapsed-links&#34;&gt;&#xA;                    &lt;h3 class=&#34;name-header&#34;&gt;&lt;a href=&#34;/&#34; style=&#34;cursor: pointer&#34;&gt;Jim Calabro&lt;/a&gt;&lt;/h3&gt;&#xA;                    &lt;div onclick=&#34;toggleMenu(this)&#34;&gt;&lt;i class=&#34;hamburger-menu fa-solid fa-bars&#34;&gt;&lt;/i&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;                &lt;div class=&#34;links&#34;&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;/about&#34;&gt;About&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&#xA;                        &lt;a href=&#34;/rss&#34;&gt;RSS&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/atom&#34;&gt;Atom&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/json&#34;&gt;JSON&lt;/a&gt;&#xA;                    &lt;/div&gt;&#xA;                    &lt;div&gt;---&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://bsky.app/profile/calabro.io&#34; target=&#34;_blank&#34;&gt;Bluesky &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://github.com/jcalabro&#34; target=&#34;_blank&#34;&gt;GitHub &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.chess.com/member/jcalabro&#34; target=&#34;_blank&#34;&gt;Chess &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.last.fm/user/thekingofping&#34; target=&#34;_blank&#34;&gt;Last.fm &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&#xA;                &#xA;                &lt;div class=&#34;post-title&#34;&gt;&#xA;                    &lt;h4&gt;uscope Update #2&lt;/h4&gt;&#xA;                    &lt;i&gt;Mar 20, 2025&lt;/i&gt;&#xA;                &lt;/div&gt;&#xA;                &#xA;                &#xA;&lt;div&gt;&#xA;    &lt;p&gt;&#xA;        Hello again! It&#39;s been a bit since I&#39;ve posted an update, and I&#39;ve been pretty quiet in open source work during that time, despite a lot happening.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        If you find this work compelling, please consider &lt;a href=&#34;https://github.com/sponsors/jcalabro&#34; target=&#34;_blank&#34;&gt;supporting my work&lt;/a&gt;. This is a strong signal to me that I&#39;m on the right track.&#xA;    &lt;/p&gt;&#xA;    &lt;a name=&#34;updates&#34; href=&#34;#updates&#34;&gt;&lt;h4&gt;Updates&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        Last month, I didn&#39;t make a ton of progress on &lt;a href=&#34;https://github.com/jcalabro/uscope&#34; target=&#34;_blank&#34;&gt;uscope&lt;/a&gt; to be perfectly honest. Life has been busy. Here are the updates:&#xA;    &lt;/p&gt;&#xA;    &lt;ul&gt;&#xA;        &lt;li&gt;&#xA;            Upgraded to &lt;a href=&#34;https://ziglang.org/download/0.14.0/release-notes.html&#34; target=&#34;_blank&#34;&gt;Zig 0.14.0&lt;/a&gt;. Congrats to the zig team on a great release!&#xA;        &lt;/li&gt;&#xA;        &lt;li&gt;&#xA;            Worked on adding Go support in the debugger (much more work is needed still, but Go DWARF parses okay now).&#xA;        &lt;/li&gt;&#xA;        &lt;li&gt;&#xA;            Added preliminary support for the &lt;a href=&#34;https://c3-lang.org/&#34; target=&#34;_blank&#34;&gt;C3 programming language&lt;/a&gt;.&#xA;        &lt;/li&gt;&#xA;        &lt;li&gt;&#xA;            Made a tiny bit of progress on debugging binaries built by the zig self-hosted backend. There&#39;s a good amount more to do here.&#xA;        &lt;/li&gt;&#xA;        &lt;li&gt;&#xA;            You won&#39;t see this in any uscope code yet, but I did a lot of experimenting with eBPF and can see a path forward on using it for many interesting debugging use-cases.&#xA;        &lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;    &lt;a name=&#34;whats-next&#34; href=&#34;#whats-next&#34;&gt;&lt;h4&gt;What&#39;s Next?&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        Big news: I accepted a new job! I haven&#39;t announced where yet, but I will soon. I start in a couple weeks and I&#39;m very excited! It&#39;s at a small, fast-growing company and I think we&#39;re a great fit culturally and technically.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Consequently, I predict that I&#39;ll have substantially less spare time and energy to work on uscope, at least for a while. So, in order to make sure we all get the most out of it, I&#39;m going to be re-focusing the project in the following ways to try to make the code I&#39;ve written as useful as possible to the community ASAP:&#xA;    &lt;/p&gt;&#xA;    &lt;ul&gt;&#xA;        &lt;li&gt;&#xA;            I&#39;m going to yanking the ELF/DWARF parsing code out in to its own standalone Zig library.&#xA;            &lt;ul&gt;&#xA;                &lt;li&gt;&#xA;                    I think a lot of people would be tempted to build more cool introspection tools if only there were an easy-to-use library for parsing debug info that was free of license obligations&#xA;                &lt;/li&gt;&#xA;                &lt;li&gt;&#xA;                    &lt;a href=&#34;https://github.com/gimli-rs/gimli&#34; target=&#34;_blank&#34;&gt;Gimli&lt;/a&gt; for rust looks great and &lt;a href=&#34;https://github.com/eliben/pyelftools&#34; target=&#34;_blank&#34;&gt;pyelftools&lt;/a&gt; is great, but for folks writing C or Zig programs, there aren&#39;t any great options that are obligation-free as far as I can tell (I certainly could be missing some lesser-known libraries besides the major ones).&#xA;                &lt;/li&gt;&#xA;            &lt;/ul&gt;&#xA;        &lt;/li&gt;&#xA;        &lt;li&gt;&#xA;            I&#39;m going to focus mostly on remote debugging since this is how I will personally use the debugger.&#xA;            &lt;ul&gt;&#xA;                &lt;li&gt;&#xA;                    I&#39;ll be creating a headless &#34;remote daemon&#34; mode for the debugger that is accessible over the network.&#xA;                &lt;/li&gt;&#xA;                &lt;li&gt;&#xA;                    This is particularly important for a Linux debugger as most of the code I care about isn&#39;t actually on my machine; it&#39;s somewhere in a data center/cloud.&#xA;                &lt;/li&gt;&#xA;            &lt;/ul&gt;&#xA;        &lt;/li&gt;&#xA;        &lt;li&gt;&#xA;            Since I&#39;m focusing on remote debugging, I&#39;m going to rip out the native desktop UI and instead create a web UI that will access the remote daemon from the browser (even if it&#39;s just running on localhost).&#xA;            &lt;ul&gt;&#xA;                &lt;li&gt;&#xA;                    I&#39;m sure this decision will be controversial, but there are many reasons to not ship a native desktop UI for Linux.&#xA;                    &lt;ul&gt;&#xA;                        &lt;li&gt;&#xA;                            It&#39;s the wild west, and I cannot reasonably support all flavors of Linux desktop as a hobby project.&#xA;                        &lt;/li&gt;&#xA;                        &lt;li&gt;&#xA;                            It&#39;s already been an issue for myself and my very limited user base, and it will only get worse over time.&#xA;                        &lt;/li&gt;&#xA;                    &lt;/ul&gt;&#xA;                &lt;/li&gt;&#xA;                &lt;li&gt;&#xA;                    At time of writing, the browser is the highest bang-for-your buck in terms of UI quality to time invested.&#xA;                    &lt;ul&gt;&#xA;                        &lt;li&gt;&#xA;                            There are some really impressive native UIs out there (&lt;a href=&#34;https://github.com/EpicGamesExt/raddebugger&#34; target=&#34;_blank&#34;&gt;1&lt;/a&gt;, &lt;a href=&#34;https://filepilot.tech/&#34; target=&#34;_blank&#34;&gt;2&lt;/a&gt;), but they take a ton of time and effort, and frankly I currently don&#39;t have the graphics chops to build a beautiful, professional native UI. I could learn these techniques over time, but that takes spare time and energy that I don&#39;t have.&#xA;                        &lt;/li&gt;&#xA;                        &lt;li&gt;&#xA;                            I used to do a ton of web development, and I&#39;m pretty confident I can build a reasonably good UI quickly.&#xA;                        &lt;/li&gt;&#xA;                        &lt;li&gt;&#xA;                            It will be written in &lt;a href=&#34;http://vanilla-js.com/&#34; target=&#34;_blank&#34;&gt;vanilla js&lt;/a&gt; and still be fast and light-weight!&#xA;                        &lt;/li&gt;&#xA;                    &lt;/ul&gt;&#xA;                &lt;/li&gt;&#xA;                &lt;li&gt;&#xA;                    I have a strong preference for only distributing statically linked binaries, and shipping an executable with a native GUI that&#39;s GPU-rendered is a really tall order.&#xA;                    &lt;ul&gt;&#xA;                        &lt;li&gt;&#xA;                            Perhaps it&#39;s impossible? Not sure. In any case, it would be going against the grain.&#xA;                        &lt;/li&gt;&#xA;                        &lt;li&gt;&#xA;                            CPU rendering is of course possible, but that comes with its own downsides (poor performance and battery draining mostly).&#xA;                        &lt;/li&gt;&#xA;                    &lt;/ul&gt;&#xA;                &lt;/li&gt;&#xA;                &lt;li&gt;&#xA;                    The user-supplied visualization plugin story becomes so much simpler when you&#39;re working in the browser.&#xA;                    &lt;ul&gt;&#xA;                        &lt;li&gt;&#xA;                            It&#39;s important that writing plugins is approachable by the programming population at large, otherwise very few people will write them at all.&#xA;                        &lt;/li&gt;&#xA;                    &lt;/ul&gt;&#xA;                &lt;/li&gt;&#xA;                &lt;li&gt;&#xA;                    The uscope code is set up in such a way that adding a new UI is actually quite easy, and we can ship multiple UI types in the same binary.&#xA;                    &lt;ul&gt;&#xA;                        &lt;li&gt;&#xA;                            For example, we can simultaneously support &#34;remote daemon&#34; mode as well as something like &#34;cli&#34; mode which would be akin to a more traditional gdb/lldb cli.&#xA;                        &lt;/li&gt;&#xA;                        &lt;li&gt;&#xA;                            I&#39;m mentioning this because it may be a good time to add a cli debugger, though no promises. I still think that the visual debugging story is dramatically more important than cli debugging.&#xA;                        &lt;/li&gt;&#xA;                        &lt;li&gt;&#xA;                            The community can still maintain a native UI if they desire; it just won&#39;t ship as part of stock uscope.&#xA;                        &lt;/li&gt;&#xA;                    &lt;/ul&gt;&#xA;                &lt;/li&gt;&#xA;            &lt;/ul&gt;&#xA;        &lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;    &lt;a name=&#34;final-words&#34; href=&#34;#final-words&#34;&gt;&lt;h4&gt;Final Words&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        Those are the updates, and they&#39;re pretty major ones! Come discuss on &lt;a href=&#34;https://discord.gg/bPWC6PZPhR&#34; target=&#34;_blank&#34;&gt;Discord&lt;/a&gt; if you have feedback.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        I&#39;ve been doing these short updates about once a month so far. Definitely expect that to slow down - there&#39;s no regular cadence to my writing on this site.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        That&#39;s all for now! Thanks for reading!&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;    &lt;/body&gt;&#xA;&lt;/html&gt;&#xA;</content>
    <link href="https://calabro.io/uscope-update-2" rel="alternate"></link>
    <summary type="html">Quick progress update, resetting project direction</summary>
  </entry>
  <entry>
    <title>uscope Update #1</title>
    <updated>2025-02-18T00:00:00Z</updated>
    <id>tag:calabro.io,2025-02-18:/uscope-update-1</id>
    <content type="html">&#xA;&lt;!DOCTYPE html&gt;&#xA;&lt;html lang=&#34;en&#34;&gt;&#xA;    &lt;head&gt;&#xA;        &lt;meta charset=&#34;utf-8&#34;&gt;&#xA;        &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no&#34; /&gt;&#xA;        &lt;meta http-equiv=&#34;Cache-control&#34; content=&#34;public&#34;&gt;&#xA;        &lt;meta name=&#34;description&#34; content=&#34;Jim Calabro&#34;&gt;&#xA;        &lt;link rel=&#34;canonical&#34; rel=&#34;noreferrer&#34; href=&#34;https://www.calabro.io&#34; /&gt;&#xA;        &lt;link rel=&#34;icon&#34; type=&#34;image/svg&#34; href=&#34;/static/favicon.svg&#34;&gt;&#xA;        &lt;title&gt;uscope Update #1 - Jim Calabro&lt;/title&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/lit.min.css&#34;&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/style.css&#34;&gt;&#xA;        &lt;link href=&#39;/static/Raleway.css&#39; rel=&#39;stylesheet&#39; type=&#39;text/css&#39; async defer&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; href=&#34;/static/font-awesome.min.css&#34;&gt;&#xA;        &#xA;        &lt;script async defer src=&#34;/static/font-awesome.min.js&#34; data-auto-replace-svg=&#34;nest&#34;&gt;&lt;/script&gt;&#xA;        &lt;script type=&#34;text/javascript&#34;&gt;&#xA;            const mobileWidth = 560;&#xA;&#xA;            function toggleMenu(event) {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    return;&#xA;                }&#xA;&#xA;                const links = document.querySelector(&#39;.links&#39;);&#xA;                const display = links.style[&#39;display&#39;];&#xA;                if (!display || display === &#39;none&#39;) {&#xA;                    links.style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    links.style.display = &#39;none&#39;;&#xA;                }&#xA;            }&#xA;&#xA;            addEventListener(&#39;resize&#39;, (event) =&gt; {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;none&#39;;&#xA;                }&#xA;            });&#xA;        &lt;/script&gt;&#xA;    &lt;/head&gt;&#xA;    &lt;body&gt;&#xA;        &lt;div class=&#34;row&#34;&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&lt;/div&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&#xA;                &lt;div class=&#34;collapsed-links&#34;&gt;&#xA;                    &lt;h3 class=&#34;name-header&#34;&gt;&lt;a href=&#34;/&#34; style=&#34;cursor: pointer&#34;&gt;Jim Calabro&lt;/a&gt;&lt;/h3&gt;&#xA;                    &lt;div onclick=&#34;toggleMenu(this)&#34;&gt;&lt;i class=&#34;hamburger-menu fa-solid fa-bars&#34;&gt;&lt;/i&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;                &lt;div class=&#34;links&#34;&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;/about&#34;&gt;About&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&#xA;                        &lt;a href=&#34;/rss&#34;&gt;RSS&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/atom&#34;&gt;Atom&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/json&#34;&gt;JSON&lt;/a&gt;&#xA;                    &lt;/div&gt;&#xA;                    &lt;div&gt;---&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://bsky.app/profile/calabro.io&#34; target=&#34;_blank&#34;&gt;Bluesky &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://github.com/jcalabro&#34; target=&#34;_blank&#34;&gt;GitHub &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.chess.com/member/jcalabro&#34; target=&#34;_blank&#34;&gt;Chess &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.last.fm/user/thekingofping&#34; target=&#34;_blank&#34;&gt;Last.fm &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&#xA;                &#xA;                &lt;div class=&#34;post-title&#34;&gt;&#xA;                    &lt;h4&gt;uscope Update #1&lt;/h4&gt;&#xA;                    &lt;i&gt;Feb 18, 2025&lt;/i&gt;&#xA;                &lt;/div&gt;&#xA;                &#xA;                &#xA;&lt;div&gt;&#xA;    &lt;p&gt;&#xA;        Hi! I wanted to give a quick update of the things I&#39;ve been working on in &lt;a href=&#34;/uscope&#34; target=&#34;_blank&#34;&gt;uscope&lt;/a&gt;, the from-scratch graphical Linux debugger I&#39;ve been building.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        If you find this work compelling, please consider &lt;a href=&#34;https://github.com/sponsors/jcalabro&#34; target=&#34;_blank&#34;&gt;supporting my work&lt;/a&gt;. This is a strong signal to me that I&#39;m on the right track.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        For starters, I &lt;a href=&#34;https://www.youtube.com/watch?v=stWBTv6grBc&#34; target=&#34;_blank&#34;&gt;demo&#39;ed the project&lt;/a&gt; on &lt;a href=&#34;https://zig.show/&#34; target=&#34;_blank&#34;&gt;Zig Showtime&lt;/a&gt;! Thank you to &lt;a href=&#34;https://kristoff.it/&#34; target=&#34;_blank&#34;&gt;Loris&lt;/a&gt; for having me; it was a lot of fun.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Next, here are the project updates in no particular order:&#xA;    &lt;/p&gt;&#xA;    &lt;ul&gt;&#xA;        &lt;li&gt;I completely re-wrote the variable rendering code so now it&#39;s displayed as an expandable tree. This means that things like arrays/slices, nested structs, and structs with circular pointers back to themselves are rendered properly. This is similar to how other debuggers render their data.&lt;/li&gt;&#xA;        &lt;li&gt;I also fixed a boat-load of bugs related to extracting variable data from the subordinate process, and folks are reporting some amount of success using the debugger on their real-world programs now!&lt;/li&gt;&#xA;        &lt;li&gt;I added the ability to debug multi-threaded subordinates (there&#39;s still a lot of bugs and things to do here, but it&#39;s a start)&lt;/li&gt;&#xA;        &lt;li&gt;Added support for Odin data types as a first-class citizen.&lt;/li&gt;&#xA;        &lt;li&gt;The debugger now renders very, very basic information for C++, Rust, and Jai data types. It&#39;s ugly, but can be usable if you&#39;re not doing anything complicated (i.e. Jai looks OK, but C++ and Rust not so much)&lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;    &lt;p&gt;&#xA;        That&#39;s all for now. Thanks for reading!&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        P.S., Here&#39;s a screenshot of uscope rendering some very basic Odin types:&#xA;    &lt;/p&gt;&#xA;    &lt;a href=&#34;https://github.com/user-attachments/assets/0e630d88-15c1-45be-9ade-357bc6dd0464&#34; target=&#34;_blank&#34;&gt;&#xA;        &lt;img src=&#34;https://github.com/user-attachments/assets/0e630d88-15c1-45be-9ade-357bc6dd0464&#34; alt=&#34;rendering odin types&#34; class=&#34;picture&#34;&gt;&#xA;    &lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;    &lt;/body&gt;&#xA;&lt;/html&gt;&#xA;</content>
    <link href="https://calabro.io/uscope-update-1" rel="alternate"></link>
    <summary type="html">Nested variable rendering, multi-threaded subordinates, Odin, and bugfixes.</summary>
  </entry>
  <entry>
    <title>uscope: A New Debugger and Introspection Toolchain</title>
    <updated>2025-01-26T00:00:00Z</updated>
    <id>tag:calabro.io,2025-01-26:/uscope</id>
    <content type="html">&#xA;&lt;!DOCTYPE html&gt;&#xA;&lt;html lang=&#34;en&#34;&gt;&#xA;    &lt;head&gt;&#xA;        &lt;meta charset=&#34;utf-8&#34;&gt;&#xA;        &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no&#34; /&gt;&#xA;        &lt;meta http-equiv=&#34;Cache-control&#34; content=&#34;public&#34;&gt;&#xA;        &lt;meta name=&#34;description&#34; content=&#34;Jim Calabro&#34;&gt;&#xA;        &lt;link rel=&#34;canonical&#34; rel=&#34;noreferrer&#34; href=&#34;https://www.calabro.io&#34; /&gt;&#xA;        &lt;link rel=&#34;icon&#34; type=&#34;image/svg&#34; href=&#34;/static/favicon.svg&#34;&gt;&#xA;        &lt;title&gt;uscope: A New Debugger and Introspection Toolchain - Jim Calabro&lt;/title&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/lit.min.css&#34;&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/style.css&#34;&gt;&#xA;        &lt;link href=&#39;/static/Raleway.css&#39; rel=&#39;stylesheet&#39; type=&#39;text/css&#39; async defer&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; href=&#34;/static/font-awesome.min.css&#34;&gt;&#xA;        &#xA;        &lt;script async defer src=&#34;/static/font-awesome.min.js&#34; data-auto-replace-svg=&#34;nest&#34;&gt;&lt;/script&gt;&#xA;        &lt;script type=&#34;text/javascript&#34;&gt;&#xA;            const mobileWidth = 560;&#xA;&#xA;            function toggleMenu(event) {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    return;&#xA;                }&#xA;&#xA;                const links = document.querySelector(&#39;.links&#39;);&#xA;                const display = links.style[&#39;display&#39;];&#xA;                if (!display || display === &#39;none&#39;) {&#xA;                    links.style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    links.style.display = &#39;none&#39;;&#xA;                }&#xA;            }&#xA;&#xA;            addEventListener(&#39;resize&#39;, (event) =&gt; {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;none&#39;;&#xA;                }&#xA;            });&#xA;        &lt;/script&gt;&#xA;    &lt;/head&gt;&#xA;    &lt;body&gt;&#xA;        &lt;div class=&#34;row&#34;&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&lt;/div&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&#xA;                &lt;div class=&#34;collapsed-links&#34;&gt;&#xA;                    &lt;h3 class=&#34;name-header&#34;&gt;&lt;a href=&#34;/&#34; style=&#34;cursor: pointer&#34;&gt;Jim Calabro&lt;/a&gt;&lt;/h3&gt;&#xA;                    &lt;div onclick=&#34;toggleMenu(this)&#34;&gt;&lt;i class=&#34;hamburger-menu fa-solid fa-bars&#34;&gt;&lt;/i&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;                &lt;div class=&#34;links&#34;&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;/about&#34;&gt;About&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&#xA;                        &lt;a href=&#34;/rss&#34;&gt;RSS&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/atom&#34;&gt;Atom&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/json&#34;&gt;JSON&lt;/a&gt;&#xA;                    &lt;/div&gt;&#xA;                    &lt;div&gt;---&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://bsky.app/profile/calabro.io&#34; target=&#34;_blank&#34;&gt;Bluesky &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://github.com/jcalabro&#34; target=&#34;_blank&#34;&gt;GitHub &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.chess.com/member/jcalabro&#34; target=&#34;_blank&#34;&gt;Chess &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.last.fm/user/thekingofping&#34; target=&#34;_blank&#34;&gt;Last.fm &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&#xA;                &#xA;                &lt;div class=&#34;post-title&#34;&gt;&#xA;                    &lt;h4&gt;uscope: A New Debugger and Introspection Toolchain&lt;/h4&gt;&#xA;                    &lt;i&gt;Jan 26, 2025&lt;/i&gt;&#xA;                &lt;/div&gt;&#xA;                &#xA;                &#xA;&lt;div&gt;&#xA;    &lt;p&gt;&#xA;        &lt;a href=&#34;https://github.com/jcalabro/uscope&#34; target=&#34;_blank&#34;&gt;uscope&lt;/a&gt; (pronounced &#34;microscope&#34;) is a new graphical debugger for Linux. Its goal is to enable you to understand what your system is actually doing as quickly and effectively as possible.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        All debugging-related code is written from scratch, i.e. parsing &lt;a href=&#34;/dwarf&#34; target=&#34;_blank&#34;&gt;ELF/DWARF&lt;/a&gt;, handling control of the subordinate process (launch, kill, step, continue, etc.), reading data from the subordinate&#39;s memory/registers and displaying it in a user-friendly manner, stack unwinding, etc.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Here&#39;s a screenshot of the debugger stopped in a simple zig program that just prints out a bunch of basic variables. You can see that a fair number of table-stakes debugger features have already been implemented, but there&#39;s still a ton more to do!&#xA;    &lt;/p&gt;&#xA;    &lt;a href=&#34;https://github.com/user-attachments/assets/9382f061-953f-48bd-9e66-63375580e4d3&#34; target=&#34;_blank&#34;&gt;&#xA;        &lt;img src=&#34;https://github.com/user-attachments/assets/9382f061-953f-48bd-9e66-63375580e4d3&#34; alt=&#34;picture of the debugger&#34; class=&#34;picture&#34; /&gt;&#xA;    &lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;why&#34; href=&#34;#why&#34;&gt;&lt;h4&gt;Why Are You Doing This?&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        &lt;a href=&#34;/dwarf#why-are-you-writing-this&#34; target=&#34;_blank&#34;&gt;I feel strongly&lt;/a&gt; that the state of the art for introspection tooling needs to improve, particularly on Linux. Figuring out what your program is doing (often called &lt;a href=&#34;https://www.youtube.com/watch?v=SvEjS4-2WJQ&amp;t=556s&#34; target=&#34;_blank&#34;&gt;observability&lt;/a&gt;) is a critical aspect of developing all modern software, whether it&#39;s a command line application, giant distributed system, video game, database, and so on. There are loads of graphical debuggers that are powerful and fast to use on Windows, but it&#39;s a missing piece of the puzzle on Linux.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Fundamentally, it is the process of collecting data, possibly transforming or aggregating it, then visualizing it to bring light to something you didn&#39;t previously understand. This feedback loop needs to be easy and fast for developers, and unfortunately, the existing tools are not good enough.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        The available Linux debuggers are gdb and lldb. When I use them, they crash a lot, they don&#39;t understand the data types I care about particularly well, and they don&#39;t make the data I need available at my fingertips as quickly as possible (which is really a problem with all CLI-based debuggers).&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Consequently, most Linux devs I know don&#39;t use a debugger and instead just printf-debug, which is often the correct choice because what&#39;s the point of a debugger that doesn&#39;t save you time? We&#39;ve got to improve the situation because without a great debugger, our software quality suffers and it takes longer to deliver.&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;plan&#34; href=&#34;#plan&#34;&gt;&lt;h4&gt;What&#39;s the Plan?&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        It&#39;s very early days now and it&#39;s going to be a while before it&#39;s usable as a daily driver. The table-stakes debugger features are off to a good start, but there&#39;s still a long way to go.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Currently, only C and Zig have any shot of being debugged nicely, but support for more languages is coming. Support for other platforms (at least macOS and Windows) is also planned, but very far off. There&#39;s more detail on specific features I want to implement &lt;a href=&#34;https://github.com/jcalabro/uscope/blob/main/README.md#project-status-and-roadmap&#34; target=&#34;_blank&#34;&gt;in the README&lt;/a&gt;.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        The debugger collects data from various sources (currently mostly ptrace), transforms those raw bytes to a common data format, then provides that to a UI for visualization. Both the backend (data collection) and frontend (data visualization) will be hackable and extensible.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        For instance, I work on chess engines for a living, and it would be pretty slick if I had a debugger that understood my exact data layout and automatically rendered an interactive chessboard when I want to inspect a variable that contains a &lt;a href=&#34;https://www.chessprogramming.org/Bitboards&#34; target=&#34;_blank&#34;&gt;bitboard&lt;/a&gt; for a position. The possibilities are limitless for bespoke debug tooling for your specific problem domain once we have a simple, extensible toolkit to work with.&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;get-involved&#34; href=&#34;#get-involved&#34;&gt;&lt;h4&gt;How do You (yes &lt;i&gt;you!&lt;/i&gt;) Get Involved?&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        Reach out! We&#39;ve got a &lt;a href=&#34;https://discord.gg/bPWC6PZPhR&#34; target=&#34;_blank&#34;&gt;Discord&lt;/a&gt;, and my personal email is jim at this domain. I love talking debug tooling.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        &lt;a href=&#34;https://github.com/jcalabro/uscope&#34; target=&#34;_blank&#34;&gt;Contributions&lt;/a&gt; are absolutely welcome, though I&#39;d recommend reaching out before tackling a large chunk of work to make sure we&#39;re on the same page and effort isn&#39;t wasted.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        You could also consider &lt;a href=&#34;https://github.com/sponsors/jcalabro&#34; target=&#34;_blank&#34;&gt;sponsoring&lt;/a&gt; my work. This is a very strong signal to me that I&#39;m on the right track.&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;    &lt;/body&gt;&#xA;&lt;/html&gt;&#xA;</content>
    <link href="https://calabro.io/uscope" rel="alternate"></link>
    <summary type="html">The why, what, and how of building a new debugger</summary>
  </entry>
  <entry>
    <title>How DWARF Works: Debug Information Entries</title>
    <updated>2024-09-28T00:00:00Z</updated>
    <id>tag:calabro.io,2024-09-28:/dwarf/die</id>
    <content type="html">&#xA;&lt;!DOCTYPE html&gt;&#xA;&lt;html lang=&#34;en&#34;&gt;&#xA;    &lt;head&gt;&#xA;        &lt;meta charset=&#34;utf-8&#34;&gt;&#xA;        &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no&#34; /&gt;&#xA;        &lt;meta http-equiv=&#34;Cache-control&#34; content=&#34;public&#34;&gt;&#xA;        &lt;meta name=&#34;description&#34; content=&#34;Jim Calabro&#34;&gt;&#xA;        &lt;link rel=&#34;canonical&#34; rel=&#34;noreferrer&#34; href=&#34;https://www.calabro.io&#34; /&gt;&#xA;        &lt;link rel=&#34;icon&#34; type=&#34;image/svg&#34; href=&#34;/static/favicon.svg&#34;&gt;&#xA;        &lt;title&gt;How DWARF Works: Debug Information Entries - Jim Calabro&lt;/title&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/lit.min.css&#34;&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/style.css&#34;&gt;&#xA;        &lt;link href=&#39;/static/Raleway.css&#39; rel=&#39;stylesheet&#39; type=&#39;text/css&#39; async defer&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; href=&#34;/static/font-awesome.min.css&#34;&gt;&#xA;        &#xA;        &lt;script async defer src=&#34;/static/font-awesome.min.js&#34; data-auto-replace-svg=&#34;nest&#34;&gt;&lt;/script&gt;&#xA;        &lt;script type=&#34;text/javascript&#34;&gt;&#xA;            const mobileWidth = 560;&#xA;&#xA;            function toggleMenu(event) {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    return;&#xA;                }&#xA;&#xA;                const links = document.querySelector(&#39;.links&#39;);&#xA;                const display = links.style[&#39;display&#39;];&#xA;                if (!display || display === &#39;none&#39;) {&#xA;                    links.style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    links.style.display = &#39;none&#39;;&#xA;                }&#xA;            }&#xA;&#xA;            addEventListener(&#39;resize&#39;, (event) =&gt; {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;none&#39;;&#xA;                }&#xA;            });&#xA;        &lt;/script&gt;&#xA;    &lt;/head&gt;&#xA;    &lt;body&gt;&#xA;        &lt;div class=&#34;row&#34;&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&lt;/div&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&#xA;                &lt;div class=&#34;collapsed-links&#34;&gt;&#xA;                    &lt;h3 class=&#34;name-header&#34;&gt;&lt;a href=&#34;/&#34; style=&#34;cursor: pointer&#34;&gt;Jim Calabro&lt;/a&gt;&lt;/h3&gt;&#xA;                    &lt;div onclick=&#34;toggleMenu(this)&#34;&gt;&lt;i class=&#34;hamburger-menu fa-solid fa-bars&#34;&gt;&lt;/i&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;                &lt;div class=&#34;links&#34;&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;/about&#34;&gt;About&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&#xA;                        &lt;a href=&#34;/rss&#34;&gt;RSS&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/atom&#34;&gt;Atom&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/json&#34;&gt;JSON&lt;/a&gt;&#xA;                    &lt;/div&gt;&#xA;                    &lt;div&gt;---&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://bsky.app/profile/calabro.io&#34; target=&#34;_blank&#34;&gt;Bluesky &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://github.com/jcalabro&#34; target=&#34;_blank&#34;&gt;GitHub &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.chess.com/member/jcalabro&#34; target=&#34;_blank&#34;&gt;Chess &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.last.fm/user/thekingofping&#34; target=&#34;_blank&#34;&gt;Last.fm &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&#xA;                &#xA;                &lt;div class=&#34;post-title&#34;&gt;&#xA;                    &lt;h4&gt;How DWARF Works: Debug Information Entries&lt;/h4&gt;&#xA;                    &lt;i&gt;Sep 28, 2024&lt;/i&gt;&#xA;                &lt;/div&gt;&#xA;                &#xA;                &#xA;&#xA;&lt;div class=&#34;margin-bottom&#34;&gt;&#xA;    &lt;i&gt;&#xA;        This is part of the &lt;a href=&#34;/dwarf&#34;&gt;series on DWARF&lt;/a&gt;.&#xA;    &lt;/i&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;debug-information-entries&#34; href=&#34;#debug-information-entries&#34;&gt;&lt;h4&gt;Debug Information Entries&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        So far, we&#39;ve parsed enough of the contents of an &lt;a href=&#34;/dwarf/elf&#34; target=&#34;_blank&#34;&gt;ELF executable&lt;/a&gt; to get the various debug info sections as raw byte arrays. Now, we&#39;ll use all these sections together to make progress towards properly inspecting a running instance of the target program.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        This post&#39;s goal is to construct the Debug Information Entry (DIE) tree. This is a long task with several side quests along the way.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        A DIE is a piece of information about a particular node of a compiled program, and they are stored in a tree. When compiler authors write parsers/lexers, the first step in the pipeline is generally attempting to turn the source text of a program in to an Abstract Syntax Tree, and you can sort of think of the DIE tree as a similar thing for DWARF debuggers. All your compile units, functions, variables, struct definitions, etc. each get an entry in the DIE tree. A DIE tree is really more like a forest in that each compile unit in your executable gets its own tree.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        We will use these DIEs later on to ascertain key facts about a running process such as &#34;what is the data type of the variable named &lt;code&gt;x&lt;/code&gt;, and where in memory/registers are its bytes?&#34;, which is critical for building a debugger.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Perhaps it&#39;s easiest to show what it looks like. Here&#39;s the relevant section of &lt;a href=&#34;/dwarf/elf#our-test-program&#34; target=&#34;_blank&#34;&gt;cloop&lt;/a&gt;&#39;s DIE tree using &lt;code&gt;dwarfdump cloop&lt;/code&gt;:&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;&amp;lt; 1&amp;gt;&amp;lt;0x000002dd&amp;gt;    DW_TAG_subprogram&#xA;                      DW_AT_external              yes(1)&#xA;                      DW_AT_name                  main&#xA;                      DW_AT_decl_file             0x00000001 /home/jcalabro/local/data/cloop/main.c&#xA;                      DW_AT_decl_line             0x00000004&#xA;                      DW_AT_decl_column           0x00000005&#xA;                      DW_AT_type                  &amp;lt;0x00000058&amp;gt;&#xA;                      DW_AT_low_pc                0x00401156&#xA;                      DW_AT_high_pc               &amp;lt;offset-from-lowpc&amp;gt; 86 &amp;lt;highpc: 0x004011ac&amp;gt;&#xA;                      DW_AT_frame_base            len 0x0001: 0x9c:&#xA;                          DW_OP_call_frame_cfa&#xA;                      DW_AT_call_all_tail_calls   yes(1)&#xA;                      DW_AT_sibling               &amp;lt;0x0000031c&amp;gt;&#xA;&amp;lt; 2&amp;gt;&amp;lt;0x000002ff&amp;gt;      DW_TAG_variable&#xA;                        DW_AT_name                  pid&#xA;                        DW_AT_decl_file             0x00000001&#xA;                        DW_AT_decl_line             0x00000005&#xA;                        DW_AT_decl_column           0x0000000b&#xA;                        DW_AT_type                  &amp;lt;0x0000009d&amp;gt;&#xA;                        DW_AT_location              len 0x0002: 0x9164:&#xA;                            DW_OP_fbreg -28&#xA;&amp;lt; 2&amp;gt;&amp;lt;0x0000030d&amp;gt;      DW_TAG_variable&#xA;                        DW_AT_name                  ndx&#xA;                        DW_AT_decl_file             0x00000001&#xA;                        DW_AT_decl_line             0x00000006&#xA;                        DW_AT_decl_column           0x00000018&#xA;                        DW_AT_type                  &amp;lt;0x0000031c&amp;gt;&#xA;                        DW_AT_location              len 0x0002: 0x9168:&#xA;                            DW_OP_fbreg -24&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;    &lt;p&gt;&#xA;        There&#39;s not a ton to see in our &lt;code&gt;cloop&lt;/code&gt; program because it&#39;s so simple, but there is a function (aka &lt;code&gt;subprogram&lt;/code&gt;) named &lt;code&gt;main&lt;/code&gt; and two variables, &lt;code&gt;ndx&lt;/code&gt; and &lt;code&gt;pid&lt;/code&gt;, just as we&#39;d expect. It tells us on what lines they appear, and what their data types are, and &lt;code&gt;DW_AT_location&lt;/code&gt; actually tells us how to find the value of a variable at runtime! There&#39;s a lot of good stuff in here.&#xA;    &lt;p&gt;&#xA;    &lt;/p&gt;&#xA;        You can see that all these entries have a single &lt;a href=&#34;https://github.com/ziglang/zig/blob/57b2b3df520cd8c243cb974636880c9f5ca3f117/lib/std/dwarf/TAG.zig&#34; target=&#34;_blank&#34;&gt;tag&lt;/a&gt; that indicates the type of entry (each prefixed with &lt;code&gt;DW_TAG_&lt;/code&gt;), then some number of &lt;a href=&#34;https://github.com/ziglang/zig/blob/085cc54aadb327b9910be2c72b31ea046e7e8f52/lib/std/dwarf/AT.zig&#34; target=&#34;_blank&#34;&gt;attributes&lt;/a&gt;, or metadata describing that particular entry (each prefixed with &lt;code&gt;DW_AT_&lt;/code&gt;). If you look at the full output, you can see a lot of DIEs for primitive C types and the standard library.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        It&#39;s a bit tough to tell because we&#39;re only one level deep, but this data is stored in a tree. You can see it in the indentation of the tags and attributes if you look carefully, or you can use the numbers on the far left as reference of depth (&lt;code&gt;&amp;lt; 1&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt; 2&amp;gt;&lt;/code&gt;). This is just how &lt;code&gt;dwarfdump&lt;/code&gt; renders the DIE tree; the actual format on disk is not of this form.&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;abbrev-tables&#34; href=&#34;#abbrev-tables&#34;&gt;&lt;h4&gt;Abbrev Tables&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        All that being said, the very first thing we have to parse actually is not the DIE tree. No, we will start by reading the full set of &lt;code&gt;abbreviation tables&lt;/code&gt;. The abbrev tables give you a lot of information that&#39;s used in constructing the DIE tree. It tells you, for each potential entry in the tree, what is its tag (AST node type), and what attributes each node has. For each attribute, it tells you its data type via its form (is it a string, is it a constant integer value, and so on). The set of abbrev tables lives in the &lt;code&gt;.debug_abbrev&lt;/code&gt; ELF section.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Parsing the abbrev table is pretty straightforward: it&#39;s an array of declarations (decls), each with a code, a tag, and some number of attributes. To see it in action, you run &lt;code&gt;dwarfdump --print-abbrev cloop&lt;/code&gt;:&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;&amp;lt;    1&gt;&amp;lt;0x00000000&gt;&lt;code:   1&gt; DW_TAG_member               DW_children_no&#xA;       &amp;lt;0x00000003&gt;              DW_AT_name                  DW_FORM_strp&#xA;       &amp;lt;0x00000005&gt;              DW_AT_decl_file             DW_FORM_implicit_const &amp;lt;4 (0x4)&gt;&#xA;       &amp;lt;0x00000008&gt;              DW_AT_decl_line             DW_FORM_data1&#xA;       &amp;lt;0x0000000a&gt;              DW_AT_decl_column           DW_FORM_data1&#xA;       &amp;lt;0x0000000c&gt;              DW_AT_type                  DW_FORM_ref4&#xA;       &amp;lt;0x0000000e&gt;              DW_AT_data_member_location  DW_FORM_data1&#xA;&amp;lt;    2&gt;&amp;lt;0x00000012&gt;&lt;code:   2&gt; DW_TAG_base_type            DW_children_no&#xA;       &amp;lt;0x00000015&gt;              DW_AT_byte_size             DW_FORM_data1&#xA;       &amp;lt;0x00000017&gt;              DW_AT_encoding              DW_FORM_data1&#xA;       &amp;lt;0x00000019&gt;              DW_AT_name                  DW_FORM_strp&#xA;&amp;lt;    3&gt;&amp;lt;0x0000001d&gt;&lt;code:   3&gt; DW_TAG_pointer_type         DW_children_no&#xA;       &amp;lt;0x00000020&gt;              DW_AT_byte_size             DW_FORM_implicit_const &amp;lt;8 (0x8)&gt;&#xA;       &amp;lt;0x00000023&gt;              DW_AT_type                  DW_FORM_ref4&#xA;&amp;lt;    4&gt;&amp;lt;0x00000027&gt;&lt;code:   4&gt; DW_TAG_typedef              DW_children_no&#xA;       &amp;lt;0x0000002a&gt;              DW_AT_name                  DW_FORM_strp&#xA;       &amp;lt;0x0000002c&gt;              DW_AT_decl_file             DW_FORM_data1&#xA;       &amp;lt;0x0000002e&gt;              DW_AT_decl_line             DW_FORM_data1&#xA;       &amp;lt;0x00000030&gt;              DW_AT_decl_column           DW_FORM_data1&#xA;       &amp;lt;0x00000032&gt;              DW_AT_type                  DW_FORM_ref4&#xA;&#xA;       etc...&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;    &lt;p&gt;&#xA;        We can write a parser for this with the following methodology:&#xA;    &lt;/p&gt;&#xA;    &lt;ul&gt;&#xA;        &lt;li&gt;Create a new abbrev table, storing our current offset (the number of bytes we&#39;ve read so far through the section)&lt;/li&gt;&#xA;        &lt;li&gt;If we&#39;ve read to the end of the section, we&#39;re done&lt;/li&gt;&#xA;        &lt;li&gt;If we&#39;re not done, read N decls for this table via:&lt;/li&gt;&#xA;        &lt;ul&gt;&#xA;            &lt;li&gt;Create a new decl and read its code&lt;/li&gt;&#xA;            &lt;li&gt;If the code is zero, we&#39;re done with this table (all tables end with a &#34;null entry&#34; whose code is zero)&lt;/li&gt;&#xA;            &lt;li&gt;Read the decl&#39;s tag enum value&lt;/li&gt;&#xA;            &lt;li&gt;Read a bool that indicates whether or not this node in the tree has children&lt;/li&gt;&#xA;            &lt;li&gt;Read all attributes for this decl&lt;/li&gt;&#xA;            &lt;ul&gt;&#xA;                &lt;li&gt;Read the attribute name enum value&lt;/li&gt;&#xA;                &lt;li&gt;Read the attribute form enum value&lt;/li&gt;&#xA;                &lt;li&gt;If the name and the form are both zero, we&#39;ve read all of this decl&#39;s attributes&lt;/li&gt;&#xA;                &lt;li&gt;If the form is DW_FORM_implicit_const, read and store the implicit const value (we&#39;ll see how this is used later in this post)&lt;/li&gt;&#xA;            &lt;/ul&gt;&#xA;        &lt;/ul&gt;&#xA;    &lt;/ul&gt;&#xA;    &lt;p&gt;&#xA;        Check the DWARF documentation for what each of those enum values should be, and see the &lt;a href=&#34;/dwarf/go#leb128&#34; target=&#34;_blank&#34;&gt;appendix&lt;/a&gt; for more information on LEB128. Translated in to code, that looks like:&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;type abbrevTable struct {&#xA;    offset int&#xA;    decls  map[uint64]abbrevDecl&#xA;}&#xA;&#xA;type abbrevDecl struct {&#xA;    code        uint64&#xA;    hasChildren uint8&#xA;    attrs       []abbrevAttribute&#xA;}&#xA;&#xA;type abbrevAttribute struct {&#xA;    name             uint64&#xA;    form             uint64&#xA;    implicitConstVal int64&#xA;}&#xA;&#xA;func parseAbbrevTables(abbrevSecion []byte) []abbrevTable {&#xA;    buf := bytes.NewBuffer(abbrevSecion)&#xA;    tables := []abbrevTable{}&#xA;    reader := NewBinaryReader(buf, binary.NativeEndian)&#xA;&#xA;    for {&#xA;        table := abbrevTable{&#xA;            offset: BufferOffset(len(abbrevSecion), buf),&#xA;            decls:  map[uint64]abbrevDecl{},&#xA;        }&#xA;        if table.offset &gt;= len(abbrevSecion) {&#xA;            break // nothing left to read in the section&#xA;        }&#xA;&#xA;        for {&#xA;            decl := abbrevDecl{}&#xA;            decl.code, _ = leb128.DecodeU64(reader)&#xA;            if decl.code == 0 {&#xA;                break // we&#39;ve finished with this table&#xA;            }&#xA;&#xA;            decl.tag, _ = leb128.DecodeU64(reader)&#xA;            decl.hasChildren, _ = Read[uint8](reader)&#xA;&#xA;            for {&#xA;                attr := abbrevAttribute{}&#xA;                attr.name, _ = leb128.DecodeU64(reader)&#xA;                attr.form, _ = leb128.DecodeU64(reader)&#xA;                if attr.name == 0 &amp;&amp; attr.form == 0 {&#xA;                    break&#xA;                }&#xA;&#xA;                if attr.form == 0x21 { // DW_FORM_implicit_const&#xA;                    attr.implicitConstVal, _ = leb128.DecodeS64(reader)&#xA;                }&#xA;&#xA;                decl.attrs = append(decl.attrs, attr)&#xA;            }&#xA;&#xA;            table.decls[decl.code] = decl&#xA;        }&#xA;&#xA;        tables = append(tables, table)&#xA;    }&#xA;&#xA;    return tables&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;    &lt;p&gt;&#xA;        That&#39;s it with abbrev tables. We&#39;ll use these quite a lot as we parse the DIE tree. You can see that we already parsed something that resembles a tree, but we&#39;re not at the point where the data is meaningful yet. Note that &lt;code&gt;cloop&lt;/code&gt; only has one table, but there will probably be many in real-world programs.&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;compilation-unit-headers&#34; href=&#34;#compilation-unit-headers&#34;&gt;&lt;h4&gt;Compilation Unit Headers&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        Now we&#39;re ready to start tackling the &lt;code&gt;.debug_info&lt;/code&gt; section, which by is the section that contains most of the DIE information. We&#39;re going to parse one or more compilation units and their DIEs in a loop, starting with the compilation unit header. The header isn&#39;t itself a DIE, but it does give us some critical information in dealing with the remainder of the tree. So our outer loop looks like:&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;type CU struct {&#xA;    header *CUHeader&#xA;    dies   []DIE&#xA;}&#xA;&#xA;type CUHeader struct {&#xA;    length            uintptr&#xA;    version           uint16&#xA;    unitType          uint8 // added in v5&#xA;    debugAbbrevOffset int&#xA;    addrSize          uint8&#xA;&#xA;    // not a real field in the DWARF standard,&#xA;    // but helpful for bookkeeping&#xA;    is32Bit bool&#xA;}&#xA;&#xA;cus := []*CU{}&#xA;infoReader := NewBinaryReader(bytes.NewBuffer(sections.info))&#xA;for {&#xA;    if infoReader.Offset() &gt;= len(sections.info) {&#xA;        break&#xA;    }&#xA;&#xA;    cuHeader := parseCUHeader(infoReader)&#xA;&#xA;    // choose the correct abbrev table for this CU&#xA;    abbrev := abbrevTables[0]&#xA;    for _, a := range abbrevTables {&#xA;        if a.offset == cuHeader.debugAbbrevOffset {&#xA;            abbrev = a&#xA;            break&#xA;        }&#xA;    }&#xA;&#xA;    cu := parseCU(infoReader, cuHeader, sections, abbrev)&#xA;    cus = append(cus, cu)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;    &lt;p&gt;&#xA;        Then, we can implement &lt;code&gt;parseCUHeader&lt;/code&gt;. Each compilation unit header contains these fields in order, but note that the order of &lt;code&gt;debug_abbrev_offset&lt;/code&gt; and &lt;code&gt;addr_size&lt;/code&gt; is reversed as of DWARF v5.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Additionally, see the &lt;a href=&#34;/dwarf/go#initial-length&#34; target=&#34;_blank&#34;&gt;appendix&lt;/a&gt; for more information on fields of type &lt;code&gt;initial length&lt;/code&gt;.&#xA;    &lt;/p&gt;&#xA;    &lt;ul&gt;&#xA;        &lt;li&gt;&lt;code&gt;length, initial length&lt;/code&gt;: the number of bytes taken by this CU header in the &lt;code&gt;.debug_info&lt;/code&gt; section (this does not include the number of bytes it takes to store the length itself, so either 4 or 12 bytes)&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;version, uint16&lt;/code&gt;: DWARF version number&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;unit_type, uint8&lt;/code&gt;: type of compilation unit (this field was added in DWARF v5 and there is no such field in v4 or below, see DWARF v5, section 3.1 for more details, and for our purposes we only care about &lt;code&gt;DW_UT_compile&lt;/code&gt;)&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;debug_abbrev_offset, uint32 if is_32_bit, else uint64&lt;/code&gt;: how far to seek in to the abbrev section in order to find this CU&#39;s abbrev table&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;addr_size, uint8&lt;/code&gt;: how large an address is in this CU&lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;    &lt;p&gt;&#xA;        So we can parse a CU header with something like:&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;func parseCUHeader(reader *BinaryReader) *CUHeader {&#xA;    header := &amp;CUHeader{}&#xA;&#xA;    header.length = readInitialLength(reader)&#xA;&#xA;    // we know it&#39;s a 32 bit binary if our initial length&#xA;    // was 4 bytes, not 12&#xA;    header.is32Bit = reader.Offset() == 4&#xA;&#xA;    header.version, _ = Read[uint16](reader)&#xA;&#xA;    if header.version &gt;= 5 {&#xA;        // this field was added in DWARF v5&#xA;        header.unitType, _ = Read[uint8](reader)&#xA;&#xA;        // DWARF v5 changes the order of fields (switches abbrev_offset and addr_size)&#xA;        header.addrSize, _ = Read[uint8](reader)&#xA;        header.debugAbbrevOffset = parseAbbrevOffset(reader, header)&#xA;    } else {&#xA;        header.debugAbbrevOffset = parseAbbrevOffset(reader, header)&#xA;        header.addrSize, _ = Read[uint8](reader)&#xA;    }&#xA;&#xA;    return header&#xA;}&#xA;&#xA;func parseAbbrevOffset(reader *BinaryReader, header *CUHeader) int {&#xA;    if header.is32Bit {&#xA;        offset, _ := Read[uint32](reader)&#xA;        return int(offset)&#xA;    }&#xA;&#xA;    offset, _ := Read[uint64](reader)&#xA;    return int(offset)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;    &lt;p&gt;&#xA;        You can check your work against &lt;code&gt;readelf --debug-dump=info cloop&lt;/code&gt;:&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;Contents of the .debug_info section:&#xA;&#xA;  Compilation Unit @ offset 0:&#xA;   Length:        0x320 (32-bit)&#xA;   Version:       5&#xA;   Unit Type:     DW_UT_compile (1)&#xA;   Abbrev Offset: 0&#xA;   Pointer Size:  8&#xA;&#xA;etc...&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;compilation-units&#34; href=&#34;#compilation-units&#34;&gt;&lt;h4&gt;Compilation Units&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        Now, we&#39;re ready to begin parsing the DIE tree in this compilation unit! What we really want at the end of this section is actually just an array of DIEs, but we do need to interpret them as a tree in order to parse the &lt;code&gt;.debug_info&lt;/code&gt; section correctly.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        First, we should pick an abbrev table by searching the list of abbrev table offsets for the one that matches with the &lt;code&gt;debug_abbrev_offset&lt;/code&gt; field from the CU header. This is the table we&#39;ll use for the rest of parsing this CU. This code is already handled in the &#34;outer loop&#34; example above.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Next, we&#39;ll begin reading through the &lt;code&gt;.debug_info&lt;/code&gt; section to determine the list of DIEs, their depth in the tree, and the &lt;code&gt;form&lt;/code&gt; of each of their attributes. DWARF forms are basically just data types, so this is just saying that we want to figure out how to interpret the bytes of each attribute (should we read a &lt;code&gt;uint8&lt;/code&gt;, a &lt;code&gt;string&lt;/code&gt;, etc.). Forms are an enum with the prefix &lt;code&gt;DW_FORM_&lt;/code&gt;.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Additionally, based on which form we&#39;re looking at, we should also be sure to store its associated attribute class for more information on how to interpret this data. We won&#39;t be using this much since our code is just a demo, but real-world, robust parsers will use this a lot. Read the spec to learn more.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        To complete this, we should:&#xA;    &lt;/p&gt;&#xA;    &lt;ul&gt;&#xA;        &lt;li&gt;Read a ULEB128, that&#39;s the code of an item in the abbrev table that we will look up&lt;/li&gt;&#xA;        &lt;li&gt;If the code is zero, we are either done reading DIEs for the entire compilation unit, or we&#39;re done reading child DIEs for a node in the tree&#xA;        &lt;ul&gt;&#xA;            &lt;li&gt;If you are at the root level of the DIE tree, you&#39;re done parsing this compile unit&lt;/li&gt;&#xA;            &lt;li&gt;Else, pop up one level in the tree and continue&lt;/li&gt;&#xA;        &lt;/ul&gt;&#xA;        &lt;li&gt;Use the code to look up the abbrev decl we care about in the abbrev table&lt;/li&gt;&#xA;        &lt;li&gt;For each attribute in the decl we&#39;re going to want to choose which type of form to use to interpret the bytes of this attribute, and read that many bytes from the &lt;code&gt;.debug_info&lt;/code&gt; section&lt;/li&gt;&#xA;        &lt;ul&gt;&#xA;            &lt;li&gt;Note that list of form types is very long, so I&#39;ll only implement enough to parse &lt;code&gt;cloop&lt;/code&gt; on my machine in the code sample below. Refer to the documentation for the version(s) of DWARF you care about to see the full list.&lt;/li&gt;&#xA;        &lt;/ul&gt;&#xA;        &lt;li&gt;If &lt;code&gt;DW_CHILDREN_yes&lt;/code&gt; was set on the decl, push an item on to the DIE tree&lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;    &lt;p&gt;&#xA;        Easy to say, but somewhat tedious to actually do. First, let&#39;s write that algorithm:&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;func parseCU(&#xA;    reader *BinaryReader,&#xA;    header *CUHeader,&#xA;    sections *DWARFSections,&#xA;    abbrev abbrevTable,&#xA;) *CU {&#xA;    dies := []DIE{}       // list of all DIEs&#xA;    dieTree := []uint64{} // stack of abbrev codes&#xA;&#xA;    for {&#xA;        dieOffset := reader.Offset()&#xA;&#xA;        abbrevCode, _ := leb128.DecodeU64(reader)&#xA;        if abbrevCode == 0 {&#xA;            if len(dieTree) == 1 {&#xA;                break // we&#39;re done&#xA;            }&#xA;&#xA;            // this DIE has no more children, pop the stack&#xA;            dieTree = dieTree[:len(dieTree)-1]&#xA;            continue&#xA;        }&#xA;&#xA;        abbrevDecl := abbrev.decls[abbrevCode]&#xA;        die := DIE{&#xA;            offset: dieOffset,&#xA;            depth:  len(dieTree),&#xA;            tag:    abbrevDecl.tag,&#xA;        }&#xA;&#xA;        for _, attr := range abbrevDecl.attrs {&#xA;            form := chooseFormAndAdvanceBySize(reader, header, sections, attr)&#xA;            die.forms = append(die.forms, form)&#xA;        }&#xA;&#xA;        dies = append(dies, die)&#xA;        if abbrevDecl.hasChildren == 1 {&#xA;            dieTree = append(dieTree, abbrevCode)&#xA;        }&#xA;    }&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;    &lt;p&gt;&#xA;        Next, let&#39;s define a few types and &#34;enums&#34; (Go doesn&#39;t really have enums):&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;type DIE struct {&#xA;    offset int&#xA;    depth  int&#xA;    tag    uint64&#xA;    forms  []Form&#xA;}&#xA;&#xA;type Form struct {&#xA;    data  any&#xA;    class Class&#xA;}&#xA;&#xA;type DWARFForm int&#xA;&#xA;const (&#xA;    // we&#39;re not going to use all of these today, but a real parser should&#xA;    DW_FORM_addr           DWARFForm = 0x01&#xA;    DW_FORM_block2         DWARFForm = 0x03&#xA;    DW_FORM_block4         DWARFForm = 0x04&#xA;    DW_FORM_data2          DWARFForm = 0x05&#xA;    DW_FORM_data4          DWARFForm = 0x06&#xA;    DW_FORM_data8          DWARFForm = 0x07&#xA;    DW_FORM_string         DWARFForm = 0x08&#xA;    DW_FORM_block          DWARFForm = 0x09&#xA;    DW_FORM_block1         DWARFForm = 0x0a&#xA;    DW_FORM_data1          DWARFForm = 0x0b&#xA;    DW_FORM_flag           DWARFForm = 0x0c&#xA;    DW_FORM_sdata          DWARFForm = 0x0d&#xA;    DW_FORM_strp           DWARFForm = 0x0e&#xA;    DW_FORM_udata          DWARFForm = 0x0f&#xA;    DW_FORM_ref_addr       DWARFForm = 0x10&#xA;    DW_FORM_ref1           DWARFForm = 0x11&#xA;    DW_FORM_ref2           DWARFForm = 0x12&#xA;    DW_FORM_ref4           DWARFForm = 0x13&#xA;    DW_FORM_ref8           DWARFForm = 0x14&#xA;    DW_FORM_ref_udata      DWARFForm = 0x15&#xA;    DW_FORM_indirect       DWARFForm = 0x16&#xA;    DW_FORM_sec_offset     DWARFForm = 0x17&#xA;    DW_FORM_exprloc        DWARFForm = 0x18&#xA;    DW_FORM_flag_present   DWARFForm = 0x19&#xA;    DW_FORM_strx           DWARFForm = 0x1a&#xA;    DW_FORM_addrx          DWARFForm = 0x1b&#xA;    DW_FORM_ref_sup4       DWARFForm = 0x1c&#xA;    DW_FORM_strp_sup       DWARFForm = 0x1d&#xA;    DW_FORM_data16         DWARFForm = 0x1e&#xA;    DW_FORM_line_strp      DWARFForm = 0x1f&#xA;    DW_FORM_ref_sig8       DWARFForm = 0x20&#xA;    DW_FORM_implicit_const DWARFForm = 0x21&#xA;    DW_FORM_loclistx       DWARFForm = 0x22&#xA;    DW_FORM_rnglistx       DWARFForm = 0x23&#xA;    DW_FORM_ref_sup8       DWARFForm = 0x24&#xA;    DW_FORM_strx1          DWARFForm = 0x25&#xA;    DW_FORM_strx2          DWARFForm = 0x26&#xA;    DW_FORM_strx3          DWARFForm = 0x27&#xA;    DW_FORM_strx4          DWARFForm = 0x28&#xA;    DW_FORM_addrx1         DWARFForm = 0x29&#xA;    DW_FORM_addrx2         DWARFForm = 0x2a&#xA;    DW_FORM_addrx3         DWARFForm = 0x2b&#xA;    DW_FORM_addrx4         DWARFForm = 0x2c&#xA;&#xA;    // extensions, etc...&#xA;)&#xA;&#xA;type Class int&#xA;&#xA;const (&#xA;    address Class = iota&#xA;    addrptr&#xA;    block&#xA;    constant&#xA;    exprloc&#xA;    flag&#xA;    lineptr&#xA;    loclist&#xA;    loclistptr&#xA;    macptr&#xA;    rnglist&#xA;    rnglistptr&#xA;    reference&#xA;    str&#xA;    stroffsetpt&#xA;)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;    &lt;p&gt;&#xA;        Then, we can write our &lt;code&gt;chooseFormAndAdvanceBySize&lt;/code&gt; function and a couple helpers:&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;func chooseFormAndAdvanceBySize(&#xA;    reader *BinaryReader,&#xA;    header *CUHeader,&#xA;    sections *DWARFSections,&#xA;    attr abbrevAttribute,&#xA;) Form {&#xA;    form := Form{}&#xA;&#xA;    dwarfForm := DWARFForm(attr.form)&#xA;    switch dwarfForm {&#xA;&#xA;    // read N bytes of data as a constant value&#xA;    case DW_FORM_data1:&#xA;        form.data, _ = Read[uint8](reader)&#xA;        form.class = constant&#xA;    case DW_FORM_data2:&#xA;        form.data, _ = Read[uint16](reader)&#xA;        form.class = constant&#xA;    case DW_FORM_data4:&#xA;        form.data, _ = Read[uint32](reader)&#xA;        form.class = constant&#xA;    case DW_FORM_data8:&#xA;        form.data, _ = Read[uint64](reader)&#xA;        form.class = constant&#xA;    case DW_FORM_sdata:&#xA;        form.data, _ = leb128.DecodeS64(reader)&#xA;        form.class = constant&#xA;    case DW_FORM_udata:&#xA;        form.data, _ = leb128.DecodeU64(reader)&#xA;        form.class = constant&#xA;&#xA;    // read an address&#xA;    case DW_FORM_addr:&#xA;        form.data, _ = Read[uintptr](reader)&#xA;        form.class = address&#xA;&#xA;    // read a reference of N bytes&#xA;    case DW_FORM_ref_addr:&#xA;        form.data = readOffset(header, reader)&#xA;        form.class = reference&#xA;    case DW_FORM_ref1:&#xA;        form.data, _ = Read[uint8](reader)&#xA;        form.class = reference&#xA;    case DW_FORM_ref2:&#xA;        form.data, _ = Read[uint16](reader)&#xA;        form.class = reference&#xA;    case DW_FORM_ref4:&#xA;        form.data, _ = Read[uint32](reader)&#xA;        form.class = reference&#xA;    case DW_FORM_ref8:&#xA;        form.data, _ = Read[uint64](reader)&#xA;        form.class = reference&#xA;&#xA;    // flags&#xA;    case DW_FORM_flag:&#xA;        form.data, _ = Read[uint8](reader)&#xA;        form.class = flag&#xA;    case DW_FORM_flag_present:&#xA;        form.data = []byte{1} // just indicates true&#xA;        form.class = flag&#xA;&#xA;    // strings&#xA;    case DW_FORM_string:&#xA;        // read a string from the .debug_info section&#xA;        form.data = readNullTerminatedString(reader)&#xA;        form.class = str&#xA;    case DW_FORM_strp:&#xA;        // read a string from the .debug_str section&#xA;        offset := readOffset(header, reader)&#xA;        strSection := sections.str[offset:]&#xA;        strReader := NewBinaryReader(bytes.NewBuffer(strSection))&#xA;        form.data = readNullTerminatedString(strReader)&#xA;        form.class = str&#xA;    case DW_FORM_line_strp: // first introduced in DWARF v5&#xA;        // read a string from the .debug_line_str section&#xA;        offset := readOffset(header, reader)&#xA;        strSection := sections.line_str[offset:]&#xA;        strReader := NewBinaryReader(bytes.NewBuffer(strSection))&#xA;        form.data = readNullTerminatedString(strReader)&#xA;        form.class = str&#xA;&#xA;    // read a DWARF expression as an N byte buffer&#xA;    // (much more on these in a later post!)&#xA;    case DW_FORM_exprloc:&#xA;        length, _ := leb128.DecodeU64(reader)&#xA;        buf := make([]byte, length)&#xA;        reader.Read(buf)&#xA;        form.data = buf&#xA;        form.class = exprloc&#xA;&#xA;    // offset in to one of many sections based on the attribute&#xA;    case DW_FORM_sec_offset:&#xA;        form.data = readOffset(header, reader)&#xA;&#xA;        // there are many more of these, and this is a real pain&#xA;        // when writing a real parser, so be sure to RTFM&#xA;        switch attr.form {&#xA;        case 0x10: // DW_AT_stmt_list&#xA;            form.class = lineptr&#xA;        }&#xA;&#xA;    // this is a hard-coded constant value that comes from&#xA;    // the attribute itself in the .debug_abbrev section&#xA;    case DW_FORM_implicit_const:&#xA;        form.data = attr.implicitConstVal&#xA;        form.class = constant&#xA;    }&#xA;&#xA;    return form&#xA;}&#xA;&#xA;func readOffset(header *CUHeader, reader *BinaryReader) uint64 {&#xA;    if header.is32Bit {&#xA;        val, _ := Read[uint32](reader)&#xA;        return uint64(val)&#xA;    }&#xA;&#xA;    val, _ := Read[uint64](reader)&#xA;    return val&#xA;}&#xA;&#xA;func readNullTerminatedString(reader *BinaryReader) string {&#xA;    buf := []byte{}&#xA;    ch, _ := Read[uint8](reader)&#xA;    for ch != 0 {&#xA;        buf = append(buf, ch)&#xA;        ch, _ = Read[uint8](reader)&#xA;    }&#xA;    return string(buf)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;    &lt;p&gt;&#xA;        Phew! That was a lot. However, now we have a full DIE tree! Be sure to check your work against &lt;code&gt;dwarfdump cloop&lt;/code&gt; because a lot of these are subtle and it&#39;s easy to make mistakes, plus a single mistake tends to lead to cascading failures (i.e. if you read one byte instead of two, all subsequent reads are now off by one).&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;summary&#34; href=&#34;#summary&#34;&gt;&lt;h4&gt;Summary&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        Today, we parsed the &lt;code&gt;.debug_abbrev&lt;/code&gt; and &lt;code&gt;.debug_info&lt;/code&gt; sections to ultimately construct a tree of debug information entries.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        As was mentioned throughout the article, there&#39;s a lot more nuance writing a &lt;code&gt;.debug_info&lt;/code&gt; parser that&#39;s able to handle any binary you&#39;d encounter in the wild. Refer to the DWARF documentation for more info, but this hopefully was a helpful jumping off point for understanding the structure of the section.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Stay tuned for the next part where we&#39;ll learn how to read line number information so we can map addresses in the program text back to their source location!&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;margin-top&#34;&gt;&#xA;    &lt;i&gt;&#xA;        Thank you for reading the &lt;a href=&#34;/dwarf&#34;&gt;series on DWARF&lt;/a&gt;. Please don&#39;t hesitate to reach out with comments, questions, or errata to jim at this domain dot com.&#xA;    &lt;/i&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;    &lt;/body&gt;&#xA;&lt;/html&gt;&#xA;</content>
    <link href="https://calabro.io/dwarf/die" rel="alternate"></link>
    <summary type="html">What are DIEs? How do we write code to load them from a binary?</summary>
  </entry>
  <entry>
    <title>How DWARF Works: Parsing Just Enough ELF</title>
    <updated>2024-09-26T00:00:00Z</updated>
    <id>tag:calabro.io,2024-09-26:/dwarf/elf</id>
    <content type="html">&#xA;&lt;!DOCTYPE html&gt;&#xA;&lt;html lang=&#34;en&#34;&gt;&#xA;    &lt;head&gt;&#xA;        &lt;meta charset=&#34;utf-8&#34;&gt;&#xA;        &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no&#34; /&gt;&#xA;        &lt;meta http-equiv=&#34;Cache-control&#34; content=&#34;public&#34;&gt;&#xA;        &lt;meta name=&#34;description&#34; content=&#34;Jim Calabro&#34;&gt;&#xA;        &lt;link rel=&#34;canonical&#34; rel=&#34;noreferrer&#34; href=&#34;https://www.calabro.io&#34; /&gt;&#xA;        &lt;link rel=&#34;icon&#34; type=&#34;image/svg&#34; href=&#34;/static/favicon.svg&#34;&gt;&#xA;        &lt;title&gt;How DWARF Works: Parsing Just Enough ELF - Jim Calabro&lt;/title&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/lit.min.css&#34;&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/style.css&#34;&gt;&#xA;        &lt;link href=&#39;/static/Raleway.css&#39; rel=&#39;stylesheet&#39; type=&#39;text/css&#39; async defer&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; href=&#34;/static/font-awesome.min.css&#34;&gt;&#xA;        &#xA;        &lt;script async defer src=&#34;/static/font-awesome.min.js&#34; data-auto-replace-svg=&#34;nest&#34;&gt;&lt;/script&gt;&#xA;        &lt;script type=&#34;text/javascript&#34;&gt;&#xA;            const mobileWidth = 560;&#xA;&#xA;            function toggleMenu(event) {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    return;&#xA;                }&#xA;&#xA;                const links = document.querySelector(&#39;.links&#39;);&#xA;                const display = links.style[&#39;display&#39;];&#xA;                if (!display || display === &#39;none&#39;) {&#xA;                    links.style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    links.style.display = &#39;none&#39;;&#xA;                }&#xA;            }&#xA;&#xA;            addEventListener(&#39;resize&#39;, (event) =&gt; {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;none&#39;;&#xA;                }&#xA;            });&#xA;        &lt;/script&gt;&#xA;    &lt;/head&gt;&#xA;    &lt;body&gt;&#xA;        &lt;div class=&#34;row&#34;&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&lt;/div&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&#xA;                &lt;div class=&#34;collapsed-links&#34;&gt;&#xA;                    &lt;h3 class=&#34;name-header&#34;&gt;&lt;a href=&#34;/&#34; style=&#34;cursor: pointer&#34;&gt;Jim Calabro&lt;/a&gt;&lt;/h3&gt;&#xA;                    &lt;div onclick=&#34;toggleMenu(this)&#34;&gt;&lt;i class=&#34;hamburger-menu fa-solid fa-bars&#34;&gt;&lt;/i&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;                &lt;div class=&#34;links&#34;&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;/about&#34;&gt;About&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&#xA;                        &lt;a href=&#34;/rss&#34;&gt;RSS&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/atom&#34;&gt;Atom&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/json&#34;&gt;JSON&lt;/a&gt;&#xA;                    &lt;/div&gt;&#xA;                    &lt;div&gt;---&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://bsky.app/profile/calabro.io&#34; target=&#34;_blank&#34;&gt;Bluesky &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://github.com/jcalabro&#34; target=&#34;_blank&#34;&gt;GitHub &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.chess.com/member/jcalabro&#34; target=&#34;_blank&#34;&gt;Chess &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.last.fm/user/thekingofping&#34; target=&#34;_blank&#34;&gt;Last.fm &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&#xA;                &#xA;                &lt;div class=&#34;post-title&#34;&gt;&#xA;                    &lt;h4&gt;How DWARF Works: Parsing Just Enough ELF&lt;/h4&gt;&#xA;                    &lt;i&gt;Sep 26, 2024&lt;/i&gt;&#xA;                &lt;/div&gt;&#xA;                &#xA;                &#xA;&#xA;&lt;div class=&#34;margin-bottom&#34;&gt;&#xA;    &lt;i&gt;&#xA;        This is part of the &lt;a href=&#34;/dwarf&#34;&gt;series on DWARF&lt;/a&gt;.&#xA;    &lt;/i&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;what-are-elf-and-dwarf&#34; href=&#34;#what-are-elf-and-dwarf&#34;&gt;&lt;h4&gt;What are ELF and DWARF?&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        &lt;a href=&#34;https://en.wikipedia.org/wiki/Executable_and_Linkable_Format&#34; target=&#34;_blank&#34;&gt;Executable and Linkable Format&lt;/a&gt; (ELF) is a file format for executables, object files, shared libraries, and more that&#39;s used on various Unix-like systems. If you&#39;ve ever downloaded and run a program on Linux, you&#39;re using an ELF executable. It&#39;s akin to an &lt;code&gt;.exe&lt;/code&gt; file on Windows.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        &lt;a href=&#34;https://dwarfstd.org/&#34; target=&#34;_blank&#34;&gt;DWARF&lt;/a&gt; is a debugging information format that is used with ELF files. Debug information allows you to do neat things with a running program such as:&#xA;    &lt;/p&gt;&#xA;    &lt;ul&gt;&#xA;        &lt;li&gt;Map the compiled machine code stored inside back to the original source code&lt;/li&gt;&#xA;        &lt;li&gt;Figure out where variables are stored throughout the lifetime of a program as it executes&lt;/li&gt;&#xA;        &lt;li&gt;Unwind the callstack to generate a backtrace to find out where your program is stopped and where it came from&lt;/li&gt;&#xA;        &lt;li&gt;Much more!&lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;    &lt;p&gt;&#xA;        In this series, we&#39;ll go in to a lot of detail on topics such as these. Let&#39;s take it from the top: parsing ELF files, which contain DWARF debug information.&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;our-test-program&#34; href=&#34;#our-test-program&#34;&gt;&lt;h4&gt;Our Test Program&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        Let&#39;s parse some files! In order to do so, we&#39;ll need a program to play around with. Throughout the rest of this series, I&#39;m going to use this dead-simple C program called &lt;code&gt;cloop&lt;/code&gt; that gets its own process ID, then loops forever and prints it once per second. C is a good choice because it is simple, has no runtime, and it&#39;s well-supported by all the tools we&#39;ll be using. Here&#39;s the full program:&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;&#xA;#include &amp;lt;stdio.h&amp;gt;&#xA;&#xA;int main() {&#xA;    pid_t pid = getpid();&#xA;    unsigned long long ndx = 0;&#xA;    while (1) {&#xA;        printf(&#34;c looping (pid %d): %llu\n&#34;, pid, ndx);&#xA;        fflush(stdout);&#xA;        ndx++;&#xA;        sleep(1);&#xA;    }&#xA;&#xA;    return 0;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;    &lt;p&gt;&#xA;        I&#39;ll be compiling this with gcc 14.2.1 on Manjaro Linux with kernel version 6.9.12 using this &lt;code&gt;build.sh&lt;/code&gt; script, but feel free to play around with &lt;code&gt;CC&lt;/code&gt; and &lt;code&gt;DWARF&lt;/code&gt; as we go (it defaults to DWARF version 5):&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;#!/usr/bin/env bash&#xA;&#xA;${CC:-gcc} -Wall -Wextra -Werror -no-pie -O0 -g -gdwarf-${DWARF:-5} -o cloop main.c&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;    &lt;p&gt;&#xA;        Additionally, for this series, I&#39;ll give some short code examples of &lt;a href=&#34;https://go.dev/&#34; target=&#34;_blank&#34;&gt;Go&lt;/a&gt; code to help illustrate various concepts. I chose Go because it&#39;s popular, terse, simple to read, and has a large standard library to help us out. I&#39;ll intentionally omit error handling and not worry about writing efficient code to keep the examples short. I&#39;m using Go version 1.22.7.&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;parsing-the-elf-file-header&#34; href=&#34;#parsing-the-elf-file-header&#34;&gt;&lt;h4&gt;Parsing The ELF File Header&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        There&#39;s a lot of data contained within ELF files, but for our needs it&#39;s pretty straightforward, and we can ignore most of it. We just want to grab the raw binary of each debug info section as well as a couple facts about the executable.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Each binary file &lt;a href=&#34;https://refspecs.linuxbase.org/elf/gabi4+/ch4.eheader.html&#34; target=&#34;_blank&#34;&gt;starts with the ELF header&lt;/a&gt;, followed by various &#34;sections&#34;, each of which is just a region of the file that has a distinct job. For instance, the program text (machine code) of your executable or object is in the &lt;code&gt;.text&lt;/code&gt; section.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        We first want to open and read the contents of the binary file. It starts with 16 bytes of &#34;ELF Identifier&#34; header data. The first four of those bytes are the &lt;a href=&#34;https://en.wikipedia.org/wiki/List_of_file_signatures&#34; target=&#34;_blank&#34;&gt;magic number&lt;/a&gt; 0x7f followed by 0x45, 0x4c, 0x46, or ELF in ASCII. So using our &lt;a href=&#34;/dwarf/go#binary-reader&#34; target=&#34;_blank&#34;&gt;BinaryReader&lt;/a&gt;, we&#39;d do:&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;fileBuf, _ := os.ReadFile(filePath)&#xA;reader := NewBinaryReader(bytes.NewBuffer(fileBuf), binary.NativeEndian)&#xA;&#xA;magic := []byte{0x7f, &#39;E&#39;, &#39;L&#39;, &#39;F&#39;}&#xA;&#xA;magicBuf := make([]byte, len(magic))&#xA;reader.Read(magicBuf)&#xA;&#xA;if !slices.Equal(magic, magicBuf) {&#xA;    panic(&#34;incorrect ELF magic number&#34;)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;    &lt;p&gt;&#xA;        Next comes a the &lt;code&gt;e_ident&lt;/code&gt; header section, which contains several one-byte flags, each prefixed with &lt;code&gt;EI_&lt;/code&gt;, then some padding, which you should skip over. They are, in order:&#xA;    &lt;/p&gt;&#xA;    &lt;ul&gt;&#xA;        &lt;li&gt;&lt;code&gt;EI_CLASS&lt;/code&gt;: address size (1 for a 32-bit binary, 2 for a 64-bit binary)&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;EI_DATA&lt;/code&gt;: byte order (1 for 2&#39;s compliment little-endian, 2 for 2&#39;s compliment big-endian)&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;EI_VERSION&lt;/code&gt;: file format version (should always be 1 as of time of writing)&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;EI_OSABI&lt;/code&gt;: operating system and ABI (see Go&#39;s implementation for a &lt;a href=&#34;https://cs.opensource.google/go/go/+/master:src/debug/elf/elf.go;drc=315b6ae682a2a4e7718924a45b8b311a0fe10043;l=122&#34; target=&#34;_blank&#34;&gt;list of values&lt;/a&gt;)&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;EI_ABIVERSION&lt;/code&gt;: often ignored on Linux&lt;/li&gt;&#xA;        &lt;li&gt;padding: 7 bytes (gives us 16 total bytes in the e_indent section)&lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;    &lt;p&gt;&#xA;        Next up is the rest of the ELF file headers, again in order. Refer to the documentation or a robust implementation such as &lt;a href=&#34;https://cs.opensource.google/go/go/+/refs/tags/go1.23.1:src/debug/elf/file.go;drc=08e73e61521d7b83198407211aa232ed4f572f18;l=278&#34; target=&#34;_blank&#34;&gt;Go&lt;/a&gt; or &lt;a href=&#34;https://github.com/ziglang/zig/blob/085cc54aadb327b9910be2c72b31ea046e7e8f52/lib/std/elf.zig&#34; target=&#34;_blank&#34;&gt;Zig&lt;/a&gt; for more information on each field and their values.&#xA;    &lt;/p&gt;&#xA;    &lt;ul&gt;&#xA;        &lt;li&gt;&lt;code&gt;e_type, uint16&lt;/code&gt;: file type&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;e_machine, uint16&lt;/code&gt;: machine type&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;e_version, uint32&lt;/code&gt;: file format version&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;e_entry, uintptr&lt;/code&gt;: virtual address at which the start of the program resides&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;e_phoff, uintptr&lt;/code&gt;: byte offset from the start of the file at which the program header table is located&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;e_shoff, uintptr&lt;/code&gt;: byte offset from the start of the file at which the section header table is located&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;e_flags, uint32&lt;/code&gt;: processor-specific flags&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;e_ehsize, uint16&lt;/code&gt;: the number of bytes in this ELF header&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;e_phentsize, uint16&lt;/code&gt;: the number of bytes in one entry in the program header table (all entries are the same size)&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;e_phnum, uint16&lt;/code&gt;: the number of entries in the program header table&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;e_shentsize, uint16&lt;/code&gt;: the number of bytes in one entry in the section header table (all entries are the same size)&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;e_shnum, uint16&lt;/code&gt;: the number of entries in the section header table&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;e_shstrndx, uint16&lt;/code&gt;: the section header table index of the entry associated with the section name string table&lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;    &lt;p&gt;&#xA;         It&#39;s giving us a few facts about the binary, then a list of offsets from the start of the file that indicate where each secion is located (everything that starts with &lt;code&gt;sh&lt;/code&gt;). We&#39;ll use these fields to look up the section header table, read each entry in the table, and use those entries to find the debug sections we care about.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Note that in Go, &lt;code&gt;uintptr&lt;/code&gt; is the built-in data type for an int of your machine&#39;s address size, meaning 4 bytes on 32-bit systems, and 8 bytes on 64-bit systems.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Also, In digging through the docs, you may have noticed some values such as &lt;code&gt;LOPROC = 0xff00; HIPROC = 0xffff;&lt;/code&gt;. Both ELF and DWARF commonly reserve large ranges of high values for each processor, programming language, OS, etc. to define their own custom values for various enums. We won&#39;t be using these too much, but be aware that GNU, Go, Zig, and others commonly make use of these. You&#39;ll be able to get more information on each by reading through various compilers.&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;parsing-the-section-header-table&#34; href=&#34;#parsing-the-section-header-table&#34;&gt;&lt;h4&gt;Parsing The Section Header Table&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        Next up, we need to parse each &lt;a href=&#34;https://refspecs.linuxbase.org/elf/gabi4+/ch4.sheader.html&#34; target=&#34;_blank&#34;&gt;section header&lt;/a&gt; contained within the file. The &#34;table&#34; is just a fancy word for &#34;an array of section header entries&#34;. So once we&#39;re done, we&#39;ll have a list of where all sections start and end within the binary, the name of each section, and some other data.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        The section header table starts at the &lt;code&gt;e_shoff&lt;/code&gt;&#39;th byte in the file, and is &lt;code&gt;e_shentsize * e_shnum&lt;/code&gt; bytes long.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        The fields of each section header are:&#xA;    &lt;/p&gt;&#xA;    &lt;ul&gt;&#xA;        &lt;li&gt;&lt;code&gt;sh_name, uint32&lt;/code&gt;: name of the section as an index in to the string table&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;sh_type, uint32&lt;/code&gt;: section type &lt;a href=&#34;https://refspecs.linuxbase.org/elf/gabi4+/ch4.sheader.html#sh_type&#34; target=&#34;_blank&#34;&gt;enum&lt;/a&gt;&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;sh_flags, uintptr&lt;/code&gt;: &lt;a href=&#34;https://refspecs.linuxbase.org/elf/gabi4+/ch4.sheader.html#sh_flags&#34; target=&#34;_blank&#34;&gt;flags&lt;/a&gt; for this section&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;sh_addr, uintptr&lt;/code&gt;: the address at which this section should reside within the address space of the process, if relevant&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;sh_offset, uintptr&lt;/code&gt;: offset from the first byte of the ELF file to where the start of this section resides&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;sh_size, uintptr&lt;/code&gt;: the number of bytes in the section&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;sh_link, uint32&lt;/code&gt;: &lt;a href=&#34;https://refspecs.linuxbase.org/elf/gabi4+/ch4.sheader.html#sh_link&#34; target=&#34;_blank&#34;&gt;enum&lt;/a&gt; indicating the linkage of this section&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;sh_info, uint32&lt;/code&gt;: &lt;a href=&#34;https://refspecs.linuxbase.org/elf/gabi4+/ch4.sheader.html#sh_link&#34; target=&#34;_blank&#34;&gt;enum&lt;/a&gt; indicating extra information about this section&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;sh_addralign, uintptr&lt;/code&gt;: contraints on the alignment of addresses on the target platform (0 and 1 mean no constraints)&lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;sh_entsize, uintptr&lt;/code&gt;: if the section contains a table of fixed-size elements (i.e. a symbol table), this is the size of each element&lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;    &lt;p&gt;&#xA;        Read &lt;code&gt;e_shnum&lt;/code&gt; entries, which should be exactly enough bytes. To give an example of how this might look in code, consider:&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;type ELFSectionHeader struct {&#xA;    sh_name      uint32&#xA;    sh_type      uint32&#xA;    sh_flags     uintptr&#xA;    sh_addr      uintptr&#xA;    sh_offset    uintptr&#xA;    sh_size      uintptr&#xA;    sh_link      uint32&#xA;    sh_info      uint32&#xA;    sh_addralign uintptr&#xA;    sh_entsize   uintptr&#xA;&#xA;    // this is not part of the standard, but we&#39;ll&#xA;    // look up and store the name on this struct later&#xA;    name string&#xA;}&#xA;&#xA;sectionHeaderTable := fileBuf[shOff : shOff+uintptr(shentSize*shNum)]&#xA;sectionHeaderTableReader := NewBinaryReader(&#xA;    bytes.NewBuffer(sectionHeaderTable),&#xA;    binary.NativeEndian,&#xA;)&#xA;&#xA;sectionHeaders := []*ELFSectionHeader{}&#xA;for ndx := 0; ndx &amp;lt; int(shNum); ndx++ {&#xA;    header := &amp;ELFSectionHeader{}&#xA;    header.sh_name, _ = Read[uint32](sectionHeaderTableReader)&#xA;    header.sh_type, _ = Read[uint32](sectionHeaderTableReader)&#xA;    header.sh_flags, _ = Read[uintptr](sectionHeaderTableReader)&#xA;    header.sh_addr, _ = Read[uintptr](sectionHeaderTableReader)&#xA;    header.sh_offset, _ = Read[uintptr](sectionHeaderTableReader)&#xA;    header.sh_size, _ = Read[uintptr](sectionHeaderTableReader)&#xA;    header.sh_link, _ = Read[uint32](sectionHeaderTableReader)&#xA;    header.sh_info, _ = Read[uint32](sectionHeaderTableReader)&#xA;    header.sh_addralign, _ = Read[uintptr](sectionHeaderTableReader)&#xA;    header.sh_entsize, _ = Read[uintptr](sectionHeaderTableReader)&#xA;&#xA;    sectionHeaders = append(sectionHeaders, header)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;    &lt;p&gt;&#xA;        Once we have all this information, we&#39;re going to want to use the &lt;code&gt;sh_name&lt;/code&gt; field to look up our section name in the section header string table. This is the ELF section with index &lt;code&gt;e_shstrndx&lt;/code&gt;, named &lt;code&gt;.shstrtab&lt;/code&gt;. In my case with the test C program, it&#39;s the 35th section, though yours may be different.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        This table is a series of null-terminated strings all next to each other in one long array. You can read the entire table in to an array, then use the &lt;code&gt;sh_name&lt;/code&gt; field to find the entry at that index.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        I&#39;ll use the &lt;code&gt;sh_size&lt;/code&gt; and &lt;code&gt;sh_offset&lt;/code&gt; fields of the &lt;code&gt;e_shstrndx&lt;/code&gt;&#39;th entry to find our location within the binary:&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;sectionNames := sectionHeaders[shStrTabNdx]&#xA;&#xA;start := sectionNames.sh_offset&#xA;end := start + sectionNames.sh_size&#xA;sectionNamesBuf := fileBuf[start:end]&#xA;&#xA;for _, header := range sectionHeaders {&#xA;    for ndx := header.sh_name; ; ndx++ {&#xA;        ch := sectionNamesBuf[ndx]&#xA;        if ch == 0 {&#xA;            break&#xA;        }&#xA;        header.name += string(ch)&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;    &lt;p&gt;&#xA;        Now we&#39;re able to look up each debug information section by name!&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;debug-info-sections&#34; href=&#34;#debug-info-sections&#34;&gt;&lt;h4&gt;Debug Info Sections&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        There&#39;s a fair number of sections in there! You may recognize some of them, but for the most part, we care about the ones that start with &lt;code&gt;.debug_&lt;/code&gt;, though we also care about &lt;code&gt;.eh_frame&lt;/code&gt;. If you want to check your work, you can with &lt;code&gt;readelf --headers cloop&lt;/code&gt;. We&#39;ll get in to what each of these sections mean over time.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        There are actually a few sections that are missing from the binary on my machine that we also would want to save if they were present (there are some sections that were present in older versions of DWARF for instance, but were dropped when v5 was released). We&#39;ll want to take the content of each one of those sections and save them for parsing later:&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;    type DWARFSections struct {&#xA;        abbrev      []byte&#xA;        line        []byte&#xA;        info        []byte&#xA;        addr        []byte&#xA;        aranges     []byte&#xA;        frame       []byte&#xA;        eh_frame    []byte&#xA;        line_str    []byte&#xA;        loc         []byte&#xA;        loclists    []byte&#xA;        names       []byte&#xA;        macinfo     []byte&#xA;        macro       []byte&#xA;        pubnames    []byte&#xA;        pubtypes    []byte&#xA;        ranges      []byte&#xA;        rnglists    []byte&#xA;        str         []byte&#xA;        str_offsets []byte&#xA;        types       []byte&#xA;    }&#xA;&#xA;    getSection := func(header *ELFSectionHeader) []byte {&#xA;        start := header.sh_offset&#xA;        end := header.sh_offset + header.sh_size&#xA;        return fileBuf[start:end]&#xA;    }&#xA;&#xA;    sections := &amp;DWARFSections{}&#xA;    for _, header := range sectionHeaders {&#xA;        switch header.name {&#xA;        case &#34;.debug_abbrev&#34;:&#xA;            sections.abbrev = getSection(header)&#xA;        case &#34;.debug_line&#34;:&#xA;            sections.line = getSection(header)&#xA;        case &#34;.debug_info&#34;:&#xA;            sections.info = getSection(header)&#xA;        case &#34;.debug_addr&#34;:&#xA;            sections.addr = getSection(header)&#xA;        case &#34;.debug_aranges&#34;:&#xA;            sections.aranges = getSection(header)&#xA;        case &#34;.debug_frame&#34;:&#xA;            sections.frame = getSection(header)&#xA;        case &#34;.eh_frame&#34;:&#xA;            sections.eh_frame = getSection(header)&#xA;        case &#34;.debug_line_str&#34;:&#xA;            sections.line_str = getSection(header)&#xA;        case &#34;.debug_loc&#34;:&#xA;            sections.loc = getSection(header)&#xA;        case &#34;.debug_loclists&#34;:&#xA;            sections.loclists = getSection(header)&#xA;        case &#34;.debug_names&#34;:&#xA;            sections.names = getSection(header)&#xA;        case &#34;.debug_macinfo&#34;:&#xA;            sections.macinfo = getSection(header)&#xA;        case &#34;.debug_macro&#34;:&#xA;            sections.macro = getSection(header)&#xA;        case &#34;.debug_pubnames&#34;:&#xA;            sections.pubnames = getSection(header)&#xA;        case &#34;.debug_pubtypes&#34;:&#xA;            sections.pubtypes = getSection(header)&#xA;        case &#34;.debug_ranges&#34;:&#xA;            sections.ranges = getSection(header)&#xA;        case &#34;.debug_rnglists&#34;:&#xA;            sections.rnglists = getSection(header)&#xA;        case &#34;.debug_str&#34;:&#xA;            sections.str = getSection(header)&#xA;        case &#34;.debug_str_offsets&#34;:&#xA;            sections.str_offsets = getSection(header)&#xA;        case &#34;.debug_types&#34;:&#xA;            sections.types = getSection(header)&#xA;        }&#xA;    }&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;    &lt;p&gt;&#xA;        Now we&#39;re &lt;i&gt;almost&lt;/i&gt; ready to start parsing those debug info sections in to something that allows us to inspect a running program!&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;pie&#34; href=&#34;#pie&#34;&gt;&lt;h4&gt;PIE 🥧&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        The last thing we&#39;ll want to do with our ELF file for now is examine it to determine if it is a &lt;a href=&#34;https://en.wikipedia.org/wiki/Position-independent_code&#34; target=&#34;_blank&#34;&gt;position independent executable&lt;/a&gt; (PIE, also known as position independent code or PIC). PIE means that the code can be loaded and executed at any address in the process&#39; memory space, and is the opposite of aboslute code, which must be loaded at a fixed address in memory. You can enable PIE with the &lt;code&gt;-fPIC&lt;/code&gt; &lt;a href=&#34;https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#index-fpic&#34; target=&#34;_blank&#34;&gt;compiler flag&lt;/a&gt; in gcc and clang. It ultimately doesn&#39;t restrict our capabilities as a debugger at all, it just means that we need to look up where in the process&#39; address space our code is loaded when we start the program (we&#39;ll do that much later).&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        For now, we can determine if we&#39;re PIC based on the value of the &lt;code&gt;FLAGS_1&lt;/code&gt; field in the &lt;code&gt;.dynamic&lt;/code&gt; section like so:&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;var dynamicHeader *ELFSectionHeader&#xA;for _, header := range sectionHeaders {&#xA;    if header.name == &#34;.dynamic&#34; {&#xA;        dynamicHeader = header&#xA;        break&#xA;    }&#xA;}&#xA;&#xA;pie := false&#xA;dynamicBuf := getSection(dynamicHeader)&#xA;dynamicReader := NewBinaryReader(bytes.NewBuffer(dynamicBuf), binary.NativeEndian)&#xA;for {&#xA;    tag, _ := Read[uintptr](dynamicReader)&#xA;    val, err := Read[uintptr](dynamicReader)&#xA;&#xA;    if tag == 0x6fff_fffb { // DT_FLAGS_1&#xA;        if (val &amp; 0x0800_0000) &gt; 0 { // DF_1_PIE&#xA;            pie = true&#xA;            break&#xA;        }&#xA;    }&#xA;&#xA;    if err == io.EOF {&#xA;        break&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;    &lt;p&gt;&#xA;        You can check your work on this using &lt;code&gt;readelf --dynamic cloop&lt;/code&gt;. You may want to try compiling cloop with &lt;code&gt;-fPIE&lt;/code&gt; and without &lt;code&gt;-no-pie&lt;/code&gt; and re-running your parser to make sure things are looking good.&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;summary&#34; href=&#34;#summary&#34;&gt;&lt;h4&gt;Summary&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        That&#39;s it for today! We learned what ELF and DWARF are as well as how to parse just enough ELF to get the debug information sections we care about. ELF is probably the easiest section in this series, so strap in.&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;margin-top&#34;&gt;&#xA;    &lt;i&gt;&#xA;        Thank you for reading the &lt;a href=&#34;/dwarf&#34;&gt;series on DWARF&lt;/a&gt;. Please don&#39;t hesitate to reach out with comments, questions, or errata to jim at this domain dot com.&#xA;    &lt;/i&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;    &lt;/body&gt;&#xA;&lt;/html&gt;&#xA;</content>
    <link href="https://calabro.io/dwarf/elf" rel="alternate"></link>
    <summary type="html">What is an ELF file? Why do we need it? How do we parse just enough of one to get the information we care about?</summary>
  </entry>
  <entry>
    <title>How DWARF Works: Table of Contents and Introduction</title>
    <updated>2024-09-25T00:00:00Z</updated>
    <id>tag:calabro.io,2024-09-25:/dwarf</id>
    <content type="html">&#xA;&lt;!DOCTYPE html&gt;&#xA;&lt;html lang=&#34;en&#34;&gt;&#xA;    &lt;head&gt;&#xA;        &lt;meta charset=&#34;utf-8&#34;&gt;&#xA;        &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no&#34; /&gt;&#xA;        &lt;meta http-equiv=&#34;Cache-control&#34; content=&#34;public&#34;&gt;&#xA;        &lt;meta name=&#34;description&#34; content=&#34;Jim Calabro&#34;&gt;&#xA;        &lt;link rel=&#34;canonical&#34; rel=&#34;noreferrer&#34; href=&#34;https://www.calabro.io&#34; /&gt;&#xA;        &lt;link rel=&#34;icon&#34; type=&#34;image/svg&#34; href=&#34;/static/favicon.svg&#34;&gt;&#xA;        &lt;title&gt;How DWARF Works: Table of Contents and Introduction - Jim Calabro&lt;/title&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/lit.min.css&#34;&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/style.css&#34;&gt;&#xA;        &lt;link href=&#39;/static/Raleway.css&#39; rel=&#39;stylesheet&#39; type=&#39;text/css&#39; async defer&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; href=&#34;/static/font-awesome.min.css&#34;&gt;&#xA;        &#xA;        &lt;script async defer src=&#34;/static/font-awesome.min.js&#34; data-auto-replace-svg=&#34;nest&#34;&gt;&lt;/script&gt;&#xA;        &lt;script type=&#34;text/javascript&#34;&gt;&#xA;            const mobileWidth = 560;&#xA;&#xA;            function toggleMenu(event) {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    return;&#xA;                }&#xA;&#xA;                const links = document.querySelector(&#39;.links&#39;);&#xA;                const display = links.style[&#39;display&#39;];&#xA;                if (!display || display === &#39;none&#39;) {&#xA;                    links.style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    links.style.display = &#39;none&#39;;&#xA;                }&#xA;            }&#xA;&#xA;            addEventListener(&#39;resize&#39;, (event) =&gt; {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;none&#39;;&#xA;                }&#xA;            });&#xA;        &lt;/script&gt;&#xA;    &lt;/head&gt;&#xA;    &lt;body&gt;&#xA;        &lt;div class=&#34;row&#34;&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&lt;/div&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&#xA;                &lt;div class=&#34;collapsed-links&#34;&gt;&#xA;                    &lt;h3 class=&#34;name-header&#34;&gt;&lt;a href=&#34;/&#34; style=&#34;cursor: pointer&#34;&gt;Jim Calabro&lt;/a&gt;&lt;/h3&gt;&#xA;                    &lt;div onclick=&#34;toggleMenu(this)&#34;&gt;&lt;i class=&#34;hamburger-menu fa-solid fa-bars&#34;&gt;&lt;/i&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;                &lt;div class=&#34;links&#34;&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;/about&#34;&gt;About&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&#xA;                        &lt;a href=&#34;/rss&#34;&gt;RSS&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/atom&#34;&gt;Atom&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/json&#34;&gt;JSON&lt;/a&gt;&#xA;                    &lt;/div&gt;&#xA;                    &lt;div&gt;---&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://bsky.app/profile/calabro.io&#34; target=&#34;_blank&#34;&gt;Bluesky &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://github.com/jcalabro&#34; target=&#34;_blank&#34;&gt;GitHub &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.chess.com/member/jcalabro&#34; target=&#34;_blank&#34;&gt;Chess &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.last.fm/user/thekingofping&#34; target=&#34;_blank&#34;&gt;Last.fm &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&#xA;                &#xA;                &lt;div class=&#34;post-title&#34;&gt;&#xA;                    &lt;h4&gt;How DWARF Works: Table of Contents and Introduction&lt;/h4&gt;&#xA;                    &lt;i&gt;Sep 25, 2024&lt;/i&gt;&#xA;                &lt;/div&gt;&#xA;                &#xA;                &#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;table-of-contents&#34; href=&#34;#table-of-contents&#34;&gt;&lt;h4&gt;Table of Contents&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;h5&gt;&lt;i&gt;Part One: Parsing Debug Info&lt;/i&gt;&lt;/h5&gt;&#xA;    &lt;ul&gt;&#xA;        &lt;li&gt;&lt;a href=&#34;/dwarf/elf&#34;&gt;Parsing ELF Files&lt;/a&gt;&lt;/li&gt;&#xA;        &lt;li&gt;&lt;a href=&#34;/dwarf/die&#34;&gt;Debug Information Entries&lt;/a&gt;&lt;/li&gt;&#xA;        &lt;li&gt;Line Number Information&lt;/li&gt;&#xA;        &lt;li&gt;Address Ranges&lt;/li&gt;&#xA;        &lt;li&gt;Frame Tables&lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;    &lt;h5 class=&#34;margin-top&#34;&gt;&lt;i&gt;Part Two: Using Debug Info at Runtime&lt;/i&gt;&lt;/h5&gt;&#xA;    &lt;ul&gt;&#xA;        &lt;li&gt;Inspecting a Child Process&lt;/li&gt;&#xA;        &lt;li&gt;Stack Unwinding&lt;/li&gt;&#xA;        &lt;li&gt;Finding Variable Values&lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;    &lt;h5 class=&#34;margin-top&#34;&gt;&lt;i&gt;Appendix&lt;/i&gt;&lt;/h5&gt;&#xA;    &lt;ul&gt;&#xA;        &lt;li&gt;&lt;a href=&#34;/dwarf/go&#34;&gt;Go Code Reference&lt;/a&gt;&lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;&lt;/div&gt;&#xA;&lt;div class=&#34;margin-top&#34;&gt;&#xA;    &lt;a name=&#34;introduction&#34; href=&#34;#introduction&#34;&gt;&lt;h4&gt;Introduction&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        Welcome to the series on parsing and using DWARF debug info!&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Its purpose is to provide a user-friendly starting point for learning about how debug information and debuggers work on Linux. It&#39;s written from the perspective of a debugger author, not a compiler author.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Non-goals include being a 100% comprehensive guide, providing details on specific languages or compilers, or giving details on other platforms.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        If you want to learn more beyond what&#39;s contained in these posts, I highly recommend reading all relevant versions of the source documentation as well as other reference implementations that are pretty decent (links below).&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        If you have questions, comments, or corrections, please don&#39;t hesitate to reach out via email to jim at this domain dot com. I love hearing from you.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Thanks for reading!&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;div class=&#34;margin-top&#34;&gt;&#xA;    &lt;a name=&#34;why-are-you-writing-this&#34; href=&#34;#why-are-you-writing-this&#34;&gt;&lt;h4&gt;Why Are You Writing This?&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        I feel strongly that the state of the art in debuggers needs to be improved on Linux. We have &lt;a href=&#34;https://www.gnu.org/software/gdb/gdb.html&#34; target=&#34;_blank&#34;&gt;gdb&lt;/a&gt; and &lt;a href=&#34;https://lldb.llvm.org/&#34; target=&#34;_blank&#34;&gt;lldb&lt;/a&gt; (plus about a million graphical front ends, none of which are good). All of these tools take a long time to learn, are slow to use, and they don&#39;t make the data you need readily apparent.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        There&#39;s also &lt;a href=&#34;https://rr-project.org/&#34; target=&#34;_blank&#34;&gt;rr&lt;/a&gt; and &lt;a href=&#34;https://pernos.co/&#34; target=&#34;_blank&#34;&gt;Pernosco&lt;/a&gt;, which are astounding technical achievements, but suffer the same issues of taking a long time to get from &#34;I don&#39;t understand what my program is doing&#34; to &#34;I understand what my program is doing&#34; (especially for simple issues). They do a great job of helping solve some of the hardest bugs much faster, but they&#39;re not tools I use every day.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Comparatively, Windows has &lt;a href=&#34;https://visualstudio.microsoft.com/&#34; target=&#34;_blank&#34;&gt;Visual Studio&lt;/a&gt;, &lt;a href=&#34;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/&#34; target=&#34;_blank&#34;&gt;WinDbg&lt;/a&gt;, &lt;a href=&#34;https://github.com/x64dbg/x64dbg&#34; target=&#34;_blank&#34;&gt;x64dbg&lt;/a&gt;, &lt;a href=&#34;https://remedybg.itch.io/remedybg&#34; target=&#34;_blank&#34;&gt;RemedyBG&lt;/a&gt;, and &lt;a href=&#34;https://github.com/EpicGamesExt/raddebugger&#34; target=&#34;_blank&#34;&gt;the RAD Debugger&lt;/a&gt;. Each of these tools do the job of making the information I need about my program available at my fingertips as fast as possible, but unfortunately, they all are Windows-only. Despite its incredible bloat, it&#39;s awesome to be able to just press F5 in Visual Studio and have a ton of helpful information at your disposal.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Graphical debuggers are hands-down better than terminal based ones because debugging is fundamentally a problem of visualization. I want to see lots of relevant information all at once every time I step, and the terminal doesn&#39;t facilitate that nearly as well as a purpose-built GUI.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Additionally, terminal based debuggers are inherently worse than graphical ones because they dont allow me to get at the information I need quickly. Starting a debugging session by writing a &lt;i&gt;.gdbinit&lt;/i&gt; file and using the REPL is much slower than using Visual Studio, where you just press F5.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        I find that most people simply don&#39;t use gdb and instead reach for printf-debugging since it&#39;s quicker and easier for the vast majority of use-cases. As a practitioner, that&#39;s the correct choice, because what is the point of a debugger that doesn&#39;t save you time? However, it leaves a ton of power on the table and is a local maximum that we must push through.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        I am certain this situation can be improved. The tools to write a robust grapical debugger for Linux already exist; it&#39;s just a long, tedious road to actually get it done.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;        Given that Linux is the most widely deployed operating system on the planet&lt;sup&gt;&lt;a href=&#34;https://gs.statcounter.com/os-market-share&#34; target=&#34;_blank&#34;&gt;1&lt;/a&gt;, &lt;a href=&#34;https://www.fortunebusinessinsights.com/server-operating-system-market-106601&#34; target=&#34;_blank&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;, further investment in tooling in this area is a no-brainer. Hopefully this series can contribute towards a bright future of Linux development tools in some small way.&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;div class=&#34;margin-top&#34;&gt;&#xA;    &lt;a name=&#34;further-reading&#34; href=&#34;#further-reading&#34;&gt;&lt;h4&gt;Further Reading&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;All of the code I link to is non-GPL.&lt;/p&gt;&#xA;    &lt;ul&gt;&#xA;        &lt;li&gt;&lt;a href=&#34;https://dwarfstd.org/&#34; target=&#34;_blank&#34;&gt;DWARF Documentation&lt;/a&gt; (be sure to download all the versions that are relevant to your needs, I typically use versions 3, 4, and 5)&lt;/li&gt;&#xA;        &lt;li&gt;&lt;a href=&#34;https://refspecs.linuxfoundation.org/elf/gabi4+/contents.html&#34; target=&#34;_blank&#34;&gt;ELF Documentation&lt;/a&gt;&lt;/li&gt;&#xA;        &lt;li&gt;&lt;a href=&#34;https://pkg.go.dev/debug/elf#NewFile&#34; target=&#34;_blank&#34;&gt;Go&#39;s ELF Parser&lt;/a&gt;&lt;/li&gt;&#xA;        &lt;li&gt;&lt;a href=&#34;https://github.com/ziglang/zig/blob/master/src/link/Elf.zig&#34; target=&#34;_blank&#34;&gt;Elf.zig&lt;/a&gt;&lt;/li&gt;&#xA;        &lt;li&gt;&lt;a href=&#34;https://github.com/gimli-rs/gimli&#34; target=&#34;_blank&#34;&gt;gimli-rs&lt;/a&gt;&lt;/li&gt;&#xA;        &lt;li&gt;Ian Lance Taylor&#39;s &lt;a href=&#34;https://www.airs.com/blog/&#34; target=&#34;_blank&#34;&gt;Blog&lt;/a&gt;, in particular these posts on stack unwinding: &lt;a href=&#34;https://www.airs.com/blog/archives/460&#34; target=&#34;_blank&#34;&gt;1&lt;/a&gt;, &lt;a href=&#34;https://www.airs.com/blog/archives/462&#34; target=&#34;_blank&#34;&gt;2&lt;/a&gt;, &lt;a href=&#34;https://www.airs.com/blog/archives/464&#34; target=&#34;_blank&#34;&gt;3&lt;/a&gt;&lt;/li&gt;&#xA;        &lt;li&gt;Eli Bendersky&#39;s &lt;a href=&#34;https://eli.thegreenplace.net/tag/debuggers&#34; target=&#34;_blank&#34;&gt;writing on debuggers&lt;/a&gt;&lt;/li&gt;&#xA;        &lt;li&gt;Sy Brand&#39;s &lt;a href=&#34;https://blog.tartanllama.xyz/writing-a-linux-debugger-setup/&#34; target=&#34;_blank&#34;&gt;Building a Debugger series&lt;/a&gt;&lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;&lt;/div&gt;&#xA;&lt;div class=&#34;margin-top&#34;&gt;&#xA;    &lt;h4&gt;Useful Tools&lt;/h4&gt;&#xA;    &lt;ul&gt;&#xA;        &lt;li&gt;&lt;a href=&#34;https://man7.org/linux/man-pages/man1/readelf.1.html&#34; target=&#34;_blank&#34;&gt;readelf&lt;/a&gt;&lt;/li&gt;&#xA;        &lt;li&gt;&lt;a href=&#34;https://man7.org/linux/man-pages/man1/objdump.1.html&#34; target=&#34;_blank&#34;&gt;objdump&lt;/a&gt;&lt;/li&gt;&#xA;        &lt;li&gt;&lt;a href=&#34;https://llvm.org/docs/CommandGuide/llvm-dwarfdump.html&#34; target=&#34;_blank&#34;&gt;dwarfdump&lt;/a&gt;&lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;&lt;/div&gt;&#xA;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;    &lt;/body&gt;&#xA;&lt;/html&gt;&#xA;</content>
    <link href="https://calabro.io/dwarf" rel="alternate"></link>
    <summary type="html">This series answers questions such as &#34;what is DWARF/ELF?&#34; and &#34;How do debuggers work?&#34;</summary>
  </entry>
  <entry>
    <title>How DWARF Works: Go Code Reference</title>
    <updated>2024-09-25T00:00:00Z</updated>
    <id>tag:calabro.io,2024-09-25:/dwarf/go</id>
    <content type="html">&#xA;&lt;!DOCTYPE html&gt;&#xA;&lt;html lang=&#34;en&#34;&gt;&#xA;    &lt;head&gt;&#xA;        &lt;meta charset=&#34;utf-8&#34;&gt;&#xA;        &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no&#34; /&gt;&#xA;        &lt;meta http-equiv=&#34;Cache-control&#34; content=&#34;public&#34;&gt;&#xA;        &lt;meta name=&#34;description&#34; content=&#34;Jim Calabro&#34;&gt;&#xA;        &lt;link rel=&#34;canonical&#34; rel=&#34;noreferrer&#34; href=&#34;https://www.calabro.io&#34; /&gt;&#xA;        &lt;link rel=&#34;icon&#34; type=&#34;image/svg&#34; href=&#34;/static/favicon.svg&#34;&gt;&#xA;        &lt;title&gt;How DWARF Works: Go Code Reference - Jim Calabro&lt;/title&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/lit.min.css&#34;&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/style.css&#34;&gt;&#xA;        &lt;link href=&#39;/static/Raleway.css&#39; rel=&#39;stylesheet&#39; type=&#39;text/css&#39; async defer&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; href=&#34;/static/font-awesome.min.css&#34;&gt;&#xA;        &#xA;        &lt;script async defer src=&#34;/static/font-awesome.min.js&#34; data-auto-replace-svg=&#34;nest&#34;&gt;&lt;/script&gt;&#xA;        &lt;script type=&#34;text/javascript&#34;&gt;&#xA;            const mobileWidth = 560;&#xA;&#xA;            function toggleMenu(event) {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    return;&#xA;                }&#xA;&#xA;                const links = document.querySelector(&#39;.links&#39;);&#xA;                const display = links.style[&#39;display&#39;];&#xA;                if (!display || display === &#39;none&#39;) {&#xA;                    links.style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    links.style.display = &#39;none&#39;;&#xA;                }&#xA;            }&#xA;&#xA;            addEventListener(&#39;resize&#39;, (event) =&gt; {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;none&#39;;&#xA;                }&#xA;            });&#xA;        &lt;/script&gt;&#xA;    &lt;/head&gt;&#xA;    &lt;body&gt;&#xA;        &lt;div class=&#34;row&#34;&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&lt;/div&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&#xA;                &lt;div class=&#34;collapsed-links&#34;&gt;&#xA;                    &lt;h3 class=&#34;name-header&#34;&gt;&lt;a href=&#34;/&#34; style=&#34;cursor: pointer&#34;&gt;Jim Calabro&lt;/a&gt;&lt;/h3&gt;&#xA;                    &lt;div onclick=&#34;toggleMenu(this)&#34;&gt;&lt;i class=&#34;hamburger-menu fa-solid fa-bars&#34;&gt;&lt;/i&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;                &lt;div class=&#34;links&#34;&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;/about&#34;&gt;About&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&#xA;                        &lt;a href=&#34;/rss&#34;&gt;RSS&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/atom&#34;&gt;Atom&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/json&#34;&gt;JSON&lt;/a&gt;&#xA;                    &lt;/div&gt;&#xA;                    &lt;div&gt;---&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://bsky.app/profile/calabro.io&#34; target=&#34;_blank&#34;&gt;Bluesky &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://github.com/jcalabro&#34; target=&#34;_blank&#34;&gt;GitHub &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.chess.com/member/jcalabro&#34; target=&#34;_blank&#34;&gt;Chess &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.last.fm/user/thekingofping&#34; target=&#34;_blank&#34;&gt;Last.fm &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&#xA;                &#xA;                &lt;div class=&#34;post-title&#34;&gt;&#xA;                    &lt;h4&gt;How DWARF Works: Go Code Reference&lt;/h4&gt;&#xA;                    &lt;i&gt;Sep 25, 2024&lt;/i&gt;&#xA;                &lt;/div&gt;&#xA;                &#xA;                &#xA;&#xA;&lt;div class=&#34;margin-bottom&#34;&gt;&#xA;    &lt;i&gt;&#xA;        This is part of the &lt;a href=&#34;/dwarf&#34;&gt;series on DWARF&lt;/a&gt;.&#xA;    &lt;/i&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;div&gt;&#xA;    &lt;p&gt;&#xA;        This page contains Go helper snippets that illustrate how parsing works, but are used across many sections. These snippets often intentionally ignore error handling in favor of brevity.&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;leb128&#34; href=&#34;#leb128&#34;&gt;&lt;h4&gt;LEB128&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;&#x9;&#x9;A key integer type that is used all over the place in DWARF is signed/unsigned &lt;a href=&#34;https://en.wikipedia.org/wiki/LEB128&#34; target=&#34;_blank&#34;&gt;LEB128&lt;/a&gt;. It&#39;s a variable-width signed/unsigned integer type, though in practice, I&#39;ve never seen anything use more than 64 bits, but it&#39;s perfectly reasonable to assume that&#39;s not always the case.&#xA;    &lt;/p&gt;&#xA;    &lt;p&gt;&#xA;&#x9;&#x9;I wrote &lt;a href=&#34;https://github.com/jcalabro/leb128&#34; target=&#34;_blank&#34;&gt;a simple library&lt;/a&gt; to parse these a couple years ago, and I&#39;m going to be using it throughout the series. You can import it just like you would any other Go library, or read its implementation and write your own. Zig also has a &lt;a href=&#34;https://github.com/ziglang/zig/blob/085cc54aadb327b9910be2c72b31ea046e7e8f52/lib/std/leb128.zig&#34; target=&#34;_blank&#34;&gt;good implementation&lt;/a&gt; for reference.&#xA;    &lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;initial-length&#34; href=&#34;#initial-length&#34;&gt;&lt;h4&gt;Initial Length&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        An &lt;code&gt;initial length&lt;/code&gt; field in DWARF is a special integer encoding that takes either 4 or 12 bytes and allows you to encode either a 4 or 8 byte integer. To parse one, you read a 4-byte integer, and if it&#39;s any value other than &lt;code&gt;0xffffffff&lt;/code&gt;, you use it as-is. If it&#39;s that special value, you read another eight bytes, discard the initial four bytes you read, and just use the final eight bytes.&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;func readInitialLength(reader *BinaryReader) uintptr {&#xA;    val32, _ := Read[uint32](reader)&#xA;    if val32 != 0xffffffff {&#xA;        return uintptr(val32)&#xA;    }&#xA;&#xA;    val64, _ := Read[uint64](reader)&#xA;    return uintptr(val64)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;div&gt;&#xA;    &lt;a name=&#34;binary-reader&#34; href=&#34;#binary-reader&#34;&gt;&lt;h4&gt;Binary Reader&lt;/h4&gt;&lt;/a&gt;&#xA;    &lt;p&gt;&#xA;        Go has an &lt;a href=&#34;https://pkg.go.dev/encoding/binary&#34; target=&#34;_blank&#34;&gt;encoding/binary&lt;/a&gt; package, but it doesn&#39;t allow for reading arbitrary integers, nor does it allow us to know how far along the buffer we&#39;ve read (our offset). We can wrap it a bit more nicely and ensure we&#39;re always reading in the correcty byte order using our own &lt;code&gt;BinaryReader&lt;/code&gt;:&#xA;    &lt;/p&gt;&#xA;    &lt;pre&gt;&lt;code&gt;type Signed interface {&#xA;&#x9;~int | ~int8 | ~int16 | ~int32 | ~int64&#xA;}&#xA;&#xA;type Unsigned interface {&#xA;&#x9;~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr&#xA;}&#xA;&#xA;type Integer interface {&#xA;&#x9;Signed | Unsigned&#xA;}&#xA;&#xA;type BinaryReader struct {&#xA;    *bytes.Buffer&#xA;&#xA;    startLen int&#xA;}&#xA;&#xA;// Returns how many bytes we&#39;ve read since the start of the buffer&#xA;func (br *BinaryReader) Offset() int {&#xA;    return br.startLen - br.Len()&#xA;}&#xA;&#xA;func NewBinaryReader(r *bytes.Buffer) *BinaryReader {&#xA;    return &amp;BinaryReader{Buffer: r, startLen: r.Len()}&#xA;}&#xA;&#xA;func Read[T Integer](br *BinaryReader) (T, error) {&#xA;    empty := *new(T)&#xA;    size := unsafe.Sizeof(empty)&#xA;    enc := binary.NativeEndian&#xA;&#xA;    // read N bytes br the reader&#xA;    buf := make([]byte, size)&#xA;    err := binary.Read(br, enc, buf)&#xA;    if err != nil {&#xA;        return empty, err&#xA;    }&#xA;&#xA;    // convert to the appropriate type&#xA;    val := *new(T)&#xA;    switch any(val).(type) {&#xA;    case int8:&#xA;        val = T(int8(buf[0]))&#xA;    case uint8:&#xA;        val = T(uint8(buf[0]))&#xA;&#xA;    case int16:&#xA;        val = T(int16(enc.Uint16(buf)))&#xA;    case uint16:&#xA;        val = T(enc.Uint16(buf))&#xA;&#xA;    case int32:&#xA;        val = T(int32(enc.Uint32(buf)))&#xA;    case uint32:&#xA;        val = T(enc.Uint32(buf))&#xA;&#xA;    case int, int64:&#xA;        val = T(int64(enc.Uint64(buf)))&#xA;    case uint, uint64:&#xA;        val = T(enc.Uint64(buf))&#xA;    case uintptr:&#xA;        if size == 4 {&#xA;            val = T(uintptr(enc.Uint32(buf)))&#xA;        } else if size == 8 {&#xA;            val = T(uintptr(enc.Uint64(buf)))&#xA;        } else {&#xA;            return empty, fmt.Errorf(&#34;word size %d not supported&#34;, size)&#xA;        }&#xA;&#xA;    default:&#xA;        return empty, fmt.Errorf(&#34;unknown data type during load: %T&#34;, val)&#xA;    }&#xA;&#xA;    return val, nil&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;    &lt;/body&gt;&#xA;&lt;/html&gt;&#xA;</content>
    <link href="https://calabro.io/dwarf/go" rel="alternate"></link>
    <summary type="html">Reference Go helper code used throughout the How DWARF Works series</summary>
  </entry>
  <entry>
    <title>Hello!</title>
    <updated>2024-07-11T00:00:00Z</updated>
    <id>tag:calabro.io,2024-07-11:/hello</id>
    <content type="html">&#xA;&lt;!DOCTYPE html&gt;&#xA;&lt;html lang=&#34;en&#34;&gt;&#xA;    &lt;head&gt;&#xA;        &lt;meta charset=&#34;utf-8&#34;&gt;&#xA;        &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no&#34; /&gt;&#xA;        &lt;meta http-equiv=&#34;Cache-control&#34; content=&#34;public&#34;&gt;&#xA;        &lt;meta name=&#34;description&#34; content=&#34;Jim Calabro&#34;&gt;&#xA;        &lt;link rel=&#34;canonical&#34; rel=&#34;noreferrer&#34; href=&#34;https://www.calabro.io&#34; /&gt;&#xA;        &lt;link rel=&#34;icon&#34; type=&#34;image/svg&#34; href=&#34;/static/favicon.svg&#34;&gt;&#xA;        &lt;title&gt;Hello! - Jim Calabro&lt;/title&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/lit.min.css&#34;&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; charset=&#34;utf-8&#34; href=&#34;/static/style.css&#34;&gt;&#xA;        &lt;link href=&#39;/static/Raleway.css&#39; rel=&#39;stylesheet&#39; type=&#39;text/css&#39; async defer&gt;&#xA;        &lt;link rel=&#34;stylesheet&#34; href=&#34;/static/font-awesome.min.css&#34;&gt;&#xA;        &#xA;        &lt;script async defer src=&#34;/static/font-awesome.min.js&#34; data-auto-replace-svg=&#34;nest&#34;&gt;&lt;/script&gt;&#xA;        &lt;script type=&#34;text/javascript&#34;&gt;&#xA;            const mobileWidth = 560;&#xA;&#xA;            function toggleMenu(event) {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    return;&#xA;                }&#xA;&#xA;                const links = document.querySelector(&#39;.links&#39;);&#xA;                const display = links.style[&#39;display&#39;];&#xA;                if (!display || display === &#39;none&#39;) {&#xA;                    links.style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    links.style.display = &#39;none&#39;;&#xA;                }&#xA;            }&#xA;&#xA;            addEventListener(&#39;resize&#39;, (event) =&gt; {&#xA;                if (window.innerWidth &gt;= mobileWidth) {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;block&#39;;&#xA;                } else {&#xA;                    document.querySelector(&#39;.links&#39;).style.display = &#39;none&#39;;&#xA;                }&#xA;            });&#xA;        &lt;/script&gt;&#xA;    &lt;/head&gt;&#xA;    &lt;body&gt;&#xA;        &lt;div class=&#34;row&#34;&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&lt;/div&gt;&#xA;            &lt;div class=&#34;col 2&#34;&gt;&#xA;                &lt;div class=&#34;collapsed-links&#34;&gt;&#xA;                    &lt;h3 class=&#34;name-header&#34;&gt;&lt;a href=&#34;/&#34; style=&#34;cursor: pointer&#34;&gt;Jim Calabro&lt;/a&gt;&lt;/h3&gt;&#xA;                    &lt;div onclick=&#34;toggleMenu(this)&#34;&gt;&lt;i class=&#34;hamburger-menu fa-solid fa-bars&#34;&gt;&lt;/i&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;                &lt;div class=&#34;links&#34;&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;/about&#34;&gt;About&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&#xA;                        &lt;a href=&#34;/rss&#34;&gt;RSS&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/atom&#34;&gt;Atom&lt;/a&gt;&#xA;                        &lt;span class=&#34;divider&#34;&gt; | &lt;/span&gt;&#xA;                        &lt;a href=&#34;/json&#34;&gt;JSON&lt;/a&gt;&#xA;                    &lt;/div&gt;&#xA;                    &lt;div&gt;---&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://bsky.app/profile/calabro.io&#34; target=&#34;_blank&#34;&gt;Bluesky &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://github.com/jcalabro&#34; target=&#34;_blank&#34;&gt;GitHub &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.chess.com/member/jcalabro&#34; target=&#34;_blank&#34;&gt;Chess &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                    &lt;div&gt;&lt;a href=&#34;https://www.last.fm/user/thekingofping&#34; target=&#34;_blank&#34;&gt;Last.fm &lt;i class=&#34;icon fa fa-arrow-up-right-from-square&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/div&gt;&#xA;                &lt;/div&gt;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&#xA;                &#xA;                &#xA;&lt;p&gt;Hey! I&#39;m Jim, and this is my site.&lt;/p&gt;&#xA;&lt;p&gt;Most of the content here will be about computing, technology, and business. There may also be some occassional stuff on cycling, games, music, or something else.&lt;/p&gt;&#xA;&lt;p&gt;The cadence of writing will be relateively lax; I woudln&#39;t expect more than a post every few months. I&#39;ll try my best to keep you appraised of the stuff I&#39;m working on.&lt;/p&gt;&#xA;&lt;p&gt;Thanks for reading!&lt;/p&gt;&#xA;&#xA;            &lt;/div&gt;&#xA;            &lt;div class=&#34;col 4&#34;&gt;&lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;    &lt;/body&gt;&#xA;&lt;/html&gt;&#xA;</content>
    <link href="https://calabro.io/hello" rel="alternate"></link>
    <summary type="html">What is this site?</summary>
  </entry>
</feed>