implement new loader
This commit is contained in:
parent
7c7832ba40
commit
0101ed8a24
41
Makefile
41
Makefile
@ -1,14 +1,25 @@
|
||||
# requires lua, curl and zlib (in the future, lua will be statically linked)
|
||||
libraries := -llua -lcurl -lz
|
||||
flags := -Wall
|
||||
|
||||
# lua is required in the build process, in order to compile the bytecode
|
||||
lua := lua
|
||||
cc := gcc
|
||||
bundle = $(src_entry_dir)/bundle.lua
|
||||
dump_loader = $(src_entry_dir)/dump_loader.lua
|
||||
|
||||
all_flags += -W -Wall -fPIC -std=gnu11
|
||||
|
||||
# requires lua, curl and zlib (in the future, lua will be statically linked)
|
||||
main_libraries += -llua -lcurl -lz
|
||||
main_cflags += $(all_flags)
|
||||
|
||||
loader_libraries += -lfuse -lpthread -lsquashfuse
|
||||
loader_cflags += $(all_flags)
|
||||
loader_cflags += -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=29
|
||||
loader_cflags += -I/usr/include/fuse
|
||||
|
||||
dst_dir = dst
|
||||
src_main_dir = src/main
|
||||
src_entry_dir = src/entry
|
||||
src_loader_dir = src/loader
|
||||
|
||||
# Uncomment to get debugging symbols
|
||||
# flags := -g
|
||||
@ -18,21 +29,27 @@ main_sources += $(wildcard $(src_main_dir)/cli/*.lua)
|
||||
main_sources += $(wildcard $(src_main_dir)/formats/*.lua)
|
||||
main_sources += $(wildcard $(src_main_dir)/util/*.lua)
|
||||
|
||||
c_sources = $(wildcard $(src_entry_dir)/*.c)
|
||||
entry_sources = $(wildcard $(src_entry_dir)/*.c)
|
||||
|
||||
build_entry = $(src_entry_dir)/build.lua
|
||||
loader_sources = $(wildcard $(src_loader_dir)/*.c)
|
||||
|
||||
target = $(dst_dir)/slimpack
|
||||
bytecode_target = $(dst_dir)/bytecode.h
|
||||
main_target = $(dst_dir)/slimpack
|
||||
loader_target = $(dst_dir)/loader
|
||||
bytecode_h_target = $(dst_dir)/bytecode.h
|
||||
loader_h_target = $(dst_dir)/loader.h
|
||||
|
||||
.PHONY: build
|
||||
|
||||
build: $(target)
|
||||
build: $(main_target)
|
||||
|
||||
$(dst_dir):
|
||||
mkdir -p $@
|
||||
|
||||
$(bytecode_target): $(main_sources) $(dst_dir) $(build_entry)
|
||||
$(lua) $(build_entry) $(src_main_dir) $@ $(main_sources)
|
||||
$(target): $(c_sources) $(bytecode_target) $(dst_dir)
|
||||
$(cc) $(flags) $(libraries) -include $(bytecode_target) $(c_sources) -o $@
|
||||
$(bytecode_h_target): $(dst_dir) $(bundle) $(main_sources)
|
||||
$(lua) $(bundle) $(src_main_dir) $@ $(main_sources)
|
||||
$(loader_h_target): $(dst_dir) $(dump_loader) $(loader_target)
|
||||
$(lua) $(dump_loader) $(loader_target) $@
|
||||
$(loader_target): $(dst_dir) $(loader_sources)
|
||||
$(cc) $(loader_cflags) $(loader_libraries) $(loader_sources) -o $@
|
||||
$(main_target): $(dst_dir) $(bytecode_h_target) $(loader_h_target) $(entry_sources) $(main_sources)
|
||||
$(cc) $(main_cflags) $(main_libraries) -include $(bytecode_h_target) -include $(loader_h_target) $(entry_sources) -o $@
|
||||
|
6
mod/elf_loader.d.lua
Normal file
6
mod/elf_loader.d.lua
Normal file
@ -0,0 +1,6 @@
|
||||
--- @meta elf_loader
|
||||
|
||||
--- @type string
|
||||
local res;
|
||||
|
||||
return res;
|
@ -8,11 +8,11 @@ local fs = {};
|
||||
--- @return string? err
|
||||
function fs.mkdir(path, recursive) end
|
||||
|
||||
--- @param src string
|
||||
--- @param dst string
|
||||
--- @param target string
|
||||
--- @param name string
|
||||
--- @return boolean? ok
|
||||
--- @return string? err
|
||||
function fs.symlink(src, dst) end
|
||||
function fs.symlink(target, name) end
|
||||
|
||||
--- @param path string
|
||||
--- @param mode string | integer
|
||||
|
36
src/entry/dump_loader.lua
Normal file
36
src/entry/dump_loader.lua
Normal file
@ -0,0 +1,36 @@
|
||||
local function escape(str)
|
||||
return "\"" .. str:gsub(".", function (c)
|
||||
local b = string.byte(c);
|
||||
|
||||
if c == "\n" then
|
||||
return "\\n";
|
||||
elseif c == "\\" then
|
||||
return "\\\\";
|
||||
elseif c == "\"" then
|
||||
return "\\\"";
|
||||
elseif b >= 32 and b <= 126 then
|
||||
return c;
|
||||
else
|
||||
return ("\\%.3o"):format(b);
|
||||
end
|
||||
end) .. "\"";
|
||||
end
|
||||
|
||||
local function main(in_f, out_f)
|
||||
local f = assert(io.open(out_f, "w"));
|
||||
f:write "#include <stdint.h>\n";
|
||||
f:write "#define LOADER loader_bytecode\n";
|
||||
f:write "#define LOADER_SIZE (sizeof loader_bytecode - 1)\n";
|
||||
f:write "static const uint8_t loader_bytecode[] = ";
|
||||
|
||||
for part in io.lines(in_f, 64) do
|
||||
f:write(escape(part));
|
||||
f:write"\n";
|
||||
end
|
||||
|
||||
f:write ";";
|
||||
|
||||
assert(f:close());
|
||||
end
|
||||
|
||||
main(...);
|
@ -77,7 +77,7 @@ static int fmt_parse_width(size_t *i, const char *raw, size_t raw_size) {
|
||||
|
||||
static fmt_code_t fmt_parse_fmt(lua_State *ctx, const char *raw, size_t raw_size) {
|
||||
if (raw == NULL || *raw == 0) return FMT_BAD_ARGS;
|
||||
if (raw_size == -1) raw_size = strlen(raw);
|
||||
if (raw_size == SIZE_MAX) raw_size = strlen(raw);
|
||||
|
||||
fmt_segment_t segments[raw_size];
|
||||
size_t segments_n = 0;
|
||||
@ -278,7 +278,7 @@ static void write_buff_fit(write_buff_t *buff) {
|
||||
|
||||
|
||||
static bool fmt_read_uint8(const uint8_t *raw, size_t *i, size_t size, uint8_t *res) {
|
||||
if ((*i) + 1 > size || *i < 0) return false;
|
||||
if ((*i) + 1 > size) return false;
|
||||
|
||||
*res = raw[(*i)++];
|
||||
|
||||
@ -289,7 +289,7 @@ static bool fmt_read_int8(const uint8_t *raw, size_t *i, size_t size, int8_t *re
|
||||
}
|
||||
|
||||
static bool fmt_read_uint16(const uint8_t *raw, size_t *i, size_t size, uint16_t *res, bool little_endian) {
|
||||
if ((*i) + 2 > size || *i < 0) return false;
|
||||
if ((*i) + 2 > size) return false;
|
||||
|
||||
uint16_t a = raw[(*i)++];
|
||||
uint16_t b = raw[(*i)++];
|
||||
@ -304,7 +304,7 @@ static bool fmt_read_int16(const uint8_t *raw, size_t *i, size_t size, int16_t *
|
||||
}
|
||||
|
||||
static bool fmt_read_uint32(const uint8_t *raw, size_t *i, size_t size, uint32_t *res, bool little_endian) {
|
||||
if ((*i) + 4 > size || *i < 0) return false;
|
||||
if ((*i) + 4 > size) return false;
|
||||
|
||||
uint8_t a = raw[(*i)++];
|
||||
uint8_t b = raw[(*i)++];
|
||||
@ -321,7 +321,7 @@ static bool fmt_read_int32(const uint8_t *raw, size_t *i, size_t size, int32_t *
|
||||
}
|
||||
|
||||
static bool fmt_read_uint64(const uint8_t *raw, size_t *i, size_t size, uint64_t *res, bool little_endian) {
|
||||
if ((*i) + 8 > size || *i < 0) return false;
|
||||
if ((*i) + 8 > size) return false;
|
||||
|
||||
uint64_t a = raw[(*i)++];
|
||||
uint64_t b = raw[(*i)++];
|
||||
@ -342,7 +342,7 @@ static bool fmt_read_int64(const uint8_t *raw, size_t *i, size_t size, int64_t *
|
||||
}
|
||||
|
||||
static bool fmt_read_float32(const uint8_t *raw, size_t *i, size_t size, float *res, bool little_endian) {
|
||||
if ((*i) + 4 > size || *i < 0) return false;
|
||||
if ((*i) + 4 > size) return false;
|
||||
|
||||
uint8_t a = raw[(*i)++];
|
||||
uint8_t b = raw[(*i)++];
|
||||
@ -356,7 +356,7 @@ static bool fmt_read_float32(const uint8_t *raw, size_t *i, size_t size, float *
|
||||
return true;
|
||||
}
|
||||
static bool fmt_read_float64(const uint8_t *raw, size_t *i, size_t size, double *res, bool little_endian) {
|
||||
if ((*i) + 8 > size || *i < 0) return false;
|
||||
if ((*i) + 8 > size) return false;
|
||||
|
||||
uint8_t a = raw[(*i)++];
|
||||
uint8_t b = raw[(*i)++];
|
||||
@ -411,7 +411,6 @@ static bool fmt_read_string(lua_State *ctx, fmt_segment_t segment, const uint8_t
|
||||
|
||||
fprintf(stderr, "%lu %lu", len, *i);
|
||||
if ((*i) + len > size) return false;
|
||||
if (*i < 0) return false;
|
||||
|
||||
char data[len];
|
||||
memcpy(data, raw + (*i), len);
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
static const char *fs_check_path(lua_State *ctx, int i, size_t *psize) {
|
||||
size_t n;
|
||||
const char *path = luaL_checklstring(ctx, 1, &n);
|
||||
const char *path = luaL_checklstring(ctx, i, &n);
|
||||
|
||||
size_t real_len = strlen(path);
|
||||
if (n != real_len) {
|
||||
@ -71,7 +71,7 @@ static int lib_fs_symlink(lua_State *ctx) {
|
||||
|
||||
if (symlink(src, dst) < 0) {
|
||||
lua_pushnil(ctx);
|
||||
lua_pushfstring(ctx, "failed to chmod: %s", strerror(errno));
|
||||
lua_pushfstring(ctx, "failed to symlink: %s", strerror(errno));
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,8 @@ typedef struct {
|
||||
} http_body_buff_t;
|
||||
|
||||
static size_t body_writer(char *ptr, size_t _, size_t size, void *pbuff) {
|
||||
(void)_;
|
||||
|
||||
http_body_buff_t *buff = pbuff;
|
||||
|
||||
size_t new_cap = buff->cap;
|
||||
|
@ -14,17 +14,29 @@
|
||||
#define BYTECODE_SIZE (sizeof entry_bytecode - 1)
|
||||
#endif
|
||||
|
||||
#ifndef LOADER
|
||||
static uint8_t loader_bytecode[] = "";
|
||||
#define LOADER loader_bytecode
|
||||
#define LOADER_SIZE (sizeof loader_bytecode - 1)
|
||||
#endif
|
||||
|
||||
static char err_bytecode[] =
|
||||
"local err = ...;"
|
||||
"local trace = debug.traceback(nil, 2):match \"^[^\\n]+\\n(.+)$\""
|
||||
"print(table.concat { \"unhandled error: \", err, \"\\n\", trace })";
|
||||
|
||||
static int elf_loader_open_lib(lua_State *ctx) {
|
||||
lua_pushlstring(ctx, (const char*)LOADER, LOADER_SIZE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void load_modules(lua_State *ctx) {
|
||||
luaL_openlibs(ctx);
|
||||
luaL_requiref(ctx, "http", http_open_lib, false);
|
||||
luaL_requiref(ctx, "fmt", fmt_open_lib, false);
|
||||
luaL_requiref(ctx, "zlib", zlib_open_lib, false);
|
||||
luaL_requiref(ctx, "fs", fs_open_lib, false);
|
||||
luaL_requiref(ctx, "elf_loader", elf_loader_open_lib, false);
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
@ -42,7 +54,7 @@ int main(int argc, const char **argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < argc; i++) {
|
||||
for (int i = 1; i < argc; i++) {
|
||||
lua_pushstring(ctx, argv[i]);
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@ static int lib_decompress_iter(lua_State *ctx) {
|
||||
case Z_STREAM_END:
|
||||
inflateEnd(&stream->stream);
|
||||
stream->finalized = true;
|
||||
// fall through
|
||||
case Z_OK: {
|
||||
lua_pushlstring(ctx, (const char*)stream->buff, sizeof stream->buff - stream->stream.avail_out);
|
||||
stream->processed = size - stream->stream.avail_in;
|
||||
|
204
src/loader/entry.c
Normal file
204
src/loader/entry.c
Normal file
@ -0,0 +1,204 @@
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <linux/limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "loader.h"
|
||||
#include "slimfs.h"
|
||||
|
||||
typedef struct {
|
||||
char root_dir[PATH_MAX];
|
||||
struct fuse *fuse;
|
||||
bool freed;
|
||||
} *fuse_pthread_data_t;
|
||||
|
||||
static slimfs_t fs;
|
||||
static char *mount_dir;
|
||||
|
||||
static void *fuse_pthread_entry(void *pdata) {
|
||||
slimfs_t fs = pdata;
|
||||
|
||||
if (fuse_loop(fs->fuse) < 0) err(1, "Couldn't start fuse loop");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static slimfs_t slimfs_mount(const char *target_dir, const char *root_dir, const char *img_path, size_t img_offset) {
|
||||
slimfs_t fs = slimfs_new(1, (char*[]){ "", NULL }, target_dir, root_dir, img_path, img_offset);
|
||||
if (fs == NULL) return NULL;
|
||||
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, NULL, fuse_pthread_entry, fs);
|
||||
|
||||
return fs;
|
||||
}
|
||||
|
||||
static void handle_close() {
|
||||
fprintf(stderr, "\nCTRL+C detected, closing SlimFS...\n");
|
||||
slimfs_free(fs);
|
||||
rmdir(mount_dir);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static int write_flag(const char *file, const char *format, ...) {
|
||||
int fd = open(file, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
warn("Failed to write to %s", file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
va_list list;
|
||||
|
||||
va_start(list, format);
|
||||
char buff[vsnprintf(NULL, 0, format, list) + 1];
|
||||
va_end(list);
|
||||
|
||||
va_start(list, format);
|
||||
vsnprintf(buff, sizeof buff, format, list);
|
||||
va_end(list);
|
||||
|
||||
int n = write(fd, buff, sizeof buff - 1);
|
||||
if (n < 0 || (size_t)n != sizeof buff - 1) {
|
||||
warn("Failed to write to %s", file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int err_mount(const char *name) {
|
||||
warn("Failed to setup virtual mount for %s", name);
|
||||
return 127;
|
||||
}
|
||||
|
||||
int slimpak_isolate(const char *root_dir, char **argv) {
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) err(EXIT_EXECERROR, "fork error");
|
||||
else if (pid == 0) {
|
||||
int uid = geteuid();
|
||||
int gid = getegid();
|
||||
|
||||
errno = 0;
|
||||
|
||||
// Child is responsible for running the actual command
|
||||
if (unshare(CLONE_NEWUSER | CLONE_NEWNS) < 0) {
|
||||
warn("Failed to containerize");
|
||||
exit(127);
|
||||
}
|
||||
|
||||
if (write_flag("/proc/self/uid_map", "%d %d 1\n", uid, uid) < 0) exit(127);
|
||||
if (write_flag("/proc/self/setgroups", "deny\n") < 0) exit(127);
|
||||
if (write_flag("/proc/self/gid_map", "%d %d 1\n", gid, gid) < 0) exit(127);
|
||||
|
||||
char *cwd = getcwd(NULL, 0);
|
||||
chdir(root_dir);
|
||||
|
||||
// if (mount(NULL, "/", NULL, MS_REC | MS_SILENT | MS_SLAVE, NULL) < 0) return err_mount("root");
|
||||
if (mount("/dev", "dev", NULL, MS_REC | MS_SILENT | MS_BIND, NULL) < 0) return err_mount("/dev");
|
||||
if (mount("/proc", "proc", NULL, MS_REC | MS_SILENT | MS_BIND, NULL) < 0) return err_mount("/proc");
|
||||
if (mount("/run", "run", NULL, MS_REC | MS_SILENT | MS_BIND, NULL) < 0) return err_mount("/run");
|
||||
|
||||
if (chroot(root_dir) < 0) {
|
||||
warn("Failed to chroot into virtual root");
|
||||
exit(127);
|
||||
}
|
||||
|
||||
chdir(cwd);
|
||||
free(cwd);
|
||||
|
||||
execv(argv[0], argv);
|
||||
|
||||
err(127, "Failed to execute command");
|
||||
}
|
||||
else {
|
||||
int status;
|
||||
if (waitpid(pid, &status, 0) < 0) {
|
||||
warn("Failed to get child status");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (WIFEXITED(status)) return (uint16_t)WEXITSTATUS(status);
|
||||
else return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
const char *self_path = "/proc/self/exe";
|
||||
const char *temp_base = "/tmp";
|
||||
const char *slimpack_arg = NULL;
|
||||
|
||||
size_t offset = slimpack_get_offset(self_path);
|
||||
|
||||
if (argc == 2 && argv[1][0] == '-' && argv[1][1] == '-') {
|
||||
slimpack_arg = (const char*)argv[1] + 2;
|
||||
}
|
||||
|
||||
if (slimpack_arg && !strcmp(slimpack_arg, "slimpack-offset")) {
|
||||
printf("%zu\n", offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
mount_dir = malloc(strlen(temp_base) + 19 + 1);
|
||||
strcpy(mount_dir, temp_base);
|
||||
strcat(mount_dir, "/.slimpack.XXXXXXXX");
|
||||
|
||||
if (!mkdtemp(mount_dir)) err(EXIT_EXECERROR, "create mount dir error");
|
||||
|
||||
fs = slimfs_mount(mount_dir, "/", self_path, offset);
|
||||
if (fs == NULL) errx(1, "Couldn't mount SlimFS");
|
||||
|
||||
signal(SIGINT, handle_close);
|
||||
|
||||
int status;
|
||||
|
||||
if (slimpack_arg && !strcmp(slimpack_arg, "slimpack-mount")) {
|
||||
fprintf(stderr, "The mountpoint is at %s. Press Ctrl+D when you're ready\n", mount_dir);
|
||||
|
||||
char buff[4096];
|
||||
|
||||
while (read(STDIN_FILENO, buff, sizeof buff) != 0);
|
||||
|
||||
slimfs_free(fs);
|
||||
rmdir(mount_dir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (slimpack_arg && !strcmp(slimpack_arg, "slimpack-shell")) {
|
||||
status = slimpak_isolate((const char*)mount_dir, (char*[]) { "/bin/bash", NULL });
|
||||
}
|
||||
else {
|
||||
argv[0] = "/entry";
|
||||
status = slimpak_isolate((const char*)mount_dir, argv);
|
||||
}
|
||||
|
||||
slimfs_free(fs);
|
||||
rmdir(mount_dir);
|
||||
|
||||
if (status < 0) {
|
||||
err(127, "Failed to start isolated process (status %d)", status);
|
||||
}
|
||||
else {
|
||||
return status;
|
||||
}
|
||||
}
|
16
src/loader/loader.h
Normal file
16
src/loader/loader.h
Normal file
@ -0,0 +1,16 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
// Exit status to use when launching an AppImage fails.
|
||||
// For applications that assign meanings to exit status codes (e.g. rsync),
|
||||
// we avoid "cluttering" pre-defined exit status codes by using 127 which
|
||||
// is known to alias an application exit status and also known as launcher
|
||||
// error, see SYSTEM(3POSIX).
|
||||
#define EXIT_EXECERROR 127
|
||||
|
||||
ssize_t slimpack_get_offset(const char *executable);
|
||||
int slimpak_isolate(const char *root_dir, char **argv);
|
||||
int slimpak_mount(const char *executable, const char *work_dir, const char *root_dir);
|
||||
int slimpak_unmount(const char *work_dir, const char *root_dir);
|
228
src/loader/offset.c
Normal file
228
src/loader/offset.c
Normal file
@ -0,0 +1,228 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "loader.h"
|
||||
|
||||
#define EI_NIDENT 16
|
||||
|
||||
#define ELFCLASS32 1
|
||||
#define ELFDATA2LSB 1
|
||||
#define ELFDATA2MSB 2
|
||||
#define ELFCLASS64 2
|
||||
#define EI_CLASS 4
|
||||
#define EI_DATA 5
|
||||
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define ELFDATANATIVE ELFDATA2LSB
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
#define ELFDATANATIVE ELFDATA2MSB
|
||||
#else
|
||||
#error "Unknown machine endian"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
unsigned char e_ident[EI_NIDENT];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
uint32_t e_version;
|
||||
uint32_t e_entry; /* Entry point */
|
||||
uint32_t e_phoff;
|
||||
uint32_t e_shoff;
|
||||
uint32_t e_flags;
|
||||
uint16_t e_ehsize;
|
||||
uint16_t e_phentsize;
|
||||
uint16_t e_phnum;
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
} elf32_hdr_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t sh_name;
|
||||
uint32_t sh_type;
|
||||
uint32_t sh_flags;
|
||||
uint32_t sh_addr;
|
||||
uint32_t sh_offset;
|
||||
uint32_t sh_size;
|
||||
uint32_t sh_link;
|
||||
uint32_t sh_info;
|
||||
uint32_t sh_addralign;
|
||||
uint32_t sh_entsize;
|
||||
} elf32_shdr_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
uint32_t e_version;
|
||||
uint64_t e_entry; /* Entry point virtual address */
|
||||
uint64_t e_phoff; /* Program header table file offset */
|
||||
uint64_t e_shoff; /* Section header table file offset */
|
||||
uint32_t e_flags;
|
||||
uint16_t e_ehsize;
|
||||
uint16_t e_phentsize;
|
||||
uint16_t e_phnum;
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
} elf64_hdr_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t sh_name; /* Section name, index in string tbl */
|
||||
uint32_t sh_type; /* Type of section */
|
||||
uint64_t sh_flags; /* Miscellaneous section attributes */
|
||||
uint64_t sh_addr; /* Section virtual addr at execution */
|
||||
uint64_t sh_offset; /* Section file offset */
|
||||
uint64_t sh_size; /* Size of section in bytes */
|
||||
uint32_t sh_link; /* Index of another section */
|
||||
uint32_t sh_info; /* Additional section information */
|
||||
uint64_t sh_addralign; /* Section alignment */
|
||||
uint64_t sh_entsize; /* Entry size if section holds table */
|
||||
} elf64_shdr_t;
|
||||
|
||||
/* Note header in a PT_NOTE section */
|
||||
typedef struct elf32_note {
|
||||
uint32_t n_namesz; /* Name size */
|
||||
uint32_t n_descsz; /* Content size */
|
||||
uint32_t n_type; /* Content type */
|
||||
} Elf32_Nhdr;
|
||||
|
||||
typedef Elf32_Nhdr Elf_Nhdr;
|
||||
|
||||
static uint16_t file16_to_cpu(uint8_t ident[EI_NIDENT], uint16_t val) {
|
||||
if (ident[EI_DATA] != ELFDATANATIVE) {
|
||||
uint16_t a = val & 0xFF;
|
||||
uint16_t b = (val >> 8) & 0xFF;
|
||||
val = a << 8 | b;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
static uint32_t file32_to_cpu(uint8_t ident[EI_NIDENT], uint32_t val) {
|
||||
if (ident[EI_DATA] != ELFDATANATIVE) {
|
||||
uint32_t a = val & 0xFF;
|
||||
uint32_t b = (val >> 8) & 0xFF;
|
||||
uint32_t c = (val >> 16) & 0xFF;
|
||||
uint32_t d = (val >> 24) & 0xFF;
|
||||
val = a << 24 | b << 16 | c << 8 | d;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
static uint64_t file64_to_cpu(uint8_t ident[EI_NIDENT], uint64_t val) {
|
||||
if (ident[EI_DATA] != ELFDATANATIVE) {
|
||||
uint64_t a = val & 0xFF;
|
||||
uint64_t b = (val >> 8) & 0xFF;
|
||||
uint64_t c = (val >> 16) & 0xFF;
|
||||
uint64_t d = (val >> 24) & 0xFF;
|
||||
uint64_t e = (val >> 32) & 0xFF;
|
||||
uint64_t f = (val >> 40) & 0xFF;
|
||||
uint64_t g = (val >> 48) & 0xFF;
|
||||
uint64_t h = (val >> 56) & 0xFF;
|
||||
val = a << 56 | b << 48 | c << 40 | d << 32 | e << 24 | f << 16 | g << 8 | h;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static ssize_t read_elf32_size(uint8_t ident[EI_NIDENT], FILE *fd) {
|
||||
ssize_t ret;
|
||||
|
||||
fseek(fd, 0, SEEK_SET);
|
||||
|
||||
elf32_hdr_t header;
|
||||
ret = fread(&header, 1, sizeof(header), fd);
|
||||
if (ret < 0 || ret != sizeof header) {
|
||||
warn("Read of ELF header failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t sh_offset = file32_to_cpu(ident, header.e_shoff);
|
||||
size_t sh_entry_size = file16_to_cpu(ident, header.e_shentsize);
|
||||
size_t sh_entry_n = file16_to_cpu(ident, header.e_shnum);
|
||||
|
||||
off_t last_shdr_offset = sh_offset + (sh_entry_size * (sh_entry_n - 1));
|
||||
fseek(fd, last_shdr_offset, SEEK_SET);
|
||||
|
||||
elf32_shdr_t sect_header;
|
||||
ret = fread(§_header, 1, sizeof(sect_header), fd);
|
||||
if (ret < 0 || (size_t) ret != sizeof(sect_header)) {
|
||||
warn("Read of ELF section header failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ELF ends either with the table of section headers (SHT) or with a section. */
|
||||
off_t sh_end = sh_offset + (sh_entry_size * sh_entry_n);
|
||||
off_t s_end = file64_to_cpu(ident, sect_header.sh_offset) + file64_to_cpu(ident, sect_header.sh_size);
|
||||
|
||||
return sh_end > s_end ? sh_end : s_end;
|
||||
}
|
||||
static ssize_t read_elf64_size(uint8_t ident[EI_NIDENT], FILE *fd) {
|
||||
off_t ret;
|
||||
|
||||
fseek(fd, 0, SEEK_SET);
|
||||
|
||||
elf64_hdr_t header;
|
||||
ret = fread(&header, 1, sizeof(header), fd);
|
||||
if (ret < 0 || (size_t) ret != sizeof(header)) {
|
||||
warn("Read of ELF header failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t sh_offset = file64_to_cpu(ident, header.e_shoff);
|
||||
size_t sh_entry_size = file16_to_cpu(ident, header.e_shentsize);
|
||||
size_t sh_entry_n = file16_to_cpu(ident, header.e_shnum);
|
||||
|
||||
off_t last_shdr_offset = sh_offset + (sh_entry_size * (sh_entry_n - 1));
|
||||
fseek(fd, last_shdr_offset, SEEK_SET);
|
||||
|
||||
elf64_shdr_t sect_header;
|
||||
ret = fread(§_header, 1, sizeof(sect_header), fd);
|
||||
if (ret < 0 || ret != sizeof(sect_header)) {
|
||||
warn("Read of ELF section header failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ELF ends either with the table of section headers (SHT) or with a section. */
|
||||
off_t sht_end = sh_offset + (sh_entry_size * sh_entry_n);
|
||||
off_t s_end = file64_to_cpu(ident, sect_header.sh_offset) + file64_to_cpu(ident, sect_header.sh_size);
|
||||
return sht_end > s_end ? sht_end : s_end;
|
||||
}
|
||||
|
||||
ssize_t slimpack_get_offset(const char *executable) {
|
||||
FILE *fd = fopen(executable, "rb");
|
||||
if (fd == NULL) {
|
||||
warn("Cannot open executable");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t ident[EI_NIDENT];
|
||||
|
||||
off_t ret = fread(ident, 1, EI_NIDENT, fd);
|
||||
if (ret != EI_NIDENT) {
|
||||
warn("Read of e_ident failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ident[EI_DATA] != ELFDATA2LSB && ident[EI_DATA] != ELFDATA2MSB) {
|
||||
warnx("Unknown ELF data order %u", ident[EI_DATA]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t res;
|
||||
|
||||
if (ident[EI_CLASS] == ELFCLASS32) res = read_elf32_size(ident, fd);
|
||||
else if (ident[EI_CLASS] == ELFCLASS64) res = read_elf64_size(ident, fd);
|
||||
else {
|
||||
fprintf(stderr, "Unknown ELF class %u\n", ident[EI_CLASS]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fclose(fd);
|
||||
return res;
|
||||
}
|
1019
src/loader/slimfs.c
Normal file
1019
src/loader/slimfs.c
Normal file
File diff suppressed because it is too large
Load Diff
25
src/loader/slimfs.h
Normal file
25
src/loader/slimfs.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* License: BSD-style license
|
||||
* Copyright: Radek Podgorny <radek@podgorny.cz>,
|
||||
* Bernd Schubert <bernd-schubert@gmx.de>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fuse.h>
|
||||
#include <squashfuse/squashfuse.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct {
|
||||
struct fuse *fuse;
|
||||
struct fuse_chan *channel;
|
||||
struct sqfs image;
|
||||
char root_dir[PATH_MAX];
|
||||
char target_dir[PATH_MAX];
|
||||
} *slimfs_t;
|
||||
|
||||
extern struct fuse_operations unionfs_oper;
|
||||
|
||||
slimfs_t slimfs_new(int argc, char **argv, const char *target_dir, const char *root_dir, const char *img_path, size_t img_offset);
|
||||
void slimfs_free(slimfs_t fs);
|
@ -1,61 +1,7 @@
|
||||
local ostree = require "ostree";
|
||||
local flatpak = require "flatpak";
|
||||
local fs = require "fs";
|
||||
|
||||
local loader = [[#!/bin/sh
|
||||
|
||||
set -euo pipefail;
|
||||
|
||||
cleanup() {
|
||||
cd /;
|
||||
|
||||
if [ "${mountpoint_dir+x}" ]; then
|
||||
umount "$mountpoint_dir";
|
||||
fi
|
||||
if [ "${squashfs_dir+x}" ]; then
|
||||
umount "$squashfs_dir";
|
||||
fi
|
||||
if [ "${tmp_dir+x}" ]; then
|
||||
rm -rf "$tmp_dir";
|
||||
fi
|
||||
}
|
||||
|
||||
jail_run() {
|
||||
unshare -cR $mountpoint_dir $@;
|
||||
}
|
||||
|
||||
trap cleanup EXIT;
|
||||
|
||||
marker="#END_OF_"LOADER"_MARKER";
|
||||
|
||||
offset=$(( $(grep -abo "$marker" "$PWD/$0" | head -n 1 | cut -d: -f1) + ${#marker} + 1 ));
|
||||
|
||||
if [ "${1-}" = "--slimpack-offset" ] || [ "${1-}" = "--appimage-offset" ]; then
|
||||
echo $offset;
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
tmp_dir=$(mktemp -d /tmp/.slimpack.XXXXXXXXXXXX);
|
||||
|
||||
mkdir "$tmp_dir/squashfs";
|
||||
squashfuse -o offset=$offset "$PWD/$0" "$tmp_dir/squashfs";
|
||||
squashfs_dir="$tmp_dir/squashfs";
|
||||
|
||||
mkdir "$tmp_dir/mount_point";
|
||||
unionfs -o suid,dev "$squashfs_dir"=RO:/=RW "$tmp_dir/mount_point";
|
||||
mountpoint_dir="$tmp_dir/mount_point";
|
||||
|
||||
if [ "${1-}" = "--slimpack-dbg" ]; then
|
||||
jail_run /bin/bash;
|
||||
elif [ "${1-}" = "--slimpack-dbg-2" ]; then
|
||||
cd "$mountpoint_dir" && bash;
|
||||
else
|
||||
jail_run "/entry" $@;
|
||||
fi
|
||||
exit 0;
|
||||
|
||||
#END_OF_LOADER_MARKER
|
||||
]];
|
||||
local elf_loader = require "elf_loader";
|
||||
|
||||
local entry_preifx = [[#!/bin/sh
|
||||
|
||||
@ -98,7 +44,7 @@ end
|
||||
|
||||
local function write_res(img_path, target)
|
||||
local f = assert(io.open(target, "wb"));
|
||||
f:write(loader);
|
||||
f:write(elf_loader);
|
||||
|
||||
local img_f = assert(io.open(img_path, "rb"));
|
||||
|
||||
|
@ -292,7 +292,7 @@ function ostree.dump_node(path, node)
|
||||
assert(fs.chmod(path, node.mode & 0xFFF));
|
||||
elseif node.type == "link" then
|
||||
assert(fs.mkdir(path:match "^(.+)/[^/]+$", true));
|
||||
assert(fs.symlink(path, node.target));
|
||||
assert(fs.symlink(node.target, path));
|
||||
else
|
||||
error("Unknown node type '" .. node.type .. "'");
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user