How to produce static executable on NixOS?

Viewed 1965

Today I met a really interesting problem with my NixOS distro. I just wanted to create a statically compiled OCaml progam and couldn`t do that. Then I tried to do that with an ANSI C canonical toy "hello world!" application:

$> cat mytest.c 
#include <stdio.h>

int
main ()
{
  puts ("hello world!") ;
}

My distro:

$> uname -a
Linux cat 4.19.36 #1-NixOS SMP Sat Apr 20 07:16:05 UTC 2019 x86_64 GNU/Linux

Compiler:

$> gcc -v
Using built-in specs.
COLLECT_GCC=/nix/store/myq0x6mjbdzxr9fckqn6kgy89kz19nkp-gfortran-7.4.0/bin/gcc
COLLECT_LTO_WRAPPER=/nix/store/myq0x6mjbdzxr9fckqn6kgy89kz19nkp-gfortran-7.4.0/libexec/gcc/x86_64-unknown-linux-gnu/7.4.0/lto-wrapper
Target: x86_64-unknown-linux-gnu
Configured with: 
Thread model: posix
gcc version 7.4.0 (GCC)

Cannot produce static exec :

$> gcc -static mytest.c -o hello
/nix/store/0y7jmqnj48ikjh37n3dl9kqw9hnn68nq-binutils-2.31.1/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status

Any ideas?

Usually a "dynamically linked" program hello is generated by gccwithout a problem.

No such issues on Lubuntu. I was advised to try a different distro and test that exec is running on NixOS. I did it - produced an exec with gcc on Lubuntu and started it on NixOS. Therefore I think that the problem is not with gcc but with NixOS.

How does NixOS treat this problem (i.e., generation of statically compiled exec files)?

And, of course, I'm also interested in the results concerning ocamlopt compiler rather then gcc but I think that the problem is common for all compilers (I tried Haskell ghc too by the way with the same results).

brgs

UPDATE: from a discussion on another thread:

  1 @Ston17 You may have the .so but not the .a – norok2 
  2 
  3 yes - i have .so what the difference? can the presence of .a improve the situation? – Ston17
  4 
  5 Yes. You typically need .a library to have the static linking work correctly – norok2


$> find /nix/store/ -name *libc.a.*
$> 

can this be the reason?

UPDATE2: as concerning ocamlopt:

source file

$> cat mytest.ml
print_string "hello world!" ;;
print_newline () ;;

as you can see no special calls to anything. let try to make static exec:

$> ocamlopt -ccopt -static mytest.ml -o ocaml_test
/nix/store/0y7jmqnj48ikjh37n3dl9kqw9hnn68nq-binutils-2.31.1/bin/ld: cannot find -lm
/nix/store/0y7jmqnj48ikjh37n3dl9kqw9hnn68nq-binutils-2.31.1/bin/ld: cannot find -ldl
/nix/store/0y7jmqnj48ikjh37n3dl9kqw9hnn68nq-binutils-2.31.1/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status

so ld just cannot link to static version of libc. and i cannot find libc.a in the hole system

any suggetions?

1 Answers

here https://vaibhavsagar.com/blog/2018/01/03/static-haskell-nix/ is explained how you could obtain in NixOS statical versions of sys libraries

nix-shell config file:

let
  pkgs = import <nixpkgs> {} ;
in pkgs.buildFHSUserEnv {
  name = "fhs" ;
  targetPkgs = pkgs: with pkgs; [
    pkgs.glibc.static
    pkgs.zlib.static
    pkgs.libffi
    pkgs.libtool
    pkgs.musl
    pkgs.ghc
    pkgs.gcc
    pkgs.ocaml
  ] ;
}

after that in nix-shell you start chroot FHS and copy needed sys libs into your folder and close chroot FHS

and after that compile your file staticaly

good with gcc:

$> gcc -static -L/home/.local/lib/ mytest.c -o ansiC_test
$> ldd ansiC_test 
  not a dynamic executable

not so good but maybe working with ocaml:

ocamlopt -ccopt -static -cclib -L/home/.local/lib mytest.ml -o ocaml_test

ocamlopt -ccopt -static -cclib -L/home/nomad/.local/lib mytest.ml -o ocaml_test
/nix/store/0y7jmqnj48ikjh37n3dl9kqw9hnn68nq-binutils-2.31.1/bin/ld: /nix/store/j1v6kkxq081q4m4fw7gazaf6rb3vy87p-ocaml-4.06.1/lib/ocaml/libasmrun.a(unix.o): in function `caml_dlopen':
/build/ocaml-4.06.1/asmrun/unix.c:273: warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking

$> ldd ocaml_test 
  not a dynamic executable

dont work with ghc though :

