<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[ShaJunXing]]></title><description><![CDATA[ShaJunXing]]></description><link>https://shajunxing.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 05 Nov 2026 01:42:15 GMT</lastBuildDate><atom:link href="https://shajunxing.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Linux Purification Plan - Make Linux portable]]></title><description><![CDATA[Now comes to my most core concept: I want all binary loading rules following only two: 1. binary's location (NOT current directory); 2. PATH environment…]]></description><link>https://shajunxing.hashnode.dev/linux-purification-plan-make-linux-portable</link><guid isPermaLink="true">https://shajunxing.hashnode.dev/linux-purification-plan-make-linux-portable</guid><dc:creator><![CDATA[shajunxing]]></dc:creator><pubDate>Fri, 30 Oct 2026 00:30:07 GMT</pubDate><content:encoded><![CDATA[<p>Now comes to my most core concept: I want all binary loading rules following only two: 1. binary's location (NOT current directory); 2. PATH environment variable. I choose musl libc to do my modification. I install it in <code>$HOME/musl</code>, and add function in <code>.bashrc</code> to switch from gcc to musl:</p>
<pre><code class="language-sh">echo Currently in gcc envirment, type \'musl\' to switch to musl.
musl() {
	export CC="$HOME/musl/bin/musl-gcc"
	export PATH="$HOME/musl/bin:$PATH"
	alias ld.so='$HOME/musl/lib/ld-musl-x86_64.so.1'
	alias ldd='ld.so --list'
	echo Done setting musl environment, use \'musl-gcc\' instead of \'gcc\'.
	unset -f musl
}
</code></pre>
<p>Only need to modify <code>ldso/dynlink.c</code>'s <code>load_library()</code> function: 1. change <code>char buf[2*NAME_MAX+2];</code> to <code>char buf[PATH_MAX + NAME_MAX + 2] = {0};</code> because I need more space to calculate path; 2. after variable declaration and before <code>if (!*name) {</code>, add <code>const char *sep = strrchr(name, '/'); if (sep) name = sep + 1;</code>, yes, even shared library path is absolute, I'll remove directory, only keep filename; 3. comment following code:</p>
<pre><code class="language-c">if (strchr(name, '/')) {
    pathname = name;
    fd = open(name, O_RDONLY|O_CLOEXEC);
} else {
    ...
}
</code></pre>
<p>change to:</p>
<pre><code class="language-c">for (p = head-&gt;next; p; p = p-&gt;next) {
    if (p-&gt;shortname &amp;&amp; !strcmp(p-&gt;shortname, name))
        return p;
}
if (strlen(name) &gt; NAME_MAX) return 0;
fd = -1;
/* app's directory */
if (head-&gt;name &amp;&amp; (sep = strrchr(head-&gt;name, '/'))) {
    size_t len = (size_t)(ptrdiff_t)(sep - head-&gt;name);
    if (len &gt; PATH_MAX) return 0;
    memcpy(buf, head-&gt;name, len);
    buf[len] = '/';
    strcpy(buf + len + 1, name);
} else {
    strcpy(buf, name);
}
fd = open(buf, O_RDONLY|O_CLOEXEC);
/* PATH */
if (fd &lt; 0) {
    const char *path = getenv("PATH");
    if (path &amp;&amp; *path) fd = path_open(name, path, buf, sizeof buf);
}
pathname = buf;
</code></pre>
<p>Rebuild musl by running <code>make &amp;&amp; make install</code>, and create <code>hello.h</code> <code>hello.c</code> <code>hello-lib.c</code> as following to do test.</p>
<pre><code class="language-c">#ifndef __HELLO_H__
#define __HELLO_H__
const char *greetings();
#endif
</code></pre>
<pre><code class="language-c">#include "hello.h"
#include &lt;stdio.h&gt;
int main(void) {
    puts(greetings());
    return 0;
}
</code></pre>
<pre><code class="language-c">#include "hello.h" 
const char *greetings() {
    return "Hello";
}
</code></pre>
<p>Test script is:</p>
<pre><code class="language-sh">#!/bin/sh
# bash script won't read .bashrc 
ldd() {
    "$HOME/musl/lib/ld-musl-x86_64.so.1" --list "$@"
}
musl-gcc hello.c hello-lib.c -static -o hello-static
musl-gcc hello-lib.c -shared -o foo/hello-lib.so
musl-gcc hello.c foo/hello-lib.so -o hello-dynamic
export PATH=$(cd $(dirname $0); pwd)/foo:$PATH
echo $PATH
./hello-dynamic
ldd hello-dynamic
</code></pre>
<p>Now everything has almost been portable, except one: PT_INTERP, which is defined in dynamic linked executable's head, and read by kernel to determine to pass to which dynamic loader. It is static path, most evil one. Nix's developers asked to support $ORIGIN, but I think it's already weird. Why static exe is loaded by kernel but dynamic is passed to external loader? Why they can't be all handled by kernel, or all handled by loader? This does not conform to layering principle of general architectural design. It is hard to change everything, but I found simplest way, just add <code>ld=</code> kernel parameter to override PT_INTERP.</p>
<p>Open <code>fs/binfmt_elf.c</code>, before function implementation <code>load_elf_binary()</code>, add:</p>
<pre><code class="language-c">static char elf_ld_path[PATH_MAX];
static int __init elf_ld_setup(char *str)
{
    if (!str || !*str)
        return 0;

    if (strlcpy(elf_ld_path, str, sizeof(elf_ld_path)) &gt;=
        sizeof(elf_ld_path))
        return 0;

    return 1;
}
__setup("ld=", elf_ld_setup);
</code></pre>
<p>And inside <code>load_elf_binary()</code>, change <code>interpreter = open_exec(elf_interpreter);</code> to <code>interpreter = open_exec(elf_ld_path[0] ? elf_ld_path : elf_interpreter);</code>.</p>
<p>Note: it only takes effect when builtin, not module. But that's enough, anyway my kernel disables modules, because they may involve problems about file paths.</p>
<p>Rebuild kernel by running <code>make</code>.</p>
<p>Dynamically rebuild init: <code>musl-gcc -o bin/init src/init.c</code>.</p>
<p>Copy needed files, including kernel, <code>$HOME/musl/lib/libc.so</code> and init, and add kernel parameter <code>ld=/libc.so</code>, command for making raw image is just like this: <code>bin\raw -o bin\output.raw -c "root=/dev/sda rw init=/init ld=/libc.so" bin\bzImage bin\init bin\libc.so</code>, also you can find it in <code>start.js</code>.</p>
<p>Now boot vm. Bingo!</p>
<p><img src="https://cdn.hashnode.com/uploads/covers/6adbdcc1e6875714bcd94295/34d80e11-ee8d-44dd-83fa-3be99d7d7dda.png" alt="1.0" /></p>
]]></content:encoded></item><item><title><![CDATA[Linux Purification Plan - Direct Boot tools for VirtualBox]]></title><description><![CDATA[So first, I need to set up handy environment. QEMU can directly boot kernel and in-memory filesystem, but I hate QEMU because it’s too messy. My another…]]></description><link>https://shajunxing.hashnode.dev/linux-purification-plan-direct-boot-tools-for-virtualbox</link><guid isPermaLink="true">https://shajunxing.hashnode.dev/linux-purification-plan-direct-boot-tools-for-virtualbox</guid><dc:creator><![CDATA[shajunxing]]></dc:creator><pubDate>Fri, 30 Oct 2026 00:28:24 GMT</pubDate><content:encoded><![CDATA[<p>So first, I need to set up handy environment. QEMU can directly boot kernel and in-memory filesystem, but I hate QEMU because it’s too messy. My another Debian laptop got completely messed up by QEMU, when I uninstalled it, it didn’t disable kernel modules, which made VirtualBox unable to start. Many people in open-source world are unable to do things right, they only love pretentious.</p>
<p>I also hate initrd/initramdisk. Why can’t those amateurs just walk straight? Why do they insist on taking detours, adding extra steps to show off their so-called technical skills? Linux kernel is already bloated enough, does it really matter how much space few filesystem drivers take?</p>
<p>I like VirtualBox's small, stable, cross-platform. I want it to work like QEMU’s direct boot. After many research, feasible approach is: find simplest FAT16 filesystem which kernel can recognize (there’s also ROMFS, but not sure if it can write to hard disk partition). Then write kernel, init and bootcode into .raw format file, and convert to .vdi using VirtualBox tools.</p>
<p>There’s no existing FAT16 creation tool, so I wrote my own <code>raw.c</code>, super floppy format, only one partition, no master boot record. Since FAT has bootstrap section, it can boot properly. Write files sequentially into root directory on consecutive sectors, filenames strictly follow 8.3 convention, and are converted to uppercase. First file will be treated as kernel, loaded by bootcode. Bootcode <code>boot.s</code> is modified from famous <a href="https://sebastian-plotz.blogspot.com/">https://sebastian-plotz.blogspot.com/</a> (included in kernel docs), compiled with nasm, then converted to text format with <code>bin2c.c</code> to include in <code>raw.c</code>. I wrote batch file <code>make.bat</code> to include above steps and generate <code>raw.exe</code>.</p>
<p>Next, compile kernel. On my Debian VM, first <code>make allnoconfig</code> or <code>make tinyconfig</code> to get minimal setup, then add support for tty/vt, msdos vfat, elf, etc. (I provided <code>.config</code> from 4.19.325 kernel. Why this version? Because I hate C++; I asked ChatGPT check latest GCC version that can be bootstrapped with pure C, which is 4.7.4, so I chose corresponding kernel version for later researches.)</p>
<p>Then, write simple <code>init.c</code> and compile it statically with <code>gcc -static -o init init.c</code>. Simplest is shown below, but I added some commands to make it looks like MS-DOS.</p>
<pre><code class="language-c">#include &lt;unistd.h&gt;
int main() {
    const char greetings[] = "Hello";
    write(STDOUT_FILENO, greetings, sizeof greetings);
    for(;;) pause();
    return 0;
}
</code></pre>
<p>With all materials ready, run <code>raw -o output.raw bzImage init</code> to generate image file, then run <code>VBoxManage convertfromraw output.raw fat16.vdi --format VDI</code> to convert, add it to 64-bit Linux VM, choose SATA/AHCI controller, boot, success. Steps above can be automated by <code>start.js</code> (based on <a href="https://github.com/shajunxing/sjx-script">https://github.com/shajunxing/sjx-script</a>).</p>
<p><img src="https://cdn.hashnode.com/uploads/covers/6adbdcc1e6875714bcd94295/764af7c4-ef4b-4eec-aaae-ca078f970795.png" alt="0.0" /></p>
]]></content:encoded></item><item><title><![CDATA[Tree control usability optimization]]></title><link>https://shajunxing.hashnode.dev/tree-control-usability-optimization</link><guid isPermaLink="true">https://shajunxing.hashnode.dev/tree-control-usability-optimization</guid><dc:creator><![CDATA[shajunxing]]></dc:creator><pubDate>Tue, 27 Oct 2026 09:07:17 GMT</pubDate><content:encoded><![CDATA[<img src="https://cdn.hashnode.com/uploads/covers/6adbdcc1e6875714bcd94295/2b1b98eb-8e4d-40f2-a3f5-cf517bdbdf31.png" alt="幻灯片1" style="display:block;margin:0 auto" />

