vlang / v

Simple, fast, safe, compiled language for developing maintainable software. Compiles itself in <1s with zero library dependencies. Supports automatic C => V translation. https://vlang.io
MIT License
35.51k stars 2.15k forks source link

Unclear how to compile a static binary #21760

Open justinas opened 3 days ago

justinas commented 3 days ago

Describe the bug

The V website claims that "You get a single statically linked binary". However, by default the compiler produces a dynamic binary, and there seems to be no way to produce a working static binary.

Reproduction Steps

Compile any code with V. Result is a dynamically linked binary.

Expected Behavior

Expected V to produce a statically linked binary.

Current Behavior

Compiling without any additional flags produces a dynamic binary:

$ cat hello.v
fn main() {
    println("hello world")
}
$ v hello.v
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, not stripped

Compiling with musl-gcc as suggested in https://github.com/vlang/v/discussions/19792#discussioncomment-7510933 does not work because pthreads fail to link:

$ v -cc musl-gcc -cg hello.v
/usr/bin/ld: /home/justinas/vlang-box/compiler/thirdparty/tcc/lib/libgc.a(gc.o): in function `open_proc_fd':
gc.c:(.text+0x13a7): undefined reference to `__snprintf_chk'
/usr/bin/ld: gc.c:(.text+0x13ba): undefined reference to `__open_2'
/usr/bin/ld: /home/justinas/vlang-box/compiler/thirdparty/tcc/lib/libgc.a(gc.o): in function `GC_fault_handler':
gc.c:(.text+0x14aa): undefined reference to `__longjmp_chk'
/usr/bin/ld: /home/justinas/vlang-box/compiler/thirdparty/tcc/lib/libgc.a(gc.o): in function `GC_with_callee_saves_pushed':
gc.c:(.text+0x2f2c): undefined reference to `getcontext'
/usr/bin/ld: /home/justinas/vlang-box/compiler/thirdparty/tcc/lib/libgc.a(gc.o): in function `GC_printf':
gc.c:(.text+0x420a): undefined reference to `__vsnprintf_chk'
/usr/bin/ld: /home/justinas/vlang-box/compiler/thirdparty/tcc/lib/libgc.a(gc.o): in function `GC_log_printf':
gc.c:(.text+0x4867): undefined reference to `__vsnprintf_chk'
/usr/bin/ld: /home/justinas/vlang-box/compiler/thirdparty/tcc/lib/libgc.a(gc.o): in function `GC_err_printf':
gc.c:(.text+0x79f4): undefined reference to `__vsnprintf_chk'
/usr/bin/ld: /home/justinas/vlang-box/compiler/thirdparty/tcc/lib/libgc.a(gc.o): in function `GC_print_callers':
gc.c:(.text+0x7c54): undefined reference to `__snprintf_chk'
/usr/bin/ld: gc.c:(.text+0x7cef): undefined reference to `__snprintf_chk'
/usr/bin/ld: gc.c:(.text+0x7d36): undefined reference to `__memcpy_chk'
/usr/bin/ld: gc.c:(.text+0x7e64): undefined reference to `__snprintf_chk'
/usr/bin/ld: /home/justinas/vlang-box/compiler/thirdparty/tcc/lib/libgc.a(gc.o): in function `GC_print_all_errors.part.0':
gc.c:(.text+0x826e): undefined reference to `__memcpy_chk'
/usr/bin/ld: gc.c:(.text+0x828a): undefined reference to `__memset_chk'
/usr/bin/ld: /home/justinas/vlang-box/compiler/thirdparty/tcc/lib/libgc.a(gc.o): in function `GC_init':
gc.c:(.text+0xf53e): undefined reference to `gnu_get_libc_version'
/usr/bin/ld: /home/justinas/vlang-box/compiler/thirdparty/tcc/lib/libgc.a(pthread_start.o): in function `GC_pthread_start_inner':
pthread_start.c:(.text+0x82): undefined reference to `__pthread_register_cancel'
/usr/bin/ld: pthread_start.c:(.text+0xa9): undefined reference to `__pthread_unregister_cancel'
collect2: error: ld returned 1 exit status
builder error:
==================
C error found. It should never happen, when compiling pure V code.
This is a V compiler bug, please report it using `v bug file.v`,
or goto https://github.com/vlang/v/issues/new/choose .
You can also use #help on Discord: https://discord.gg/vlang .

