I’m a bit out of the loop. Has anyone solved it?
(or at least Wayland+Vulkan without libwayland-client)
Continuing topic:
I’m a bit out of the loop. Has anyone solved it?
(or at least Wayland+Vulkan without libwayland-client)
Continuing topic:
I decided some time ago that what I really wanted on linux was to be able to dlopen dynamic libraries on a user’s system from a non-libc static executable. So I made dyn-loader. I use it to load any dynamic library I want while keeping the main executable statically linked and libc-free, and I’m pretty happy with this way of doing it: I copy-paste the executable and it just works. Easy shipping, no musl vs glibc vs glibc versions or other problems with the interesting state that the variety of linux distributions has created regarding “desktop user space”.
There isn’t currently a wayland + vulkan example in the repo, but there is an x11 + vulkan triangle (I still use x11 on my main machine). The vulkan part of the example was derived from a direct copy-paste of GitHub - Snektron/vulkan-zig: Vulkan binding generator for Zig · GitHub. I have another project that loads x11 or wayland (using this custom dynamic loader) depending on the user’s environment, but it uses EGL and not yet vulkan. And I confess that I just load libwayland-client and libEGL, and that of course ends up loading libc as a dependency (so this is a bit off topic), but at least in the process I never pretend to know anything about the end user’s system at compile time. I just check at runtime whether these libraries are available, and if so I load them.
To be completely honest, this functionality is what I hope std.DynLib on linux will become, implemented with stdlib-quality code of course ![]()
Ugh how do I get rid of this ridiculous voting thing?
Anyway this is meant to be a reply to @TibboddiT.
Your project looks really interesting. I was able to get the x11_window example and vulkan_instance example working successfully with this nix-shell:
{ pkgs ? import <nixpkgs> { } }:
pkgs.mkShell {
hardeningDisable = [ "all" ];
buildInputs = [
];
packages = [
pkgs.vulkan-loader
pkgs.vulkan-validation-layers
pkgs.libdrm
pkgs.xorg.libXdmcp
pkgs.xorg.libXau
pkgs.xorg.libxcb
pkgs.xorg.libX11
pkgs.glibc
];
VK_LAYER_PATH="${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d";
LD_LIBRARY_PATH="${pkgs.vulkan-loader}/lib:${pkgs.libdrm}/lib:${pkgs.xorg.libXdmcp}/lib:${pkgs.xorg.libXau}/lib:${pkgs.xorg.libxcb}/lib:${pkgs.xorg.libX11}/lib:${pkgs.glibc}/lib";
}
However, the x11_vulkan_triangle example crashes like this:
[nix-shell:~/src/dyn-loader]$ ./zig-out/bin/x11_vulkan_triangle
info: loading 'libX11.so.6'...
info: loading 'libvulkan.so.1'...
debug: getting vulkan symbol vkCreateInstance
debug: vulkan symbol vkCreateInstance: got address 0x7a4d0b420bf0
debug: getting vulkan symbol vkGetInstanceProcAddr
debug: vulkan symbol vkGetInstanceProcAddr: got address 0x7a4d0b4200b0
debug: getting vulkan symbol vkEnumerateInstanceVersion
debug: vulkan symbol vkEnumerateInstanceVersion: got address 0x7a4d0b420820
debug: getting vulkan symbol vkEnumerateInstanceLayerProperties
debug: vulkan symbol vkEnumerateInstanceLayerProperties: got address 0x7a4d0b420550
debug: getting vulkan symbol vkEnumerateInstanceExtensionProperties
debug: vulkan symbol vkEnumerateInstanceExtensionProperties: got address 0x7a4d0b420270
error(dynamic_library_loader): cannot find library libxshmfence.so.1
error(dynamic_library_loader): unresolved libvulkan_gfxstream.so dependency: libxshmfence.so.1
error(dynamic_library_loader): dlopen("/nix/store/i7my973ka8kddq8rg62znr6ckz1xa7wl-mesa-25.2.6/lib/libvulkan_gfxstream.so", 0x1) failed: error.LibraryNotFound
error(dynamic_library_loader): cannot find library libz.so.1
error(dynamic_library_loader): unresolved libvulkan_freedreno.so dependency: libz.so.1
error(dynamic_library_loader): dlopen("/nix/store/i7my973ka8kddq8rg62znr6ckz1xa7wl-mesa-25.2.6/lib/libvulkan_freedreno.so", 0x1) failed: error.LibraryNotFound
error(dynamic_library_loader): cannot find library libz.so.1
error(dynamic_library_loader): unresolved libvulkan_broadcom.so dependency: libz.so.1
error(dynamic_library_loader): dlopen("/nix/store/i7my973ka8kddq8rg62znr6ckz1xa7wl-mesa-25.2.6/lib/libvulkan_broadcom.so", 0x1) failed: error.LibraryNotFound
error(dynamic_library_loader): cannot find library libz.so.1
error(dynamic_library_loader): unresolved libvulkan_intel_hasvk.so dependency: libz.so.1
error(dynamic_library_loader): dlopen("/nix/store/i7my973ka8kddq8rg62znr6ckz1xa7wl-mesa-25.2.6/lib/libvulkan_intel_hasvk.so", 0x1) failed: error.LibraryNotFound
error(dynamic_library_loader): cannot find library libz.so.1
error(dynamic_library_loader): unresolved libvulkan_asahi.so dependency: libz.so.1
error(dynamic_library_loader): dlopen("/nix/store/i7my973ka8kddq8rg62znr6ckz1xa7wl-mesa-25.2.6/lib/libvulkan_asahi.so", 0x1) failed: error.LibraryNotFound
error(dynamic_library_loader): cannot find library libpowervr_rogue.so
error(dynamic_library_loader): unresolved libvulkan_powervr_mesa.so dependency: libpowervr_rogue.so
error(dynamic_library_loader): dlopen("/nix/store/i7my973ka8kddq8rg62znr6ckz1xa7wl-mesa-25.2.6/lib/libvulkan_powervr_mesa.so", 0x1) failed: error.LibraryNotFound
error(dynamic_library_loader): cannot find library libz.so.1
error(dynamic_library_loader): unresolved libvulkan_nouveau.so dependency: libz.so.1
error(dynamic_library_loader): dlopen("/nix/store/i7my973ka8kddq8rg62znr6ckz1xa7wl-mesa-25.2.6/lib/libvulkan_nouveau.so", 0x1) failed: error.LibraryNotFound
error(dynamic_library_loader): cannot find library libLLVM.so.21.1
error(dynamic_library_loader): unresolved libvulkan_lvp.so dependency: libLLVM.so.21.1
error(dynamic_library_loader): dlopen("/nix/store/i7my973ka8kddq8rg62znr6ckz1xa7wl-mesa-25.2.6/lib/libvulkan_lvp.so", 0x1) failed: error.LibraryNotFound
error(dynamic_library_loader): cannot find library libz.so.1
error(dynamic_library_loader): unresolved libvulkan_dzn.so dependency: libz.so.1
error(dynamic_library_loader): dlopen("/nix/store/i7my973ka8kddq8rg62znr6ckz1xa7wl-mesa-25.2.6/lib/libvulkan_dzn.so", 0x1) failed: error.LibraryNotFound
error(dynamic_library_loader): cannot find library libLLVM.so.21.1
error(dynamic_library_loader): unresolved libvulkan_radeon.so dependency: libLLVM.so.21.1
error(dynamic_library_loader): dlopen("/nix/store/i7my973ka8kddq8rg62znr6ckz1xa7wl-mesa-25.2.6/lib/libvulkan_radeon.so", 0x1) failed: error.LibraryNotFound
error(dynamic_library_loader): cannot find library libz.so.1
error(dynamic_library_loader): unresolved libvulkan_panfrost.so dependency: libz.so.1
error(dynamic_library_loader): dlopen("/nix/store/i7my973ka8kddq8rg62znr6ckz1xa7wl-mesa-25.2.6/lib/libvulkan_panfrost.so", 0x1) failed: error.LibraryNotFound
error(dynamic_library_loader): cannot find library libz.so.1
error(dynamic_library_loader): unresolved libvulkan_intel.so dependency: libz.so.1
error(dynamic_library_loader): dlopen("/nix/store/i7my973ka8kddq8rg62znr6ckz1xa7wl-mesa-25.2.6/lib/libvulkan_intel.so", 0x1) failed: error.LibraryNotFound
error(dynamic_library_loader): cannot find library libz.so.1
error(dynamic_library_loader): unresolved libvulkan_virtio.so dependency: libz.so.1
error(dynamic_library_loader): dlopen("/nix/store/i7my973ka8kddq8rg62znr6ckz1xa7wl-mesa-25.2.6/lib/libvulkan_virtio.so", 0x1) failed: error.LibraryNotFound
error: IncompatibleDriver
/home/andy/src/dyn-loader/examples/vulkan_advanced/vk.zig:27853:53: 0x1308722 in createInstance (x11_vulkan_triangle.zig)
Result.error_incompatible_driver => return error.IncompatibleDriver,
^
/home/andy/src/dyn-loader/examples/vulkan_advanced/graphics_context.zig:71:26: 0x12b0977 in init (x11_vulkan_triangle.zig)
const instance = try self.vkb.createInstance(&.{
^
/home/andy/src/dyn-loader/examples/vulkan_advanced/x11_vulkan_triangle.zig:113:16: 0x12bbc67 in main (x11_vulkan_triangle.zig)
const gc = try GraphicsContext.init(allocator, app_name, x11_display, x11_window);
^
I guess I would also need to chase down the recursive dependencies such as libLLVM.so and put those in the shell too. I don’t need to do that in a project when building from source however. I’m not sure what mechanism is chasing down those recursive dynamic libraries, or what magic nix incantation to use to populate LD_LIBRARY_PATH based on the selected packages.
Regardless, I’ve seen enough, this is great and I think I will try to join forces with you!
Maybe this comment can help you? Shimizu - The Wayland Protocol, in Zig - #8 by geemili
Honestly, this is one of the biggest things I am trying to learn.
There is another similar topic: how can you play and modify sound without libc or something like sound/asound.h
Anyone here still remembers that we have Zig here because a dude wants to write a DAW lol ![]()
Wayland is easy enough. Protocol is in xml files that ship with the system and you just communicate over a Unix socket, so no libc needed at all - just need to implement the protocol from the xml. Shimizu (as mentioned already) is the best equivalent of wayland-scanner for generating zig code. I have some code somewhere for comptime xml parsing to build the protocol, but this felt like a horrible idea (and was rather slow) so I ended up just implementing the few bits I needed.
Vulkan on the other hand - I think not very easy without libc, as libvulkan links libc, and there’s not a nice ABI in the same way. There’s been a few topics here but not sure anyone actually got to a solution. When I last looked into this I went down a rabbit hole with using DRM directly (who needs dependencies, right?)
Fun fact: when you search how to use vulkan without libc on duckduckgo, this post is the first result… I guess this proves that it is hard enough…
This post by @TibboddiT mentions vulkan without libc:
I have a little proof-of-concept from a few months ago which spins up a Wayland window with Vulkan, but without using the standard Vulkan WSI extensions, which avoids the libwayland-client tie-in you get with VK_KHR_wayland_surface. I can try to clean up that code tomorrow and link it here.
It does currently use libwayland-client IIRC, but that’s just because it was the easiest approach. There should be no issue replacing it with Shimizu or another Wayland client protocol implementation.
@andrewrk Glad you like the idea, honestly dlopen without libc from a static executable feels like a superpower.
I’m not a nix user, but it seems library search paths are not standard there. The loader supports LD_LIBRARY_PATH, but the internet says DT_RPATH / DT_RUNPATH are often used, and I delayed implementing them because I had never encountered an actual use case. Maybe the time has come! Can you check with readelf whether for example libvulkan_intel.so makes use of that kind of thing? I think adding an entry to LD_LIBRARY_PATH for each dynamic library is not very convenient ![]()