<img src="https://cdn.hashnode.com/uploads/covers/6adbdcc1e6875714bcd94295/ab89766b-dbb4-4472-a84b-31c00edd7372.png" alt="幻灯片2" style="display:block;margin:0 auto" />

<img src="https://cdn.hashnode.com/uploads/covers/6adbdcc1e6875714bcd94295/2a573ecd-0a7f-4050-a0af-7fc5eb7d68b1.png" alt="幻灯片3" style="display:block;margin:0 auto" />

<img src="https://cdn.hashnode.com/uploads/covers/6adbdcc1e6875714bcd94295/ab6a2e50-0bf9-4444-8c11-16c9a3ec9aa9.png" alt="幻灯片4" style="display:block;margin:0 auto" />

<img src="https://cdn.hashnode.com/uploads/covers/6adbdcc1e6875714bcd94295/2106f1ce-f019-48cb-8602-a0c6166f4bc0.png" alt="幻灯片5" style="display:block;margin:0 auto" />

<p><img src="https://cdn.hashnode.com/uploads/covers/6adbdcc1e6875714bcd94295/7fd7506b-fcd2-4e99-9449-8a75666c8dc5.png" alt="幻灯片6" /></p>
]]></content:encoded></item><item><title><![CDATA[Linux Purification Plan - Preface]]></title><description><![CDATA[Project location is , continuously evolving. Linux has never followed KISS. To ease my OCD, it must be purified. Why they all use absolute path? Why can't…]]></description><link>https://shajunxing.hashnode.dev/linux-purification-plan-preface</link><guid isPermaLink="true">https://shajunxing.hashnode.dev/linux-purification-plan-preface</guid><dc:creator><![CDATA[shajunxing]]></dc:creator><pubDate>Sat, 24 Oct 2026 02:30:30 GMT</pubDate><content:encoded><![CDATA[<p>Project location is <a href="https://github.com/shajunxing/linux-purification-plan">https://github.com/shajunxing/linux-purification-plan</a> , continuously evolving.</p>
<h2>Preface</h2>
<p>Linux has never followed KISS. To ease my OCD, it must be purified. Why they all use absolute path? Why can't I install two standalone versions /linux1 /linux2 ? In Windows, I can put portable apps everywhere, even C compiler is portable. Xorg, Wayland, GTK, QT... these split generates multiplicative associations between layers, disaster in software engineering. Some distributions such as Nix tried to solve problem, but sadly they became even more complex.</p>
<p>Don't build another layer to hide the complexity. Remove the complexity at its source.</p>
<p>To be honest, I also dislike Windows since XP. I imagine if it can be added from <a href="https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/validation-os-overview">Validation OS</a> by necessary parts: network, security, direct x, get rid of everything corresponding .net dwm, and add classic shell like Win95, that's enough. But I also realized putting too much effort in is meaningless.</p>
<p>In my mind, a Linux system that truly belongs to me should work like this:</p>
<ol>
<li>Boot from a Live CD, use simple tools to partition and format the disk if necessary, extract the core system into a single directory, and set up the bootloader. That's it. No installation required.</li>
<li>User software comes as ZIP archives, too. <strong>Just extract and run</strong>. At most, you might need to add its directory to PATH. In most cases, you won't even need to do that.</li>
</ol>
]]></content:encoded></item></channel></rss>