diff --git a/src/os/env.go b/src/os/env.go index 9f574b53..330297b3 100644 --- a/src/os/env.go +++ b/src/os/env.go @@ -8,7 +8,6 @@ package os import ( "internal/testlog" - "strings" "syscall" ) @@ -138,13 +137,5 @@ func Clearenv() { // Environ returns a copy of strings representing the environment, // in the form "key=value". func Environ() []string { - orig := syscall.Environ() - single := strings.Join(orig, "") - env := make([]string, len(orig)) - for i, v := range orig { - s := single[:len(v)] - env[i] = s - single = single[len(v):] - } - return env + return syscall.Environ() } diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go index da46b9ff..0ef64984 100644 --- a/src/syscall/syscall_libc.go +++ b/src/syscall/syscall_libc.go @@ -235,11 +235,24 @@ func Mprotect(b []byte, prot int) (err error) { } func Environ() []string { - environ := libc_environ - var envs []string - for *environ != nil { - // Convert the C string to a Go string. - length := libc_strlen(*environ) + // calculate total memory required + var length uintptr + var vars int + for environ := libc_environ; *environ != nil; { + length += libc_strlen(*environ) + vars++ + environ = (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(environ)) + unsafe.Sizeof(environ))) + } + + // allocate our backing slice for the strings + b := make([]byte, length) + // and the slice we're going to return + envs := make([]string, 0, vars) + + // loop over the environment again, this time copying over the data to the backing slice + for environ := libc_environ; *environ != nil; { + length = libc_strlen(*environ) + // construct a Go string pointing at the libc-allocated environment variable data var envVar string rawEnvVar := (*struct { ptr unsafe.Pointer @@ -247,8 +260,16 @@ func Environ() []string { })(unsafe.Pointer(&envVar)) rawEnvVar.ptr = *environ rawEnvVar.length = length - envs = append(envs, envVar) - // This is the Go equivalent of "environ++" in C. + // pull off the number of bytes we need for this environment variable + var bs []byte + bs, b = b[:length], b[length:] + // copy over the bytes to the Go heap + copy(bs, envVar) + // convert trimmed slice to string + s := *(*string)(unsafe.Pointer(&bs)) + // add s to our list of environment variables + envs = append(envs, s) + // environ++ environ = (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(environ)) + unsafe.Sizeof(environ))) } return envs