ghc -static -optl-static -L/home/.local/lib/ mytest.hs -o haskell_test
[1 of 1] Compiling Main             ( mytest.hs, mytest.o )
Linking haskell_test ...
/nix/store/0y7jmqnj48ikjh37n3dl9kqw9hnn68nq-binutils-2.31.1/bin/ld: /nix/store/wfgrz42bpcl1r635dasfk7r236hm83az-ghc-8.6.4/lib/ghc-8.6.4/rts/libHSrts.a(Linker.o): in function `internal_dlopen':
Linker.c:(.text.internal_dlopen+0x7): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/nix/store/0y7jmqnj48ikjh37n3dl9kqw9hnn68nq-binutils-2.31.1/bin/ld: cannot find -lffi
collect2: error: ld returned 1 exit status
`cc' failed in phase `Linker'. (Exit code: 1)
make: *** [makefile:5: haskell] Error 1

ok. ocaml works:

lubuntu@lubuntu:~/Documents$ ./ocaml_hello_nix 
hello world!

lubuntu@lubuntu:~/Documents$ readelf -l ocaml_hello_nix 

Elf file type is EXEC (Executable file)
Entry point 0x4017a0
There are 9 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x00000000000005d8 0x00000000000005d8  R      0x1000
  LOAD           0x0000000000001000 0x0000000000401000 0x0000000000401000
                 0x0000000000107275 0x0000000000107275  R E    0x1000
  LOAD           0x0000000000109000 0x0000000000509000 0x0000000000509000
                 0x00000000000db191 0x00000000000db191  R      0x1000
  LOAD           0x00000000001e5140 0x00000000005e6140 0x00000000005e6140
                 0x0000000000008a18 0x000000000001dfe0  RW     0x1000
  NOTE           0x0000000000000238 0x0000000000400238 0x0000000000400238
                 0x0000000000000020 0x0000000000000020  R      0x4
  NOTE           0x0000000000000258 0x0000000000400258 0x0000000000400258
                 0x0000000000000020 0x0000000000000020  R      0x8
  TLS            0x00000000001e5140 0x00000000005e6140 0x00000000005e6140
                 0x0000000000000020 0x0000000000000060  R      0x8
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000

                     0x0000000000000000 0x0000000000000000  RW     0x10
      GNU_RELRO      0x00000000001e5140 0x00000000005e6140 0x00000000005e6140
                     0x0000000000002ec0 0x0000000000002ec0  R      0x1

     Section to Segment mapping:
      Segment Sections...
       00     .note.ABI-tag .note.gnu.property .rela.plt 
       01     .init .plt .text __libc_freeres_fn __libc_thread_freeres_fn .fini 
       02     .rodata .eh_frame .gcc_except_table 
       03     .tdata .init_array .fini_array .data.rel.ro .got .got.plt .data __libc_subfreeres __libc_IO_vtables __libc_atexit __libc_thread_subfreeres .bss __libc_freeres_ptrs 
       04     .note.ABI-tag 
       05     .note.gnu.property 
       06     .tdata .tbss 
       07     
       08     .tdata .init_array .fini_array .data.rel.ro .got 

haskell works. the reason was that binary NixOS packet was built without FFI support. i installed ghc from sources and all become fine:

lubuntu@lubuntu:~/Documents$ ./haskell_hello_nix 
hello world!

lubuntu@lubuntu:~/Documents$ readelf -l haskell_hello_nix 

Elf file type is EXEC (Executable file)
Entry point 0x405d40
There are 6 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000188ba4 0x0000000000188ba4  R E    0x1000
  LOAD           0x0000000000188ec0 0x0000000000589ec0 0x0000000000589ec0
                 0x00000000000104c0 0x000000000001ac98  RW     0x1000
  NOTE           0x0000000000000190 0x0000000000400190 0x0000000000400190
                 0x0000000000000020 0x0000000000000020  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  TLS            0x0000000000188ec0 0x0000000000589ec0 0x0000000000589ec0
                 0x0000000000000070 0x00000000000000b8  R      0x8
  GNU_RELRO      0x0000000000188ec0 0x0000000000589ec0 0x0000000000589ec0
                 0x0000000000003140 0x0000000000003140  RW     0x20

 Section to Segment mapping:
  Segment Sections...
   00     .note.ABI-tag .rela.plt .init .plt .text __libc_thread_freeres_fn .fini .rodata .gcc_except_table .eh_frame 
   01     .tdata .data.rel.ro.local .fini_array .init_array .data.rel.ro .preinit_array .got .got.plt .data .tm_clone_table __libc_IO_vtables __libc_atexit __libc_thread_subfreeres .bss __libc_freeres_ptrs 
   02     .note.ABI-tag 
   03     
   04     .tdata .tbss 
   05     .tdata .data.rel.ro.local .fini_array .init_array .data.rel.ro .preinit_array .got 

good. tnx to all who was concerned. you save my a$$

Related