ziglang/zig

relocation error: symbol pthread_sigmask version GLIBC_2.2.5 not defined in file libc.so.6 with link time reference

lithdew opened this issue · 8 comments

Encountered this error while trying to run CI tests on Github Actions for a Zig project, using the Zig nightly bundle provided here: https://ziglang.org/builds/zig-linux-x86_64-0.7.1+dfacac916.tar.xz. Linking with musl makes the problem go away.

The latest glibc version available in Github Actions is 2.27.

root@dew:/github/workspace# ldd ./zig-cache/o/ea49ec9c878272fe2410dfdf1fc66939/test
        linux-vdso.so.1 (0x00007ffe5b38e000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa3c72c5000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fa3c6f27000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fa3c6d08000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fa3c6b04000)
        librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fa3c68fc000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fa3c76b6000)
        libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007fa3c66f9000)
root@dew:/github/workspace# nm -D /lib/x86_64-linux-gnu/libpthread.so.0 | grep pthread_sig
000000000000f290 T pthread_sigmask
000000000000f3e0 T pthread_sigqueue
root@dew:/github/workspace# nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep pthread_sig
root@dew:/github/workspace# 

Reproduction requires an 18.2GB Docker image that emulates the Github Actions CI Environment, which can be found here: https://hub.docker.com/layers/nektos/act-environments-ubuntu/18.04/images/sha256-4d991875312e2ad084ff5350aa193e8a7dbdc4c144476d48df6a69e1e3404427

References to pthread symbols in built executable:

root@dew:/github/workspace# objdump -T ./zig-cache/o/ea49ec9c878272fe2410dfdf1fc66939/test | grep pthread
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.3.2 pthread_cond_destroy
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.3.2 pthread_cond_init
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.3.2 pthread_cond_signal
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.3.2 pthread_cond_wait
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_create
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_getspecific
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_join
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_key_create
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_key_delete
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.12  pthread_mutex_consistent
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_mutex_destroy
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_mutex_init
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_mutex_lock
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_mutex_unlock
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_mutexattr_destroy
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_mutexattr_init
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_mutexattr_setpshared
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.12  pthread_mutexattr_setrobust
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_self
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_setspecific
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_sigmask
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_atfork
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_mutex_trylock

hello.zig reduction (fails on issue host, works for my host)

const std = @import("std");

// hack to force symbol requirement
extern "c" var pthread_sigmask: u32;

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    try stdout.print("Hello, {}!\n", .{"world"});
    try stdout.print("Hello, {d}!\n", .{pthread_sigmask});
}

output from readelf -a hello on archlinux w/ glibc 2.32; snipped to sections/headers that have pthread_sigmask

