diff --git a/.github/riscv32-qemu-setup-all.sh b/.github/riscv32-qemu-setup-all.sh new file mode 100644 index 0000000000..c0088752ba --- /dev/null +++ b/.github/riscv32-qemu-setup-all.sh @@ -0,0 +1,63 @@ +#!/bin/sh +set -eux + +MINSTACK_VERSION=v12 + +# Download the linux kernel image, opensbi firmware, and the busybox userspace from the minstack repo +wget -q "https://github.com/moste00/riscv-minstack/releases/download/${MINSTACK_VERSION}/rv32-Image" +wget -q "https://github.com/moste00/riscv-minstack/releases/download/${MINSTACK_VERSION}/rv32-fw_dynamic.bin" +wget -q "https://github.com/moste00/riscv-minstack/releases/download/${MINSTACK_VERSION}/rv32-busybox" + +#--------------------------------------------------------------------------------------------------------------------- +cd rizin +# Setup cross file +sed -i "s|<>|${BOOTLIN_INSTALLATION}|g" doc/examples/cross_builds/meson-linux-riscv32.ini +export PATH="${BOOTLIN_TOOLS}:${PATH}" + +# Build Rizin +meson setup build --cross-file doc/examples/cross_builds/meson-linux-riscv32.ini +meson compile -C build || true # Expected to fail due to Zydis bug on RISC-V 32-bit +# Quick and dirty fix for Zydis compilation error, remove when upstream merges a fix and we bump +patch -p1 < patches/fix_zydis_amalgamated_riscv32_build +# Build again +meson compile -C build + +file build/binrz/rizin/rizin +cd .. +#--------------------------------------------------------------------------------------------------------------------- + +# Create a init config for busybox +cat << EOF > inittab +::sysinit:/mount -t proc proc /proc +::sysinit:/mount -t sysfs sysfs /sys +::sysinit:/mount -t devtmpfs devtmpfs /dev +::sysinit:/mount -t tmpfs -o size=128M,nodev,nosuid tmpfs /tmp +::wait:$@ +::once:/poweroff -f +EOF + +# Describe the boot filesystem +cat << EOF > initramfs +dir /bin 0755 0 0 +dir /etc 0755 0 0 +dir /lib 0755 0 0 +dir /tmp 1777 0 0 +dir /proc 0755 0 0 +dir /sys 0755 0 0 +dir /dev 0755 0 0 + +nod /dev/console 0600 0 0 c 5 1 + +file /bin/busybox rv32-busybox 0755 0 0 +slink /init /bin/busybox 0777 0 0 +slink /mount /bin/busybox 0777 0 0 +slink /poweroff /bin/busybox 0777 0 0 +file /etc/inittab inittab 0644 0 0 + +file /lib/ld-musl-riscv32.so.1 ${BOOTLIN_INSTALLATION}/riscv32-buildroot-linux-musl/sysroot/lib/ld-musl-riscv32.so.1 0755 0 0 +file /bin/rizin rizin/build/binrz/rizin/rizin 0755 0 0 +EOF + +# Generate the boot filesystem +gcc rizin/sys/gen_cpio_script.c -O3 -o gen_cpio +./gen_cpio initramfs > initrd_rv32.cpio \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c42df07e0..e604bcf94b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -914,6 +914,7 @@ jobs: .\rizin_cmake_test test-riscv-inside-qemu: + name: Test Rizin on RISC-V 64-bit inside QEMU runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 @@ -943,6 +944,56 @@ jobs: -append "console=ttyS0,115200 earlycon" \ -nographic + test-riscv32-in-qemu: + name: Test Rizin on RISC-V 32-bit inside QEMU + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + - name: Install dependencies + run: | + sudo apt-get update -q + sudo apt-get install -y qemu-system-misc qemu-user-static + pip3 install meson ninja + + - name: Bootlin env definitions + run: | + set -a + BOOTLIN_VERSION=stable-2025.08-1 + BOOTLIN_LIBC=musl + BOOTLIN_RV32_TOOLCHAIN=riscv32-ilp32d--${BOOTLIN_LIBC}--${BOOTLIN_VERSION}.tar.xz + BOOTLIN_DOMAIN=https://toolchains.bootlin.com + BOOTLIN_DOWNLOAD_URL=${BOOTLIN_DOMAIN}/downloads/releases/toolchains/riscv32-ilp32d/tarballs/${BOOTLIN_RV32_TOOLCHAIN} + BOOTLIN_INSTALLATION="${GITHUB_WORKSPACE}/toolchain/${BOOTLIN_RV32_TOOLCHAIN%.tar.xz}" + BOOTLIN_TOOLS="${BOOTLIN_INSTALLATION}/bin" + env | grep "^BOOTLIN_" >> $GITHUB_ENV + + - name: Try getting the Bootlin toolchain from cache + id: cache-toolchain + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/toolchain + key: ${{ env.BOOTLIN_RV32_TOOLCHAIN }} + + - name: Download Bootlin on cache miss + if: steps.cache-toolchain.outputs.cache-hit != 'true' + run: | + mkdir -p toolchain && cd toolchain + wget -q ${{ env.BOOTLIN_DOWNLOAD_URL }} + tar -xf ${{ env.BOOTLIN_RV32_TOOLCHAIN }} + rm ${{ env.BOOTLIN_RV32_TOOLCHAIN }} + + - name: Setup the QEMU image + run: cd .. && sh ./rizin/.github/riscv32-qemu-setup-all.sh /bin/rizin -hh + + - name: Run Rizin on RISCV32 inside QEMU + run: | + cd .. + qemu-system-riscv32 -machine virt -m 512M \ + -bios rv32-fw_dynamic.bin \ + -kernel rv32-Image \ + -initrd initrd_rv32.cpio \ + -append "earlycon console=hvc0" \ + -nographic create-release: name: Create draft release and upload artifacts diff --git a/doc/examples/cross_builds/meson-linux-riscv32.ini b/doc/examples/cross_builds/meson-linux-riscv32.ini new file mode 100644 index 0000000000..42d309cafe --- /dev/null +++ b/doc/examples/cross_builds/meson-linux-riscv32.ini @@ -0,0 +1,30 @@ +# SPDX-FileCopyrightText: 2024-2026 moste00 +# SPDX-License-Identifier: BSD-3-Clause +# Replace (manually or with sed -i) <> with the actual path to your Bootlin directory +# (The Bootlin directory is the parent of bin/riscv32-buildroot-linux-musl-*) +[binaries] +c = 'riscv32-buildroot-linux-musl-gcc' +cpp = 'riscv32-buildroot-linux-musl-g++' +ar = 'riscv32-buildroot-linux-musl-ar' +strip = 'riscv32-buildroot-linux-musl-strip' +objcopy = 'riscv32-buildroot-linux-musl-objcopy' +objdump = 'riscv32-buildroot-linux-musl-objdump' +ranlib = 'riscv32-buildroot-linux-musl-ranlib' +pkgconfig = 'pkg-config' + +[built-in options] +c_args = ['-march=rv32gc', '-mabi=ilp32d', '-static', '-fPIC'] +cpp_args = ['-march=rv32gc', '-mabi=ilp32d', '-static', '-fPIC'] +c_link_args = ['-march=rv32gc', '-mabi=ilp32d', '-fPIC'] +cpp_link_args = ['-march=rv32gc', '-mabi=ilp32d', '-fPIC'] +default_library = 'static' + +[properties] +sys_root = '<>/riscv32-buildroot-linux-musl/sysroot' +pkg_config_libdir = '<>/riscv32-buildroot-linux-musl/sysroot/usr/lib/pkgconfig' + +[host_machine] +system = 'linux' +cpu_family = 'riscv32' +cpu = 'riscv32' +endian = 'little' \ No newline at end of file diff --git a/patches/fix_zydis_amalgamated_riscv32_build b/patches/fix_zydis_amalgamated_riscv32_build new file mode 100644 index 0000000000..3a32953037 --- /dev/null +++ b/patches/fix_zydis_amalgamated_riscv32_build @@ -0,0 +1,22 @@ +diff --git a/home/mostafa/BEFORE_ZYDIS.c b/subprojects/zydis/amalgamated-dist/Zydis.c +index 66dcb1ed3d..297dfd050a 100644 +--- a/subprojects/zydis/amalgamated-dist/Zydis.c ++++ b/subprojects/zydis/amalgamated-dist/Zydis.c +@@ -54437,7 +54437,7 @@ static const char* const DECIMAL_LOOKUP = + /* Decimal */ + /* ---------------------------------------------------------------------------------------------- */ + +-#if defined(ZYAN_X86) || defined(ZYAN_ARM) || defined(ZYAN_EMSCRIPTEN) || defined(ZYAN_WASM) || defined(ZYAN_PPC) || defined(ZYAN_S390) ++#if defined(ZYAN_X86) || defined(ZYAN_ARM) || defined(ZYAN_EMSCRIPTEN) || defined(ZYAN_WASM) || defined(ZYAN_PPC) || defined(ZYAN_S390) || defined(ZYAN_RISCV32) + static ZyanStatus ZydisStringAppendDecU32(ZyanString* string, ZyanU32 value, ZyanU8 padding_length) + { + ZYAN_ASSERT(string); +@@ -54529,7 +54529,7 @@ static ZyanStatus ZydisStringAppendDecU64(ZyanString* string, ZyanU64 value, Zya + /* Hexadecimal */ + /* ---------------------------------------------------------------------------------------------- */ + +-#if defined(ZYAN_X86) || defined(ZYAN_ARM) || defined(ZYAN_EMSCRIPTEN) || defined(ZYAN_WASM) || defined(ZYAN_PPC) ++#if defined(ZYAN_X86) || defined(ZYAN_ARM) || defined(ZYAN_EMSCRIPTEN) || defined(ZYAN_WASM) || defined(ZYAN_PPC) || defined(ZYAN_RISCV32) + static ZyanStatus ZydisStringAppendHexU32(ZyanString* string, ZyanU32 value, ZyanU8 padding_length, + ZyanBool force_leading_number, ZyanBool uppercase) + { diff --git a/sys/gen_cpio_script.c b/sys/gen_cpio_script.c new file mode 100644 index 0000000000..e83bbb4ebc --- /dev/null +++ b/sys/gen_cpio_script.c @@ -0,0 +1,766 @@ +// VENDORED FROM: Linux Kernel sources 6.19.0 +// PLEASE DO NOT EDIT BY HAND UNLESS YOU KNOW WHAT YOU'RE DOING +// (But can copy again from newer kernel sources when/if necessary, +// but please keep this text and the FileCopyrightText line) +// ---------------------------------------------------------------------------- + +// SPDX-FileCopyrightText: 2005 Jeff Garzik +// SPDX-License-Identifier: GPL-2.0-only +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Original work by Jeff Garzik + * + * External file lists, symlink, pipe and fifo support by Thayne Harbaugh + * Hard link support by Luciano Rocha + */ + +#define xstr(s) #s +#define str(s) xstr(s) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define CPIO_HDR_LEN 110 +#define CPIO_TRAILER "TRAILER!!!" +#define padlen(_off, _align) (((_align) - ((_off) & ((_align) - 1))) % (_align)) + +/* zero-padding the filename field for data alignment is limited by PATH_MAX */ +static char padding[PATH_MAX]; +static unsigned int offset; +static unsigned int ino = 721; +static time_t default_mtime; +static bool do_file_mtime; +static bool do_csum = false; +static int outfd = STDOUT_FILENO; +static unsigned int dalign; + +struct file_handler { + const char *type; + int (*handler)(const char *line); +}; + +static int push_buf(const char *name, size_t name_len) { + ssize_t len; + + len = write(outfd, name, name_len); + if (len != name_len) + return -1; + + offset += name_len; + return 0; +} + +static int push_pad(size_t padlen) { + ssize_t len = 0; + + if (!padlen) + return 0; + + if (padlen < sizeof(padding)) + len = write(outfd, padding, padlen); + if (len != padlen) + return -1; + + offset += padlen; + return 0; +} + +static int push_rest(const char *name, size_t name_len) { + ssize_t len; + + len = write(outfd, name, name_len); + if (len != name_len) + return -1; + + offset += name_len; + + return push_pad(padlen(name_len + CPIO_HDR_LEN, 4)); +} + +static int cpio_trailer(void) { + int len; + unsigned int namesize = sizeof(CPIO_TRAILER); + + len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX" + "%08X%08X%08X%08X%08X%08X%08X", + do_csum ? "070702" : "070701", /* magic */ + 0, /* ino */ + 0, /* mode */ + (long)0, /* uid */ + (long)0, /* gid */ + 1, /* nlink */ + (long)0, /* mtime */ + 0, /* filesize */ + 0, /* major */ + 0, /* minor */ + 0, /* rmajor */ + 0, /* rminor */ + namesize, /* namesize */ + 0); /* chksum */ + offset += len; + + if (len != CPIO_HDR_LEN || + push_rest(CPIO_TRAILER, namesize) < 0 || + push_pad(padlen(offset, 512)) < 0) + return -1; + + if (fsync(outfd) < 0 && errno != EINVAL) + return -1; + + return 0; +} + +static int cpio_mkslink(const char *name, const char *target, + unsigned int mode, uid_t uid, gid_t gid) { + int len; + unsigned int namesize, targetsize = strlen(target) + 1; + + if (name[0] == '/') + name++; + namesize = strlen(name) + 1; + + len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX" + "%08X%08X%08X%08X%08X%08X%08X", + do_csum ? "070702" : "070701", /* magic */ + ino++, /* ino */ + S_IFLNK | mode, /* mode */ + (long)uid, /* uid */ + (long)gid, /* gid */ + 1, /* nlink */ + (long)default_mtime, /* mtime */ + targetsize, /* filesize */ + 3, /* major */ + 1, /* minor */ + 0, /* rmajor */ + 0, /* rminor */ + namesize, /* namesize */ + 0); /* chksum */ + offset += len; + + if (len != CPIO_HDR_LEN || + push_buf(name, namesize) < 0 || + push_pad(padlen(offset, 4)) < 0 || + push_buf(target, targetsize) < 0 || + push_pad(padlen(offset, 4)) < 0) + return -1; + + return 0; +} + +static int cpio_mkslink_line(const char *line) { + char name[PATH_MAX + 1]; + char target[PATH_MAX + 1]; + unsigned int mode; + int uid; + int gid; + int rc = -1; + + if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, target, &mode, &uid, &gid)) { + fprintf(stderr, "Unrecognized dir format '%s'", line); + goto fail; + } + rc = cpio_mkslink(name, target, mode, uid, gid); +fail: + return rc; +} + +static int cpio_mkgeneric(const char *name, unsigned int mode, + uid_t uid, gid_t gid) { + int len; + unsigned int namesize; + + if (name[0] == '/') + name++; + namesize = strlen(name) + 1; + + len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX" + "%08X%08X%08X%08X%08X%08X%08X", + do_csum ? "070702" : "070701", /* magic */ + ino++, /* ino */ + mode, /* mode */ + (long)uid, /* uid */ + (long)gid, /* gid */ + 2, /* nlink */ + (long)default_mtime, /* mtime */ + 0, /* filesize */ + 3, /* major */ + 1, /* minor */ + 0, /* rmajor */ + 0, /* rminor */ + namesize, /* namesize */ + 0); /* chksum */ + offset += len; + + if (len != CPIO_HDR_LEN || + push_rest(name, namesize) < 0) + return -1; + + return 0; +} + +enum generic_types { + GT_DIR, + GT_PIPE, + GT_SOCK +}; + +struct generic_type { + const char *type; + mode_t mode; +}; + +static const struct generic_type generic_type_table[] = { + [GT_DIR] = { + .type = "dir", + .mode = S_IFDIR }, + [GT_PIPE] = { .type = "pipe", .mode = S_IFIFO }, + [GT_SOCK] = { .type = "sock", .mode = S_IFSOCK } +}; + +static int cpio_mkgeneric_line(const char *line, enum generic_types gt) { + char name[PATH_MAX + 1]; + unsigned int mode; + int uid; + int gid; + int rc = -1; + + if (4 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d", name, &mode, &uid, &gid)) { + fprintf(stderr, "Unrecognized %s format '%s'", + line, generic_type_table[gt].type); + goto fail; + } + mode |= generic_type_table[gt].mode; + rc = cpio_mkgeneric(name, mode, uid, gid); +fail: + return rc; +} + +static int cpio_mkdir_line(const char *line) { + return cpio_mkgeneric_line(line, GT_DIR); +} + +static int cpio_mkpipe_line(const char *line) { + return cpio_mkgeneric_line(line, GT_PIPE); +} + +static int cpio_mksock_line(const char *line) { + return cpio_mkgeneric_line(line, GT_SOCK); +} + +static int cpio_mknod(const char *name, unsigned int mode, + uid_t uid, gid_t gid, char dev_type, + unsigned int maj, unsigned int min) { + int len; + unsigned int namesize; + + if (dev_type == 'b') + mode |= S_IFBLK; + else + mode |= S_IFCHR; + + if (name[0] == '/') + name++; + namesize = strlen(name) + 1; + + len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX" + "%08X%08X%08X%08X%08X%08X%08X", + do_csum ? "070702" : "070701", /* magic */ + ino++, /* ino */ + mode, /* mode */ + (long)uid, /* uid */ + (long)gid, /* gid */ + 1, /* nlink */ + (long)default_mtime, /* mtime */ + 0, /* filesize */ + 3, /* major */ + 1, /* minor */ + maj, /* rmajor */ + min, /* rminor */ + namesize, /* namesize */ + 0); /* chksum */ + offset += len; + + if (len != CPIO_HDR_LEN || + push_rest(name, namesize) < 0) + return -1; + + return 0; +} + +static int cpio_mknod_line(const char *line) { + char name[PATH_MAX + 1]; + unsigned int mode; + int uid; + int gid; + char dev_type; + unsigned int maj; + unsigned int min; + int rc = -1; + + if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u", name, &mode, &uid, &gid, &dev_type, &maj, &min)) { + fprintf(stderr, "Unrecognized nod format '%s'", line); + goto fail; + } + rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min); +fail: + return rc; +} + +static int cpio_mkfile_csum(int fd, unsigned long size, uint32_t *csum) { + while (size) { + unsigned char filebuf[65536]; + ssize_t this_read; + size_t i, this_size = MIN(size, sizeof(filebuf)); + + this_read = read(fd, filebuf, this_size); + if (this_read <= 0 || this_read > this_size) + return -1; + + for (i = 0; i < this_read; i++) + *csum += filebuf[i]; + + size -= this_read; + } + /* seek back to the start for data segment I/O */ + if (lseek(fd, 0, SEEK_SET) < 0) + return -1; + + return 0; +} + +static int cpio_mkfile(const char *name, const char *location, + unsigned int mode, uid_t uid, gid_t gid, + unsigned int nlinks) { + struct stat buf; + unsigned long size; + int file, retval, len; + int rc = -1; + time_t mtime; + int namesize, namepadlen; + unsigned int i; + uint32_t csum = 0; + ssize_t this_read; + + mode |= S_IFREG; + + file = open(location, O_RDONLY); + if (file < 0) { + fprintf(stderr, "File %s could not be opened for reading\n", location); + goto error; + } + + retval = fstat(file, &buf); + if (retval) { + fprintf(stderr, "File %s could not be stat()'ed\n", location); + goto error; + } + + if (do_file_mtime) { + mtime = default_mtime; + } else { + mtime = buf.st_mtime; + if (mtime > 0xffffffff) { + fprintf(stderr, "%s: Timestamp exceeds maximum cpio timestamp, clipping.\n", + location); + mtime = 0xffffffff; + } + + if (mtime < 0) { + fprintf(stderr, "%s: Timestamp negative, clipping.\n", + location); + mtime = 0; + } + } + + if (buf.st_size > 0xffffffff) { + fprintf(stderr, "%s: Size exceeds maximum cpio file size\n", + location); + goto error; + } + + if (do_csum && cpio_mkfile_csum(file, buf.st_size, &csum) < 0) { + fprintf(stderr, "Failed to checksum file %s\n", location); + goto error; + } + + size = 0; + namepadlen = 0; + for (i = 1; i <= nlinks; i++) { + if (name[0] == '/') + name++; + namesize = strlen(name) + 1; + + /* data goes on last link, after any alignment padding */ + if (i == nlinks) + size = buf.st_size; + + if (dalign && size > dalign) { + namepadlen = padlen(offset + CPIO_HDR_LEN + namesize, + dalign); + if (namesize + namepadlen > PATH_MAX) { + fprintf(stderr, + "%s: best-effort alignment %u missed\n", + name, dalign); + namepadlen = 0; + } + } + + len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX" + "%08lX%08X%08X%08X%08X%08X%08X", + do_csum ? "070702" : "070701", /* magic */ + ino, /* ino */ + mode, /* mode */ + (long)uid, /* uid */ + (long)gid, /* gid */ + nlinks, /* nlink */ + (long)mtime, /* mtime */ + size, /* filesize */ + 3, /* major */ + 1, /* minor */ + 0, /* rmajor */ + 0, /* rminor */ + namesize + namepadlen, /* namesize */ + size ? csum : 0); /* chksum */ + offset += len; + + if (len != CPIO_HDR_LEN || + push_buf(name, namesize) < 0 || + push_pad(namepadlen ? namepadlen : padlen(offset, 4)) < 0) + goto error; + + if (size) { + this_read = copy_file_range(file, NULL, outfd, NULL, size, 0); + if (this_read > 0) { + if (this_read > size) + goto error; + offset += this_read; + size -= this_read; + } + /* short or failed copy falls back to read/write... */ + } + + while (size) { + unsigned char filebuf[65536]; + size_t this_size = MIN(size, sizeof(filebuf)); + + this_read = read(file, filebuf, this_size); + if (this_read <= 0 || this_read > this_size) { + fprintf(stderr, "Can not read %s file\n", location); + goto error; + } + + if (write(outfd, filebuf, this_read) != this_read) { + fprintf(stderr, "writing filebuf failed\n"); + goto error; + } + offset += this_read; + size -= this_read; + } + if (push_pad(padlen(offset, 4)) < 0) + goto error; + + name += namesize; + } + ino++; + rc = 0; + +error: + if (file >= 0) + close(file); + return rc; +} + +static char *cpio_replace_env(char *new_location) { + char expanded[PATH_MAX + 1]; + char *start, *end, *var; + + while ((start = strstr(new_location, "${")) && + (end = strchr(start + 2, '}'))) { + *start = *end = 0; + var = getenv(start + 2); + snprintf(expanded, sizeof expanded, "%s%s%s", + new_location, var ? var : "", end + 1); + strcpy(new_location, expanded); + } + + return new_location; +} + +static int cpio_mkfile_line(const char *line) { + char name[PATH_MAX + 1]; + char *dname = NULL; /* malloc'ed buffer for hard links */ + char location[PATH_MAX + 1]; + unsigned int mode; + int uid; + int gid; + int nlinks = 1; + int end = 0, dname_len = 0; + int rc = -1; + + if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d %n", + name, location, &mode, &uid, &gid, &end)) { + fprintf(stderr, "Unrecognized file format '%s'", line); + goto fail; + } + if (end && isgraph(line[end])) { + int len; + int nend; + + dname = malloc(strlen(line)); + if (!dname) { + fprintf(stderr, "out of memory (%d)\n", dname_len); + goto fail; + } + + dname_len = strlen(name) + 1; + memcpy(dname, name, dname_len); + + do { + nend = 0; + if (sscanf(line + end, "%" str(PATH_MAX) "s %n", + name, &nend) < 1) + break; + len = strlen(name) + 1; + memcpy(dname + dname_len, name, len); + dname_len += len; + nlinks++; + end += nend; + } while (isgraph(line[end])); + } else { + dname = name; + } + rc = cpio_mkfile(dname, cpio_replace_env(location), + mode, uid, gid, nlinks); +fail: + if (dname_len) + free(dname); + return rc; +} + +static void usage(const char *prog) { + fprintf(stderr, "Usage:\n" + "\t%s [-t ] [-c] [-o ] [-a ] \n" + "\n" + " is a file containing newline separated entries that\n" + "describe the files to be included in the initramfs archive:\n" + "\n" + "# a comment\n" + "file []\n" + "dir \n" + "nod \n" + "slink \n" + "pipe \n" + "sock \n" + "\n" + " name of the file/dir/nod/etc in the archive\n" + " location of the file in the current filesystem\n" + " expands shell variables quoted with ${}\n" + " link target\n" + " mode/permissions of the file\n" + " user id (0=root)\n" + " group id (0=root)\n" + " device type (b=block, c=character)\n" + " major number of nod\n" + " minor number of nod\n" + " space separated list of other links to file\n" + "\n" + "example:\n" + "# A simple initramfs\n" + "dir /dev 0755 0 0\n" + "nod /dev/console 0600 0 0 c 5 1\n" + "dir /root 0700 0 0\n" + "dir /sbin 0755 0 0\n" + "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n" + "\n" + " is time in seconds since Epoch that will be used\n" + "as mtime for symlinks, directories, regular and special files.\n" + "The default is to use the current time for all files, but\n" + "preserve modification time for regular files.\n" + "-c: calculate and store 32-bit checksums for file data.\n" + ": write cpio to this file instead of stdout\n" + ": attempt to align file data by zero-padding the\n" + "filename field up to data_align. Must be a multiple of 4.\n" + "Alignment is best-effort; PATH_MAX limits filename padding.\n", + prog); +} + +static const struct file_handler file_handler_table[] = { + { + .type = "file", + .handler = cpio_mkfile_line, + }, + { + .type = "nod", + .handler = cpio_mknod_line, + }, + { + .type = "dir", + .handler = cpio_mkdir_line, + }, + { + .type = "slink", + .handler = cpio_mkslink_line, + }, + { + .type = "pipe", + .handler = cpio_mkpipe_line, + }, + { + .type = "sock", + .handler = cpio_mksock_line, + }, + { + .type = NULL, + .handler = NULL, + } +}; + +#define LINE_SIZE (2 * PATH_MAX + 50) + +int main(int argc, char *argv[]) { + FILE *cpio_list; + char line[LINE_SIZE]; + char *args, *type; + int ec = 0; + int line_nr = 0; + const char *filename; + + default_mtime = time(NULL); + while (1) { + int opt = getopt(argc, argv, "t:cho:a:"); + char *invalid; + + if (opt == -1) + break; + switch (opt) { + case 't': + default_mtime = strtol(optarg, &invalid, 10); + if (!*optarg || *invalid) { + fprintf(stderr, "Invalid timestamp: %s\n", + optarg); + usage(argv[0]); + exit(1); + } + do_file_mtime = true; + break; + case 'c': + do_csum = true; + break; + case 'o': + outfd = open(optarg, + O_WRONLY | O_CREAT | O_LARGEFILE | O_TRUNC, + 0600); + if (outfd < 0) { + fprintf(stderr, "failed to open %s\n", optarg); + usage(argv[0]); + exit(1); + } + break; + case 'a': + dalign = strtoul(optarg, &invalid, 10); + if (!*optarg || *invalid || (dalign & 3)) { + fprintf(stderr, "Invalid data_align: %s\n", + optarg); + usage(argv[0]); + exit(1); + } + break; + case 'h': + case '?': + usage(argv[0]); + exit(opt == 'h' ? 0 : 1); + } + } + + /* + * Timestamps after 2106-02-07 06:28:15 UTC have an ascii hex time_t + * representation that exceeds 8 chars and breaks the cpio header + * specification. Negative timestamps similarly exceed 8 chars. + */ + if (default_mtime > 0xffffffff || default_mtime < 0) { + fprintf(stderr, "ERROR: Timestamp out of range for cpio format\n"); + exit(1); + } + + if (argc - optind != 1) { + usage(argv[0]); + exit(1); + } + filename = argv[optind]; + if (!strcmp(filename, "-")) + cpio_list = stdin; + else if (!(cpio_list = fopen(filename, "r"))) { + fprintf(stderr, "ERROR: unable to open '%s': %s\n\n", + filename, strerror(errno)); + usage(argv[0]); + exit(1); + } + + while (fgets(line, LINE_SIZE, cpio_list)) { + int type_idx; + size_t slen = strlen(line); + + line_nr++; + + if ('#' == *line) { + /* comment - skip to next line */ + continue; + } + + if (!(type = strtok(line, " \t"))) { + fprintf(stderr, + "ERROR: incorrect format, could not locate file type line %d: '%s'\n", + line_nr, line); + ec = -1; + break; + } + + if ('\n' == *type) { + /* a blank line */ + continue; + } + + if (slen == strlen(type)) { + /* must be an empty line */ + continue; + } + + if (!(args = strtok(NULL, "\n"))) { + fprintf(stderr, + "ERROR: incorrect format, newline required line %d: '%s'\n", + line_nr, line); + ec = -1; + } + + for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) { + int rc; + if (!strcmp(line, file_handler_table[type_idx].type)) { + if ((rc = file_handler_table[type_idx].handler(args))) { + ec = rc; + fprintf(stderr, " line %d\n", line_nr); + } + break; + } + } + + if (NULL == file_handler_table[type_idx].type) { + fprintf(stderr, "unknown file type line %d: '%s'\n", + line_nr, line); + } + } + if (ec == 0) + ec = cpio_trailer(); + + exit(ec); +}