Loading `libvulkan.so.1` on Linux with `std.ElfDynLib` Part 2

2025-02-13

This was originally a topic on Ziggit.

Spent some time on this today; and I got it to the point where it’s recursively loading libraries and applying relocations. I hope I’m doing the relocations correctly, I haven’t made a test program to check. Maybe I should do that.

Currently stuck at handling relocations that have to do with thread local storage:

debug: needed library: 0x1c73 libc.so.6
debug: needed library: 0x869c ld-linux-x86-64.so.2
debug: ld-linux-x86-64.so.2 loaded at 0x712c95391000 - 0x712c953c9310
warning: unhandled relocation(libc.so.6)[0001]: 0x001e7c68 <- "" (0x0)	elf.R_X86_64.TPOFF64	56
warning: unhandled relocation(libc.so.6)[0002]: 0x001e7c70 <- "" (0x0)	elf.R_X86_64.TPOFF64	48
warning: unhandled relocation(libc.so.6)[0003]: 0x001e7c78 <- "" (0x0)	elf.R_X86_64.TPOFF64	72
warning: unhandled relocation(libc.so.6)[0004]: 0x001e7c80 <- "" (0x0)	elf.R_X86_64.TPOFF64	88
warning: unhandled relocation(libc.so.6)[0005]: 0x001e7c88 <- "" (0x0)	elf.R_X86_64.TPOFF64	80
warning: unhandled relocation(libc.so.6)[0006]: 0x001e7c90 <- "" (0x0)	elf.R_X86_64.TPOFF64	100
warning: unhandled relocation(libc.so.6)[0007]: 0x001e7c98 <- "" (0x0)	elf.R_X86_64.TPOFF64	120
warning: unhandled relocation(libc.so.6)[0008]: 0x001e7ca0 <- "" (0x0)	elf.R_X86_64.TPOFF64	128
warning: unhandled relocation(libc.so.6)[0009]: 0x001e7cc0 <- "" (0x0)	elf.R_X86_64.TPOFF64	24
warning: unhandled relocation(libc.so.6)[0010]: 0x001e7cd8 <- "" (0x0)	elf.R_X86_64.TPOFF64	40
warning: unhandled relocation(libc.so.6)[0011]: 0x001e7cf0 <- "" (0x0)	elf.R_X86_64.TPOFF64	16
warning: unhandled relocation(libc.so.6)[0012]: 0x001e7d78 <- "" (0x0)	elf.R_X86_64.TPOFF64	96
warning: unhandled relocation(libc.so.6)[0013]: 0x001e7ef8 <- "" (0x0)	elf.R_X86_64.TPOFF64	0
warning: unhandled relocation(libc.so.6)[0014]: 0x001e7fd8 <- "" (0x0)	elf.R_X86_64.TPOFF64	8
warning: unhandled relocation(libc.so.6)[0015]: 0x001e7ff0 <- "" (0x0)	elf.R_X86_64.TPOFF64	32
warning: unhandled relocation(libc.so.6)[0074]: 0x001e7ed0 <- "__libc_dlerror_result" (0x5f7)	elf.R_X86_64.TPOFF64	0
debug: libc.so.6 loaded at 0x712c95402000 - 0x712c955f3c78
debug: libvulkan.so.1 loaded at 0x712c9580e000 - 0x712c958971b0
debug: vkEnumerateInstanceVersion = fn (*u32) callconv(.c) vk.Result@712c958417a0
General protection exception (no address available)
???:?:?: 0x712c954a47af in ??? (???)
Unwind information for `???:0x712c954a47af` was not available, trace may be incomplete

???:?:?: 0x712c954a964c in ??? (???)
???:?:?: 0x712c9583626e in ??? (???)
???:?:?: 0x712c95842461 in ??? (???)
???:?:?: 0x712c958608f8 in ??? (???)
???:?:?: 0x712c958417f0 in ??? (???)
/home/geemili/code/dlopen-mesa-with-zig/src/main.zig:16:46: 0x1049859 in main (dlopen-mesa-with-zig)
    const result = vkEnumerateInstanceVersion(&version);
                                             ^