Compiling with -freestanding as suggested by a Stack Overflow answer does produce a static binary, but it also makes V not link the C standard library at all (statically or dynamically). Since basic functionality of V depends on libc, this means that even basic code does not work out of the box in this mode:

$ cat array.v
fn main() {
    mut foo := [1, 2, 3]
    foo.sort()
    println(foo)
}
$ v -freestanding array.v
$ file array
array: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=19986c1fa887f421581deb1982106b06a5a33ee6, not stripped
$ ./array
V panicsort does not work with -freestanding

Possible Solution

V should either:

  1. Output static binaries by default, since that is the claim on the front page.
  2. Provide a dedicated, well documented flag that would link the resulting binary statically instead of dynamically, e.g. v -static. V shouldn't lose basic functionality when the code is compiled this way.

Additional Information/Context

No response

V version

V 0.4.6 4a7c70c

Environment details (OS name and version, etc.)

$ v doctor
V full version: V 0.4.6 4a7c70c
OS: linux, Linux version 6.6.33 (nixbld@localhost) (gcc (GCC) 13.2.0, GNU ld (GNU Binutils) 2.41) #1-NixOS SMP PREEMPT_DYNAMIC Wed Jun 12 09:13:03 UTC 2024 (chroot)
Processor: 16 cpus, 64bit, little endian, AMD Ryzen 7 PRO 4750U with Radeon Graphics

getwd: /home/justinas/vlang-testcases
vexe: /home/justinas/vlang-box/compiler/v
vexe mtime: 2024-06-28 20:18:31

vroot: OK, value: /home/justinas/vlang-box/compiler
VMODULES: OK, value: /home/justinas/.vmodules
VTMP: OK, value: /tmp/v_1000

Git version: git version 2.43.0
Git vroot status: weekly.2024.26-14-g4a7c70c9 (3 commit(s) behind V master)
.git/config present: true

CC version: cc (Ubuntu 13.2.0-23ubuntu4) 13.2.0
thirdparty/tcc status: thirdparty-linux-amd64 40e5cbb5

[!NOTE] You can use the 👍 reaction to increase the issue's priority for developers.

Please note that only the 👍 reaction to the issue itself counts as a vote. Other reactions and those to comments will not be taken into account.

JalonSolov commented 3 days ago

v -cflags "-static" hello.v

Results in

$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=5218322b2e2cfe1833e376dff28d3a93ddf4c9be, for GNU/Linux 4.4.0, not stripped
justinas commented 3 days ago

v -cflags "-static" hello.v

Results in

$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=5218322b2e2cfe1833e376dff28d3a93ddf4c9be, for GNU/Linux 4.4.0, not stripped

Cool! It does work on Ubuntu. On NixOS, you need to have glibc.static.

$ nix-shell -p glibc.static vlang
$ v -cflags -static hello.v
$ file ./hello
./hello: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.10.0, not stripped
$ ./hello
hello world

I still think that either this should be made the default, or the necessary flags clearly documented, or the claims about producing static binaries adjusted.

On another note, -cflags will of course have no effect on the native backend, so eventually (once native backend is viable) something like a dedicated -static flag will be necessary anyway.

JalonSolov commented 3 days ago

I doubt it will be made the default, as it increases the file size by quite a bit...

[jalon@7950x ~]$ v hello.v
[jalon@7950x ~]$ ls -l hello
-rwxr-xr-x 1 jalon jalon 701432 Jun 29 15:56 hello
[jalon@7950x ~]$ v -cflags -static hello.v
[jalon@7950x ~]$ ls -l hello
-rwxr-xr-x 1 jalon jalon 1416784 Jun 29 15:56 hello
[jalon@7950x ~]$ 

... over twice as large output.

However, agreed that it could be made more obvious how to achieve statically linked binaries, and that it will also be necessary for a switch that works with the native backend... once that's actually working. :-)