diff --git a/.config.helloworld_qemu-x86_64 b/.config.helloworld_qemu-x86_64 new file mode 100644 index 0000000..ea25429 --- /dev/null +++ b/.config.helloworld_qemu-x86_64 @@ -0,0 +1,310 @@ +# +# Automatically generated file; DO NOT EDIT. +# Unikraft/0.17.0~8838cfd Configuration +# +CONFIG_UK_FULLVERSION="0.17.0~8838cfd" +CONFIG_UK_CODENAME="Calypso" +CONFIG_UK_ARCH="x86_64" +CONFIG_HOST_ARCH="x86_64" +CONFIG_UK_BASE="/home/lobo/home/unikraft-zig-native-hello/.unikraft/unikraft" +CONFIG_UK_APP="/home/lobo/home/unikraft-zig-native-hello" +CONFIG_UK_DEFNAME="helloworld" + +# +# Architecture Selection +# +CONFIG_ARCH_X86_64=y +# CONFIG_ARCH_ARM_64 is not set +# CONFIG_ARCH_ARM_32 is not set +# CONFIG_MARCH_X86_64_NATIVE is not set +CONFIG_MARCH_X86_64_GENERIC=y +# CONFIG_MARCH_X86_64_NOCONA is not set +# CONFIG_MARCH_X86_64_CORE2 is not set +# CONFIG_MARCH_X86_64_COREI7 is not set +# CONFIG_MARCH_X86_64_COREI7AVX is not set +# CONFIG_MARCH_X86_64_COREI7AVXI is not set +# CONFIG_MARCH_X86_64_ATOM is not set +# CONFIG_MARCH_X86_64_K8 is not set +# CONFIG_MARCH_X86_64_K8SSE3 is not set +# CONFIG_MARCH_X86_64_AMDFAM10 is not set +# CONFIG_MARCH_X86_64_BTVER1 is not set +# CONFIG_MARCH_X86_64_BDVER1 is not set +# CONFIG_MARCH_X86_64_BDVER2 is not set +# CONFIG_MARCH_X86_64_BDVER3 is not set +# CONFIG_MARCH_X86_64_BTVER2 is not set + +# +# Processor Features +# +CONFIG_X86_64_HAVE_RANDOM=y +# end of Processor Features + +CONFIG_STACK_SIZE_PAGE_ORDER=4 +CONFIG_CPU_EXCEPT_STACK_SIZE_PAGE_ORDER=4 +CONFIG_AUXSTACK_SIZE_PAGE_ORDER=4 +CONFIG_HAVE_RANDOM=y +# end of Architecture Selection + +# +# Platform Configuration +# +CONFIG_PLAT_KVM=y +CONFIG_KVM_BOOT_PROTO_MULTIBOOT=y + +# +# Hint: EFI stub depends on OPTIMIZE_PIE +# +CONFIG_KVM_VMM_QEMU=y +# CONFIG_KVM_VMM_FIRECRACKER is not set + +# +# Console Options +# +CONFIG_KVM_KERNEL_VGA_CONSOLE=y +CONFIG_KVM_DEBUG_SERIAL_CONSOLE=y +CONFIG_KVM_DEBUG_VGA_CONSOLE=y +CONFIG_KVM_KERNEL_SERIAL_CONSOLE=y + +# +# Serial console configuration +# +CONFIG_KVM_SERIAL_BAUD_115200=y +# CONFIG_KVM_SERIAL_BAUD_57600 is not set +# CONFIG_KVM_SERIAL_BAUD_38400 is not set +# CONFIG_KVM_SERIAL_BAUD_19200 is not set +# end of Serial console configuration +# end of Console Options + +# CONFIG_PLAT_XEN is not set + +# +# Platform Interface Options +# +# CONFIG_UKPLAT_MEMRNAME is not set +CONFIG_UKPLAT_MEMREGION_MAX_COUNT=128 +# CONFIG_UKPLAT_ACPI is not set +CONFIG_UKPLAT_LCPU_MAXCOUNT=1 +# CONFIG_PAGING is not set +# end of Platform Interface Options + +CONFIG_HZ=100 +# end of Platform Configuration + +# +# Device Drivers +# + +# +# Real Time Clock +# +# end of Real Time Clock + +# +# Serial console +# +# end of Serial console + +# +# Bus drivers +# +CONFIG_HAVE_PCI=y +# CONFIG_LIBUKBUS_PCI is not set +# CONFIG_LIBUKBUS_PLATFORM is not set +# end of Bus drivers + +# +# Interrupt controller +# +CONFIG_HAVE_APIC=y +CONFIG_LIBUKINTCTLR_XPIC=y +# CONFIG_LIBUKINTCTLR_APIC is not set +# end of Interrupt controller + +# +# Virtio +# +CONFIG_HAVE_MMIO=y +# end of Virtio + +# +# Xen +# +# end of Xen +# end of Device Drivers + +# +# Library Configuration +# +CONFIG_LIBISRLIB=y +CONFIG_LIBNOLIBC=y +CONFIG_LIBNOLIBC_UKDEBUG_ASSERT=y +# CONFIG_LIBNOLIBC_SYSLOG is not set +CONFIG_LIBNOLIBC_FD_SETSIZE=64 +# CONFIG_LIBPOSIX_ENVIRON is not set +# CONFIG_LIBPOSIX_EVENTFD is not set +# CONFIG_LIBPOSIX_FDIO is not set +# CONFIG_LIBPOSIX_FDTAB is not set +# CONFIG_LIBPOSIX_FUTEX is not set +# CONFIG_LIBPOSIX_LIBDL is not set +# CONFIG_LIBPOSIX_PIPE is not set +# CONFIG_LIBPOSIX_POLL is not set +CONFIG_LIBPOSIX_PROCESS=y +CONFIG_LIBPOSIX_PROCESS_PIDS=y +CONFIG_LIBPOSIX_PROCESS_MAX_PID=31 +CONFIG_LIBPOSIX_PROCESS_INIT_PIDS=y +# CONFIG_LIBPOSIX_PROCESS_CLONE is not set +# CONFIG_LIBPOSIX_PROCESS_DEBUG is not set +# CONFIG_LIBPOSIX_SOCKET is not set +# CONFIG_LIBPOSIX_SYSINFO is not set +# CONFIG_LIBPOSIX_TIME is not set +# CONFIG_LIBPOSIX_TIMERFD is not set +# CONFIG_LIBPOSIX_TTY is not set +# CONFIG_LIBPOSIX_UNIXSOCKET is not set +# CONFIG_LIBPOSIX_USER is not set +# CONFIG_LIBSYSCALL_SHIM is not set +# CONFIG_LIBUBSAN is not set +# CONFIG_LIBUK9P is not set +CONFIG_LIBUKALLOC=y +# CONFIG_LIBUKALLOC_IFMALLOC is not set +# CONFIG_LIBUKALLOC_IFSTATS is not set +CONFIG_LIBUKALLOCBBUDDY=y +# CONFIG_LIBUKALLOCBBUDDY_FREELIST_SANITY is not set +# CONFIG_LIBUKALLOCPOOL is not set +# CONFIG_LIBUKALLOCREGION is not set +CONFIG_LIBUKALLOCSTACK=y +CONFIG_LIBUKARGPARSE=y +# CONFIG_LIBUKARGPARSE_TEST is not set +CONFIG_LIBUKATOMIC=y +CONFIG_LIBUKBITOPS=y +# CONFIG_LIBUKBLKDEV is not set +CONFIG_LIBUKBOOT=y +# CONFIG_LIBUKBOOT_BANNER_NONE is not set +# CONFIG_LIBUKBOOT_BANNER_MINIMAL is not set +# CONFIG_LIBUKBOOT_BANNER_CLASSIC is not set +# CONFIG_LIBUKBOOT_BANNER_POWEREDBY is not set +# CONFIG_LIBUKBOOT_BANNER_POWEREDBY_ANSI is not set +# CONFIG_LIBUKBOOT_BANNER_POWEREDBY_ANSI2 is not set +# CONFIG_LIBUKBOOT_BANNER_POWEREDBY_EA is not set +# CONFIG_LIBUKBOOT_BANNER_POWEREDBY_EAANSI is not set +# CONFIG_LIBUKBOOT_BANNER_POWEREDBY_EAANSI2 is not set +# CONFIG_LIBUKBOOT_BANNER_POWEREDBY_U8 is not set +CONFIG_LIBUKBOOT_BANNER_POWEREDBY_U8ANSI=y +# CONFIG_LIBUKBOOT_BANNER_POWEREDBY_U8ANSI2 is not set +CONFIG_LIBUKBOOT_MAXNBARGS=60 +CONFIG_LIBUKBOOT_INITALLOC=y +CONFIG_LIBUKBOOT_INITBBUDDY=y +# CONFIG_LIBUKBOOT_INITREGION is not set +# CONFIG_LIBUKBOOT_INITNOALLOC is not set +CONFIG_LIBUKBOOT_INITSCHED=y +CONFIG_LIBUKBOOT_INITSCHEDCOOP=y +# CONFIG_LIBUKBOOT_INITNOSCHED is not set +# CONFIG_LIBUKBOOT_MAINTHREAD is not set +CONFIG_LIBUKBOOT_ALLOCSTACK=y +# CONFIG_LIBUKBUS is not set +CONFIG_LIBUKDEBUG=y +CONFIG_LIBUKDEBUG_PRINTK=y +# CONFIG_LIBUKDEBUG_PRINTK_INFO is not set +# CONFIG_LIBUKDEBUG_PRINTK_WARN is not set +CONFIG_LIBUKDEBUG_PRINTK_ERR=y +# CONFIG_LIBUKDEBUG_PRINTK_CRIT is not set +# CONFIG_LIBUKDEBUG_PRINTD is not set +# CONFIG_LIBUKDEBUG_NOREDIR is not set +CONFIG_LIBUKDEBUG_REDIR_PRINTD=y +# CONFIG_LIBUKDEBUG_REDIR_PRINTK is not set +CONFIG_LIBUKDEBUG_PRINT_TIME=y +# CONFIG_LIBUKDEBUG_PRINT_THREAD is not set +# CONFIG_LIBUKDEBUG_PRINT_CALLER is not set +CONFIG_LIBUKDEBUG_PRINT_SRCNAME=y +# CONFIG_LIBUKDEBUG_ANSI_COLOR is not set +CONFIG_LIBUKDEBUG_ENABLE_ASSERT=y +# CONFIG_LIBUKDEBUG_TRACEPOINTS is not set +# CONFIG_LIBUKFALLOC is not set +# CONFIG_LIBUKFALLOCBUDDY is not set +CONFIG_LIBUKFILE=y +# CONFIG_LIBUKGCOV is not set +CONFIG_HAVE_INTCTLR=y +CONFIG_LIBUKINTCTLR=y +CONFIG_LIBUKINTCTLR_MAX_HANDLERS_PER_IRQ=8 +# CONFIG_LIBUKINTCTLR_ISR_ECTX_ASSERTIONS is not set +CONFIG_LIBUKLIBID=y +CONFIG_LIBUKLIBID_INFO=y + +# +# Global metadata +# +CONFIG_LIBUKLIBID_INFO_UKFULLVERSION=y +# CONFIG_LIBUKLIBID_INFO_UKCODENAME is not set +# CONFIG_LIBUKLIBID_INFO_COMPILER is not set +CONFIG_LIBUKLIBID_INFO_COMPILEDATE=y +# CONFIG_LIBUKLIBID_INFO_UKCONFIGGZ is not set +# end of Global metadata + +# +# Per library metadata +# +# CONFIG_LIBUKLIBID_INFO_LIB_UKVERSION is not set +# CONFIG_LIBUKLIBID_INFO_LIB_UKCODENAME is not set +CONFIG_LIBUKLIBID_INFO_LIB_COMPILER=y +# CONFIG_LIBUKLIBID_INFO_LIB_COMPILEDATE is not set +# end of Per library metadata + +# CONFIG_LIBUKLIBID_INFO_COMPILEDBY is not set +# CONFIG_LIBUKLIBID_INFO_BOOTDUMP is not set +# CONFIG_LIBUKLIBPARAM is not set +CONFIG_LIBUKLOCK=y +CONFIG_LIBUKLOCK_SEMAPHORE=y +CONFIG_LIBUKLOCK_MUTEX=y +# CONFIG_LIBUKLOCK_MUTEX_METRICS is not set +CONFIG_LIBUKLOCK_RWLOCK=y +# CONFIG_LIBUKMMAP is not set +# CONFIG_LIBUKMPI is not set +# CONFIG_LIBUKNETDEV is not set +# CONFIG_LIBUKNOFAULT is not set +CONFIG_LIBUKRANDOM=y +# CONFIG_LIBUKRANDOM_SEED_INSECURE is not set +# CONFIG_LIBUKRING is not set +# CONFIG_LIBUKRUST is not set +CONFIG_LIBUKSCHED=y +# CONFIG_LIBUKSCHED_DEBUG is not set +CONFIG_LIBUKSCHEDCOOP=y +# CONFIG_LIBUKSGLIST is not set +# CONFIG_LIBUKSIGNAL is not set +# CONFIG_LIBUKSP is not set +# CONFIG_LIBUKSTORE is not set +CONFIG_LIBUKSTREAMBUF=y +# CONFIG_LIBUKSTREAMBUF_TEST is not set +# CONFIG_LIBUKTEST is not set +CONFIG_LIBUKTIMECONV=y +# CONFIG_LIBUKVMEM is not set +# CONFIG_LIBVFSCORE is not set +CONFIG_HAVE_BOOTENTRY=y +CONFIG_HAVE_SCHED=y +# end of Library Configuration + +# +# Application Options +# + +# +# Build Options +# +CONFIG_OPTIMIZE_NONE=y +# CONFIG_OPTIMIZE_PERF is not set +# CONFIG_OPTIMIZE_SIZE is not set +CONFIG_OPTIMIZE_NOOMITFP=y +# CONFIG_OPTIMIZE_DEADELIM is not set +# CONFIG_OPTIMIZE_LTO is not set +# CONFIG_OPTIMIZE_PIE is not set +# CONFIG_DEBUG_SYMBOLS_LVL0 is not set +# CONFIG_DEBUG_SYMBOLS_LVL1 is not set +# CONFIG_DEBUG_SYMBOLS_LVL2 is not set +CONFIG_DEBUG_SYMBOLS_LVL3=y +# CONFIG_OPTIMIZE_WARNISERROR is not set +# CONFIG_OPTIMIZE_SYMFILE is not set +# CONFIG_OPTIMIZE_COMPRESS is not set +# CONFIG_RECORD_BUILDTIME is not set +CONFIG_CROSS_COMPILE="" +CONFIG_LLVM_TARGET_ARCH="" +# end of Build Options + +CONFIG_UK_NAME="helloworld" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d8c8979 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.zig-cache +zig-out diff --git a/Kraftfile b/Kraftfile new file mode 100644 index 0000000..fc5a2f5 --- /dev/null +++ b/Kraftfile @@ -0,0 +1,13 @@ +spec: v0.6 + +name: helloworld + +unikraft: + version: stable + +targets: +- fc/arm64 +- fc/x86_64 +- qemu/arm64 +- qemu/x86_64 +- xen/x86_64 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c8366ed --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Emil Lerch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile.uk b/Makefile.uk new file mode 100644 index 0000000..0d6a3bc --- /dev/null +++ b/Makefile.uk @@ -0,0 +1,5 @@ +$(eval $(call addlib,apphelloworld)) + +APPHELLOWORLD_SRCS-y += $(APPHELLOWORLD_BASE)/helloworld.c +APPHELLOWORLD_SRCS-y += $(APPHELLOWORLD_BASE)/undefined.c +UK_ALIBS-y += $(APPHELLOWORLD_BASE)/libziggy.a diff --git a/README.md b/README.md new file mode 100644 index 0000000..6e80df9 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +(cd ziggy && zig build -Doptimize=ReleaseSafe -Dtarget=x86_64-linux-gnu.2.13 -Dcpu=baseline) && cp ziggy/zig-out/lib/libziggy.a . && kraft build --plat qemu --arch x86_64 --log-level debug --log-type basic && kraft run --pla +t qemu --arch x86_64 diff --git a/helloworld.c b/helloworld.c new file mode 100644 index 0000000..72574d2 --- /dev/null +++ b/helloworld.c @@ -0,0 +1,22 @@ +#include +#include + +extern int add(int, int); +extern int gettid(void); + +int main(void) +{ + int result = add(2, 2); + /* write(1, "from helloworld", 15); */ + printf("Hello, World! 2+2="); + char buffer[10]; + for (int i = 0; i < 10; i++ ) buffer[i] = 0; + buffer[0] = '0' + result; + puts(buffer); + + // requires posix-process, process and thread ids + puts("Attempting gettid()"); + printf("tid=%d\n", gettid()); + + return 0; +} diff --git a/undefined.c b/undefined.c new file mode 100644 index 0000000..c2eb070 --- /dev/null +++ b/undefined.c @@ -0,0 +1,196 @@ +#include + +/* Linking our zig to libc results in the following undefined symbols: + mmap64 + dl_iterate_phdr + getcontext + sigaction + write + close + realpath + read + msync + munmap + environ + openat64 + flock + fstat64 + dl_iterate_phdr + getenv + isatty + */ + +// Provide stubs for missing functions +#define UNUSED(x) (void)(x) +#define SEGFAULT() {int *ptr = 0; *ptr = 42;} +void *mmap64(void *addr, size_t length, int prot, int flags, + int fd, long int offset){ + UNUSED(addr); + UNUSED(length); + UNUSED(prot); + UNUSED(flags); + UNUSED(flags); + UNUSED(fd); + UNUSED(offset); + puts("unsupported function mmap called"); + // force a seg fault + SEGFAULT(); + return 0; // unreachable +} + +int dl_iterate_phdr( + int (*callback)(struct dl_phdr_info *info, + size_t size, void *data), + void *data){ + UNUSED(callback); + UNUSED(data); + puts("unsupported function dl_iterate_phdr called"); + SEGFAULT(); + return 0; +} +int getcontext(void *ucp) { + UNUSED(ucp); + puts("unsupported function getcontext called"); + SEGFAULT(); + return 0; + } + +int sigaction(int signum, void* act, void* oldact) { + UNUSED(signum); + UNUSED(act); + UNUSED(oldact); + // const struct sigaction *_Nullable restrict act, + // struct sigaction *_Nullable restrict oldact) { + puts("unsupported function sigaction called"); + SEGFAULT(); + return 0; +} + +#define ANSI_COLOR_RED "\x1b[31m" +#define ANSI_COLOR_GREEN "\x1b[32m" +#define ANSI_COLOR_YELLOW "\x1b[33m" +#define ANSI_COLOR_BLUE "\x1b[34m" +#define ANSI_COLOR_MAGENTA "\x1b[35m" +#define ANSI_COLOR_CYAN "\x1b[36m" +#define ANSI_COLOR_RESET "\x1b[0m" + +// TODO: This if is not correct +#if !(defined(CONFIG_LIBPOSIX_TTY) || defined(CONFIG_LIBSYSCALL_SHIM)) + size_t write(int fd, const char *buf, size_t count){ + UNUSED(fd); + UNUSED(buf); + UNUSED(count); + char *color; + switch (fd) { + case 1: // stdout + color = ANSI_COLOR_RESET; + break; + case 2: + color = ANSI_COLOR_RED; + break; + default: + printf("write called on unsupported file descriptor: "); + char fd_buf[10] = "fd = "; + fd_buf[5] = '0' + fd; + fd_buf[6] = 0; + puts(fd_buf); + SEGFAULT(); + break; + } + if (fd != 1) printf(color); + if (fd == 2) printf("(stderr): "); + printf("%.*s", (int)count, buf); + if (fd != 1) printf(ANSI_COLOR_RESET); + return count; + } + + int close(int fd) { + UNUSED(fd); + // const struct sigaction *_Nullable restrict act, + // struct sigaction *_Nullable restrict oldact) { + puts("unsupported function close called"); + SEGFAULT(); + return 0; + } + size_t read(int fd, void *buf, size_t count) { + UNUSED(fd); + UNUSED(buf); + UNUSED(count); + puts("unsupported function read called"); + SEGFAULT(); + return 0; + } +#endif + +char *realpath(const char *path, + char *resolved_path) { + UNUSED(path); + UNUSED(resolved_path); + // const struct sigaction *_Nullable restrict act, + // struct sigaction *_Nullable restrict oldact) { + puts("unsupported function realpath called"); + SEGFAULT(); + return 0; +} + +int msync(void *addr, size_t length, int flags) { + UNUSED(addr); + UNUSED(length); + UNUSED(flags); + puts("unsupported function msync called"); + SEGFAULT(); + return 0; +} +int munmap(void *addr, size_t length) { + UNUSED(addr); + UNUSED(length); + puts("unsupported function munmap called"); + SEGFAULT(); + return 0; +} + +int openat64(int fd, const char * path, int oflag, ...) { + UNUSED(fd); + UNUSED(path); + UNUSED(oflag); + puts("unsupported function openat64 called"); + SEGFAULT(); + return 0; +} + +int flock(int fd, int operation) { + UNUSED(fd); + UNUSED(operation); + puts("unsupported function flock called"); + SEGFAULT(); + return 0; +} + +int fstat64(int fd, void *statbuf) { + UNUSED(fd); + UNUSED(statbuf); + puts("unsupported function fstat called"); + SEGFAULT(); + return 0; +} +// If .config... file has environment support (CONFIG_LIBPOSIX_ENVIRON=y) +// defined, these variable are handled +#ifndef CONFIG_LIBPOSIX_ENVIRON + char **environ = 0; + char *getenv(const char *name) { + UNUSED(name); + puts(ANSI_COLOR_RED); + puts("unsupported function getenv called. use `kraft menu` to configure environment variable support"); + puts("or set CONFIG_LIBPOSIX_ENVIRON in .config._-"); + puts(ANSI_COLOR_RESET); + SEGFAULT(); + return 0; + } +#endif + +int isatty(int fd) { + UNUSED(fd); + puts("unsupported function isatty called"); + SEGFAULT(); + return 0; +} diff --git a/ziggy/build.zig b/ziggy/build.zig new file mode 100644 index 0000000..9c8f2fc --- /dev/null +++ b/ziggy/build.zig @@ -0,0 +1,92 @@ +const std = @import("std"); + +// Although this function looks imperative, note that its job is to +// declaratively construct a build graph that will be executed by an external +// runner. +pub fn build(b: *std.Build) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{}); + + // Standard optimization options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not + // set a preferred release mode, allowing the user to decide how to optimize. + const optimize = b.standardOptimizeOption(.{}); + + const lib = b.addStaticLibrary(.{ + .name = "ziggy", + // In this case the main source file is merely a path, however, in more + // complicated build scripts, this could be a generated file. + .root_source_file = b.path("src/root.zig"), + .target = target, + .optimize = optimize, + }); + lib.bundle_compiler_rt = true; // necessary since we're linking with a c program + lib.linkLibC(); + // This declares intent for the library to be installed into the standard + // location when the user invokes the "install" step (the default step when + // running `zig build`). + b.installArtifact(lib); + + const exe = b.addExecutable(.{ + .name = "ziggy", + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + + // This declares intent for the executable to be installed into the + // standard location when the user invokes the "install" step (the default + // step when running `zig build`). + b.installArtifact(exe); + + // This *creates* a Run step in the build graph, to be executed when another + // step is evaluated that depends on it. The next line below will establish + // such a dependency. + const run_cmd = b.addRunArtifact(exe); + + // By making the run step depend on the install step, it will be run from the + // installation directory rather than directly from within the cache directory. + // This is not necessary, however, if the application depends on other installed + // files, this ensures they will be present and in the expected location. + run_cmd.step.dependOn(b.getInstallStep()); + + // This allows the user to pass arguments to the application in the build + // command itself, like this: `zig build run -- arg1 arg2 etc` + if (b.args) |args| { + run_cmd.addArgs(args); + } + + // This creates a build step. It will be visible in the `zig build --help` menu, + // and can be selected like this: `zig build run` + // This will evaluate the `run` step rather than the default, which is "install". + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + // Creates a step for unit testing. This only builds the test executable + // but does not run it. + const lib_unit_tests = b.addTest(.{ + .root_source_file = b.path("src/root.zig"), + .target = target, + .optimize = optimize, + }); + + const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); + + const exe_unit_tests = b.addTest(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + + const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + + // Similar to creating the run step earlier, this exposes a `test` step to + // the `zig build --help` menu, providing a way for the user to request + // running the unit tests. + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&run_lib_unit_tests.step); + test_step.dependOn(&run_exe_unit_tests.step); +} diff --git a/ziggy/build.zig.zon b/ziggy/build.zig.zon new file mode 100644 index 0000000..ea42b0c --- /dev/null +++ b/ziggy/build.zig.zon @@ -0,0 +1,72 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = "ziggy", + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/ziggy/src/main.zig b/ziggy/src/main.zig new file mode 100644 index 0000000..c8a3f67 --- /dev/null +++ b/ziggy/src/main.zig @@ -0,0 +1,24 @@ +const std = @import("std"); + +pub fn main() !void { + // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`) + std.debug.print("All your {s} are belong to us.\n", .{"codebase"}); + + // stdout is for the actual output of your application, for example if you + // are implementing gzip, then only the compressed bytes should be sent to + // stdout, not any debugging messages. + const stdout_file = std.io.getStdOut().writer(); + var bw = std.io.bufferedWriter(stdout_file); + const stdout = bw.writer(); + + try stdout.print("Run `zig build test` to run the tests.\n", .{}); + + try bw.flush(); // don't forget to flush! +} + +test "simple test" { + var list = std.ArrayList(i32).init(std.testing.allocator); + defer list.deinit(); // try commenting this out and see if zig detects the memory leak! + try list.append(42); + try std.testing.expectEqual(@as(i32, 42), list.pop()); +} diff --git a/ziggy/src/root.zig b/ziggy/src/root.zig new file mode 100644 index 0000000..ae6e693 --- /dev/null +++ b/ziggy/src/root.zig @@ -0,0 +1,43 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const testing = std.testing; + +pub extern "c" fn gettid() std.c.pid_t; + +export fn add(a: i32, b: i32) i32 { + if (builtin.mode == .Debug) { + const out = std.io.getStdErr().writer(); + out.print("WARNING: Building debug mode will likely crash in Unikraft environment. Use -Doptimize=ReleaseSafe\n", .{}) catch {}; + } + const out = std.io.getStdOut().writer(); + out.print("Hello from lib\n", .{}) catch {}; + std.log.err("logging error", .{}); + out.print("Checking thread id\n", .{}) catch {}; + out.print("WAT: {d}\n", .{gettid()}) catch {}; + out.print("Thread id: {d}\n", .{gettid()}) catch {}; + // out.print("Thread id: {d}\n", .{std.Thread.getCurrentId()}) catch {}; + // We have a theory we need posix-futex enabled for locking/unlocking + // std.debug.print("debug print", .{}); + // if (builtin.single_threaded) @compileError("single threaded"); + return a + b + 1; +} + +// Unhandled Trap 6 (invalid opcode), error code=0x0 +// +// Thread.getCurrentId() calls fall flat. It looks like they use the linux +// call gettid, but that is not implemented by unikraft as of 0.17.0 (I +// believe). I tried adding process/thread support, but that did not address +// the problem. getCurrentId is used by the following: +// +// Mutex (only when building debug mode) +// Condition (only when building in debug mode) +// std.debug panic implementation +// +// So: +// 1) don't call it ourselves +// 2) build in release mode +// 3) don't panic ;-) + +test "basic add functionality" { + try testing.expect(add(3, 7) == 10); +}