/home/geemili/code/zig/build-master/stage3/lib/zig/std/start.zig:656:37: 0x104277a in posixCallMainAndExit (dlopen-mesa-with-zig)
            const result = root.main() catch |err| {
                                    ^
/home/geemili/code/zig/build-master/stage3/lib/zig/std/start.zig:271:5: 0x104232d in _start (dlopen-mesa-with-zig)
    asm volatile (switch (native_arch) {
    ^

Running it in lldb we get some disassembly:

Process 450185 stopped
* thread #1, name = 'dlopen-mesa-wit', stop reason = signal SIGSEGV: invalid address (fault address: 0x0)
    frame #0: 0x00007ffff7b847af
->  0x7ffff7b847af: movq   %rcx, %fs:(%rax)
    0x7ffff7b847b3: addq   $0x60, %rcx
    0x7ffff7b847b7: movq   %rcx, %rax
    0x7ffff7b847ba: leaq   0x7f0(%rcx), %rdx
(lldb) 

I don’t understand how thread local storage works at the moment, so I’ll have to come back to this. I probably also need to handle .tdata and .tbss.

I am wondering at this point if the system libc.so.6 could be replaced with a theoretical ziglibc. libvulkan.so.1 itself only uses a couple symbols:

     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __strcat_chk@GLIBC_2.3.4 (2)
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getenv@GLIBC_2.2.5 (3)
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __snprintf_chk@GLIBC_2.3.4 (2)
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND dlerror@GLIBC_2.34 (4)
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND free@GLIBC_2.2.5 (3)
     6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND abort@GLIBC_2.2.5 (3)
     7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __errno_location@GLIBC_2.2.5 (3)
     8: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strncpy@GLIBC_2.2.5 (3)
     9: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strncmp@GLIBC_2.2.5 (3)
    10: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTable
    11: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND secure_getenv@GLIBC_2.17 (5)
    12: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __isoc23_sscanf@GLIBC_2.38 (6)
    13: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND qsort@GLIBC_2.2.5 (3)
    14: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fread@GLIBC_2.2.5 (3)
    15: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strtod@GLIBC_2.2.5 (3)
    16: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND readlink@GLIBC_2.2.5 (3)
    17: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fclose@GLIBC_2.2.5 (3)
    18: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND opendir@GLIBC_2.2.5 (3)
    19: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strlen@GLIBC_2.2.5 (3)
    20: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __stack_chk_fail@GLIBC_2.4 (7)
    21: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strchr@GLIBC_2.2.5 (3)
    22: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_mutex_destroy@GLIBC_2.2.5 (3)
    23: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND snprintf@GLIBC_2.2.5 (3)
    24: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strrchr@GLIBC_2.2.5 (3)
    25: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fputs@GLIBC_2.2.5 (3)
    26: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND memset@GLIBC_2.2.5 (3)
    27: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strncat@GLIBC_2.2.5 (3)
    28: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND closedir@GLIBC_2.2.5 (3)
    29: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fputc@GLIBC_2.2.5 (3)
    30: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strcspn@GLIBC_2.2.5 (3)
    31: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strtok_r@GLIBC_2.2.5 (3)
    32: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND calloc@GLIBC_2.2.5 (3)
    33: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strcmp@GLIBC_2.2.5 (3)
    34: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND dlopen@GLIBC_2.34 (4)
    35: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __memmove_chk@GLIBC_2.3.4 (2)
    36: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __memcpy_chk@GLIBC_2.3.4 (2)
    37: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    38: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND memcpy@GLIBC_2.14 (8)
    39: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __isoc23_strtol@GLIBC_2.38 (6)
    40: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fileno@GLIBC_2.2.5 (3)
    41: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND readdir@GLIBC_2.2.5 (3)
    42: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_mutex_unlock@GLIBC_2.2.5 (3)
    43: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND malloc@GLIBC_2.2.5 (3)
    44: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __vsnprintf_chk@GLIBC_2.3.4 (2)
    45: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __strncpy_chk@GLIBC_2.3.4 (2)
    46: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND realloc@GLIBC_2.2.5 (3)
    47: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND memmove@GLIBC_2.2.5 (3)
    48: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND access@GLIBC_2.2.5 (3)
    49: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fopen@GLIBC_2.2.5 (3)
    50: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND dlsym@GLIBC_2.34 (4)
    51: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __memset_chk@GLIBC_2.3.4 (2)
    52: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __strncat_chk@GLIBC_2.3.4 (2)
    53: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
    54: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strerror@GLIBC_2.2.5 (3)
    55: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND dlclose@GLIBC_2.34 (4)
    56: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_mutex_init@GLIBC_2.2.5 (3)
    57: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fstat@GLIBC_2.33 (9)
    58: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (3)
    59: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strstr@GLIBC_2.2.5 (3)
    60: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_mutex_lock@GLIBC_2.2.5 (3)
    61: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __ctype_tolower_loc@GLIBC_2.3 (10)
    62: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  UND stderr@GLIBC_2.2.5 (3)

Though I guess the drivers that libvulkan.so.1 loads would probably rely on many more symbols.