Skip to content

Commit 819d36a

Browse files
committed
Fixes using go .so and .a libraries with non glibc systems such as musl/uclinux when linking at compile time
1 parent 24fcbb9 commit 819d36a

File tree

7 files changed

+99
-10
lines changed

7 files changed

+99
-10
lines changed

misc/cgo/testcarchive/testdata/libgo/libgo.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@ func DidMainRun() bool { return ranMain }
4646

4747
//export CheckArgs
4848
func CheckArgs() {
49-
if len(os.Args) != 3 || os.Args[1] != "arg1" || os.Args[2] != "arg2" {
49+
// Dynamic linkers which supply the library initialization functions with the
50+
// main program's argc / argc should have 3 args here, else they should have
51+
// none.
52+
valid := (len(os.Args) == 3 && os.Args[1] == "arg1" && os.Args[2] == "arg2") || (len(os.Args) == 0)
53+
if !valid {
5054
fmt.Printf("CheckArgs: want [_, arg1, arg2], got: %v\n", os.Args)
5155
os.Exit(2)
5256
}

src/runtime/cgo.go

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import "unsafe"
1212

1313
//go:linkname _cgo_init _cgo_init
1414
//go:linkname _cgo_thread_start _cgo_thread_start
15+
//go:linkname _cgo_sys_lib_args_valid _cgo_sys_lib_args_valid
1516
//go:linkname _cgo_sys_thread_create _cgo_sys_thread_create
1617
//go:linkname _cgo_notify_runtime_init_done _cgo_notify_runtime_init_done
1718
//go:linkname _cgo_callers _cgo_callers
@@ -21,6 +22,7 @@ import "unsafe"
2122
var (
2223
_cgo_init unsafe.Pointer
2324
_cgo_thread_start unsafe.Pointer
25+
_cgo_sys_lib_args_valid unsafe.Pointer
2426
_cgo_sys_thread_create unsafe.Pointer
2527
_cgo_notify_runtime_init_done unsafe.Pointer
2628
_cgo_callers unsafe.Pointer

src/runtime/cgo/callbacks.go

+8
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,14 @@ var _cgo_init = &x_cgo_init
5858
var x_cgo_thread_start byte
5959
var _cgo_thread_start = &x_cgo_thread_start
6060

61+
// Determines if the argc / argv passed to the library initialization functions
62+
// are valid.
63+
//go:cgo_import_static x_cgo_sys_lib_args_valid
64+
//go:linkname x_cgo_sys_lib_args_valid x_cgo_sys_lib_args_valid
65+
//go:linkname _cgo_sys_lib_args_valid _cgo_sys_lib_args_valid
66+
var x_cgo_sys_lib_args_valid byte
67+
var _cgo_sys_lib_args_valid = &x_cgo_sys_lib_args_valid
68+
6169
// Creates a new system thread without updating any Go state.
6270
//
6371
// This method is invoked during shared library loading to create a new OS

src/runtime/cgo/gcc_libinit.c

+15
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <pthread.h>
99
#include <errno.h>
10+
#include <limits.h>
1011
#include <stdio.h>
1112
#include <stdlib.h>
1213
#include <string.h> // strerror
@@ -21,6 +22,20 @@ static int runtime_init_done;
2122
// The context function, used when tracing back C calls into Go.
2223
static void (*cgo_context_function)(struct context_arg*);
2324

25+
// Detect if using glibc
26+
int
27+
x_cgo_sys_lib_args_valid()
28+
{
29+
// The ELF gABI doesn't require an argc / argv to be passed to the functions
30+
// in the DT_INIT_ARRAY. However, glibc always does.
31+
// Ignore uClibc masquerading as glibc.
32+
#if defined(__GLIBC__) && !defined(__UCLIBC__)
33+
return 1;
34+
#else
35+
return 0;
36+
#endif
37+
}
38+
2439
void
2540
x_cgo_sys_thread_create(void* (*func)(void*), void* arg) {
2641
pthread_t p;

src/runtime/cgo/gcc_libinit_windows.c

+5
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ _cgo_maybe_run_preinit() {
5151
}
5252
}
5353

54+
int
55+
x_cgo_sys_lib_args_valid() {
56+
return 1;
57+
}
58+
5459
void
5560
x_cgo_sys_thread_create(void (*func)(void*), void* arg) {
5661
uintptr_t thandle;

src/runtime/os_linux.go

+32-9
Original file line numberDiff line numberDiff line change
@@ -218,19 +218,42 @@ func mincore(addr unsafe.Pointer, n uintptr, dst *byte) int32
218218
func sysargs(argc int32, argv **byte) {
219219
n := argc + 1
220220

221-
// skip over argv, envp to get to auxv
222-
for argv_index(argv, n) != nil {
223-
n++
221+
argsValid := true
222+
if islibrary || isarchive {
223+
if !sysLibArgsValid() {
224+
argsValid = false
225+
}
224226
}
225227

226-
// skip NULL separator
227-
n++
228+
if argsValid {
229+
// skip over argv, envp to get to auxv
230+
for argv_index(argv, n) != nil {
231+
n++
232+
}
228233

229-
// now argv+n is auxv
230-
auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*goarch.PtrSize))
231-
if sysauxv(auxv[:]) != 0 {
232-
return
234+
// skip NULL separator
235+
n++
236+
237+
// now argv+n is auxv
238+
auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*goarch.PtrSize))
239+
if sysauxv(auxv[:]) != 0 {
240+
return
241+
}
242+
} else {
243+
args := unsafe.Pointer(persistentalloc(goarch.PtrSize*4, 0, &memstats.other_sys))
244+
// argv pointer
245+
*(**byte)(args) = (*byte)(add(args, goarch.PtrSize*1))
246+
// argv data
247+
*(**byte)(add(args, goarch.PtrSize*1)) = (*byte)(nil) // end argv TODO: READ FROM /proc/
248+
*(**byte)(add(args, goarch.PtrSize*2)) = (*byte)(nil) // end envp TODO: READ FROM /proc/
249+
*(**byte)(add(args, goarch.PtrSize*3)) = (*byte)(nil) // end auxv TODO: READ FROM /proc/
250+
argc = 0
251+
argv = (**byte)(args)
252+
253+
// argc = 0
254+
// argv = (**byte)(&[3]*byte{nil, nil, nil})
233255
}
256+
234257
// In some situations we don't get a loader-provided
235258
// auxv, such as when loaded as a library on Android.
236259
// Fall back to /proc/self/auxv.

src/runtime/runtime1.go

+32
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,27 @@ var (
5252
argv **byte
5353
)
5454

55+
// when using -buildmode=c-archive or -buildmode=c-shared on linux
56+
// we have to first make sure that glibc is being used or else
57+
// we cannot rely on argc/argv/auxv to be accurate
58+
func sysLibArgsValid() bool {
59+
if _cgo_sys_lib_args_valid != nil {
60+
ret := asmcgocall(_cgo_sys_lib_args_valid, nil)
61+
if ret != 1 {
62+
return false
63+
}
64+
}
65+
return true
66+
}
67+
5568
// nosplit for use in linux startup sysargs
5669
//go:nosplit
5770
func argv_index(argv **byte, i int32) *byte {
71+
if islibrary || isarchive {
72+
if !sysLibArgsValid() {
73+
return nil
74+
}
75+
}
5876
return *(**byte)(add(unsafe.Pointer(argv), uintptr(i)*goarch.PtrSize))
5977
}
6078

@@ -68,13 +86,27 @@ func goargs() {
6886
if GOOS == "windows" {
6987
return
7088
}
89+
90+
if islibrary || isarchive {
91+
if !sysLibArgsValid() {
92+
return
93+
}
94+
}
95+
7196
argslice = make([]string, argc)
7297
for i := int32(0); i < argc; i++ {
7398
argslice[i] = gostringnocopy(argv_index(argv, i))
7499
}
75100
}
76101

77102
func goenvs_unix() {
103+
if islibrary || isarchive {
104+
if !sysLibArgsValid() {
105+
envs = make([]string, 0)
106+
return
107+
}
108+
}
109+
78110
// TODO(austin): ppc64 in dynamic linking mode doesn't
79111
// guarantee env[] will immediately follow argv. Might cause
80112
// problems.

0 commit comments

Comments
 (0)