Dynamic section at offset 0x38f70 contains 27 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libpthread.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libdl.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [librt.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [ld-linux-x86-64.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libutil.so.1]
[SNIP]

Relocation section '.rela.dyn' at offset 0x850 contains 5 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
00000023b138  000100000006 R_X86_64_GLOB_DAT 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
00000023b140  000200000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
00000023a998  000300000001 R_X86_64_64       0000000000000000 __errno_location@GLIBC_2.2.5 + 0
00000023b150  000900000006 R_X86_64_GLOB_DAT 0000000000000000 environ@GLIBC_2.2.5 + 0
00000023b148  001400000006 R_X86_64_GLOB_DAT 0000000000000000 pthread_sigmask@GLIBC_2.2.5 + 0

Symbol table '.symtab' contains 928 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
[SNIP]
   919: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_mutex_lock
   920: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_mutex_unlock
   921: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_sigmask

your quick grep on discord showed 3 matches to pthread_sigmask for the command. Can you post here?

@daurnimator found this:

https://sourceware.org/pipermail/glibc-cvs/2020q2/069412.html

TL;DR: as of glibc 2.32 symbol for pthread_sigmask moved from libpthreadlibc and it might be the trigger for this issue; issue host is a glibc 2.27 system.

your quick grep on discord showed 3 matches to pthread_sigmask for the command. Can you post here?

Sure:

root@dew:/github/workspace# readelf -a /root/.cache/zig/o/9ef07c7e75e1fe8f2ae6df075cfa21ed/hello | grep pthread_sigmask
00000023adb8  001400000006 R_X86_64_GLOB_DAT 0000000000000000 pthread_sigmask@GLIBC_2.2.5 + 0
    20: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_sigmask@GLIBC_2.2.5 (2)
   904: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_sigmask

@daurnimator found this:

https://sourceware.org/pipermail/glibc-cvs/2020q2/069412.html

TL;DR: as of glibc 2.32 symbol for pthread_sigmask moved from libpthreadlibc and it might be the trigger for this issue; issue host is a glibc 2.27 system.

Hmm, is there any way to workaround this?

I also faced this issue (and suspect the same underlying cause) but was unable to find a reasonable workaround.

I am getting this too on an internal project (which requires use of glibc 2.19):

id: relocation error: /lib/x86_64-linux-gnu/libnss_uber.so.2: symbol pthread_sigmask, version GLIBC_2.2.5 not defined in file libc.so.6 with link time reference

+1 on "would be nice to find a reasonable workaround". I will try to find some time to read about linkers and hopefully find one.

It seems that the problem might be related more to dynamically loaded libraries and incorrect symbol mapping to library from zig side.

Minimal reproducible c code example in debian jessie:
a.c file compiled by running: zig cc --target=x86_64-linux-gnu.2.19 -o a a.c

#define _GNU_SOURCE
#include <dlfcn.h>

int main()
{
  void *lib = dlopen("./libshared.so", RTLD_LAZY);
  void (*exported_func)(void);
  exported_func = (void (*)(void))dlsym(lib, "exported_func");
  exported_func();

  return 0; 
}

sharedlib.c compiled into libshared.so by running the following commands:

zig cc --target=x86_64-linux-gnu.2.19  -c -Wall -Werror -fpic sharedlib.c -o sharedlib.o
zig cc  -shared -o libshared.so sharedlib.o -lpthread
#include <signal.h>
#include <stdio.h>

void exported_func()
{
    printf("Address of pthread_sigmask: %p\n", pthread_sigmask);
}

Output of readelf -Wa libshared.so | grep -Ei "pthread_sigmask|GLIBC.*Version" -1

0000000000002610  0000000100000006 R_X86_64_GLOB_DAT      0000000000000000 __gmon_start__ + 0
0000000000002618  0000000200000006 R_X86_64_GLOB_DAT      0000000000000000 pthread_sigmask@GLIBC_2.2.5 + 0

--
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_sigmask@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.2.5 (2)
--
     6: 0000000000001430    24 FUNC    GLOBAL DEFAULT   12 exported_func
     7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_sigmask
     8: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf
-- .gnu.version_r 
  000000: Version: 1  File: libc.so.6  Cnt: 1
  0x0010:   Name: GLIBC_2.2.5  Flags: none  Version: 2

It seems that zig maps pthread_sigmask to libc library in .gnu.version_r section, but this symbol is still in libpthread in glibc 2.19. I assume that this fns.txt file is used to map symbols to specific libraries, because once I change this line pthread_sigmask c to pthread_sigmask pthread then zig cc correctly maps pthread_sigmask to libpthread and relocation error is gone:

0000000000002630  0000000100000006 R_X86_64_GLOB_DAT      0000000000000000 __gmon_start__ + 0
0000000000002638  0000000200000006 R_X86_64_GLOB_DAT      0000000000000000 pthread_sigmask@GLIBC_2.2.5 + 0

--
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_sigmask@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.2.5 (3)
--
     6: 0000000000001450    24 FUNC    GLOBAL DEFAULT   12 exported_func
     7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_sigmask
     8: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf
--
  000000: Version: 1  File: libpthread.so.0  Cnt: 1
  0x0020:   Name: GLIBC_2.2.5  Flags: none  Version: 2
  0x0010: Version: 1  File: libc.so.6  Cnt: 1
  0x0030:   Name: GLIBC_2.2.5  Flags: none  Version: 3

I assume that this edge case where a symbol can move between libraries might be overlooked and would need to be fixed by either having a separate fns.txt file for each glibc version or by introducing some versioning where there might be 2 lines for symbols like pthread_sigmask with something like pthread_sigmask pthread@2.31 where @2.31 is the last version where symbol is in that library. So when choosing specific glibc target version, correct symbol library mapping would be used.

@mjonaitis1 thanks for looking into this. Your suggested solution seems reasonable to me.