Linux Purification Plan - Make Linux portable
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 $HOME/musl, and add function in .bashrc to switch from gcc to musl:
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
}
Only need to modify ldso/dynlink.c's load_library() function: 1. change char buf[2*NAME_MAX+2]; to char buf[PATH_MAX + NAME_MAX + 2] = {0}; because I need more space to calculate path; 2. after variable declaration and before if (!*name) {, add const char *sep = strrchr(name, '/'); if (sep) name = sep + 1;, yes, even shared library path is absolute, I'll remove directory, only keep filename; 3. comment following code:
if (strchr(name, '/')) {
pathname = name;
fd = open(name, O_RDONLY|O_CLOEXEC);
} else {
...
}
change to:
for (p = head->next; p; p = p->next) {
if (p->shortname && !strcmp(p->shortname, name))
return p;
}
if (strlen(name) > NAME_MAX) return 0;
fd = -1;
/* app's directory */
if (head->name && (sep = strrchr(head->name, '/'))) {
size_t len = (size_t)(ptrdiff_t)(sep - head->name);
if (len > PATH_MAX) return 0;
memcpy(buf, head->name, len);
buf[len] = '/';
strcpy(buf + len + 1, name);
} else {
strcpy(buf, name);
}
fd = open(buf, O_RDONLY|O_CLOEXEC);
/* PATH */
if (fd < 0) {
const char *path = getenv("PATH");
if (path && *path) fd = path_open(name, path, buf, sizeof buf);
}
pathname = buf;
Rebuild musl by running make && make install, and create hello.h hello.c hello-lib.c as following to do test.
#ifndef __HELLO_H__
#define __HELLO_H__
const char *greetings();
#endif
#include "hello.h"
#include <stdio.h>
int main(void) {
puts(greetings());
return 0;
}
#include "hello.h"
const char *greetings() {
return "Hello";
}
Test script is:
#!/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
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 ld= kernel parameter to override PT_INTERP.
Open fs/binfmt_elf.c, before function implementation load_elf_binary(), add:
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)) >=
sizeof(elf_ld_path))
return 0;
return 1;
}
__setup("ld=", elf_ld_setup);
And inside load_elf_binary(), change interpreter = open_exec(elf_interpreter); to interpreter = open_exec(elf_ld_path[0] ? elf_ld_path : elf_interpreter);.
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.
Rebuild kernel by running make.
Dynamically rebuild init: musl-gcc -o bin/init src/init.c.
Copy needed files, including kernel, $HOME/musl/lib/libc.so and init, and add kernel parameter ld=/libc.so, command for making raw image is just like this: bin\raw -o bin\output.raw -c "root=/dev/sda rw init=/init ld=/libc.so" bin\bzImage bin\init bin\libc.so, also you can find it in start.js.
Now boot vm. Bingo!

Comments
Join the discussionNo comments yet. Be the first to comment.