gccgo, LD_PRELOAD, and lagging behind on Go versions

478 views
Skip to first unread message

Peter JIN

unread,
May 31, 2024, 1:25:09 AMMay 31
to golang-dev
Can we please make the update of gccgo to latest Go versions a bigger priority?

My use case: I have several LD_PRELOAD libraries written in C. To make them work with programs written in Go, I have to compile it with gccgo to make it dynamically linked -- static linking is not an option. However, it seems like gccgo has been stuck on 1.18 for a while, at least I can see for about the past two years. This is problematic mostly because many of the dependencies in some of the Go programs I use require the use of later Go versions, making it not possible to use with gccgo.

Forgoing the use of LD_PRELOAD is not an option, but I would be open to any alternative ideas, including overriding functions in the standard library.

Any ideas?

side note: IMHO, static linking is a mistake. It specifically relies on the fact that the Linux kernel ABI is much more stable than the glibc API in terms of function calls. And interposing system calls is much more difficult because it involves a kernel-user boundary -- effectively, you would need to modify the CPU's interrupt vector table to "intercept" a system call, whereas function calls in glibc can much more easily be interposed in the user space.

Peter

wagner riiffel

unread,
May 31, 2024, 7:40:37 PMMay 31
to Peter JIN, golang-dev
On 5/31/2024 1:32 AM, Peter JIN wrote:
> side note: IMHO, static linking is a mistake. It specifically relies on the
> fact that the Linux kernel ABI is much more stable than the glibc API in
> terms of function calls.

If Linux has an API more stable than glibc, why would one choose the
later? Static linking is the only sane option, specially on Linux land,
where there's multiple libc implementations, e.g. Alpine Linux which
does not use glibc and is a popular target for Go programs, thus built
programs that dynamically link against glibc won't execute there, making
a needless split to distribute the program.

-w

Ian Lance Taylor

unread,
May 31, 2024, 7:43:04 PMMay 31
to Peter JIN, golang-dev
On Thu, May 30, 2024 at 10:25 PM Peter JIN <pjin...@gmail.com> wrote:
>
> Can we please make the update of gccgo to latest Go versions a bigger priority?
>
> My use case: I have several LD_PRELOAD libraries written in C. To make them work with programs written in Go, I have to compile it with gccgo to make it dynamically linked -- static linking is not an option. However, it seems like gccgo has been stuck on 1.18 for a while, at least I can see for about the past two years. This is problematic mostly because many of the dependencies in some of the Go programs I use require the use of later Go versions, making it not possible to use with gccgo.

Unfortunately at this point gccgo development is basically me working
on my own time, and I don't have much time.

Ian

Peter JIN

unread,
May 31, 2024, 10:07:37 PMMay 31
to golang-dev
As I said, the main issue is that to "intercept" a system call, you need to intercept a system call instruction like "int $0x80" in i386, "syscall" in x86_64, or "svc 0" on ARM. This is much more difficult to do, especially in a user space program, because it would ultimately require changing GDT/IDT/IVT entries in the CPU, which is not really feasible or allowed in a userspace program.

On the other hand, overriding a library function is simply just a matter of changing where the function pointers in the PLT entries point to, namely in the LD_PRELOAD library rather than to libc, which is an operation easily done in user space.

I have been considering the use of the seccomp functions SECCOMP_RET_TRAP and SECCOMP_RET_USER_NOTIF along with a "program loader" that reads the ELF header and installs a SIGSYS handler to do effectively what the LD_PRELOAD library would have done, though I'm not sure if that is a good long-term solution, especially if it requires architecture-specific assembler instructions.

That being said, my use of LD_PRELOAD is never to "decrease" the privileges of a running program. There should be other means of ensuring that the privileges are contained, such as with mount, network, and user namespaces. LD_PRELOAD is mainly intended to facilitate the use of "alternate access" where the containerized environment has access to such facilities, it just needs to be made to use it, for example, connecting to a Unix domain socket, but the app itself only supports inet sockets.

The glibc version incompatibility issue could theoretically be fixed by the use of weak symbols and by probing for the existence of those symbols at run time or by additional LD_PRELOAD libraries to retrofit in the newer-version symbols into older versions of glibc.

Hope that helps,

Peter

Peter JIN

unread,
Aug 17, 2024, 12:07:38 AMAug 17
to golang-dev
I did a bit more analysis on this, and it appears that the syscall instruction is performed in runtime/internal/syscall.Syscall6, as shown from the objdump output of the .a file created by go build -compiler gc -buildmode c-archive.

I was wondering whether it might be possible to replace this function with a cgo call to the libc syscall function, or if doing so will result in an infinite loop.

Note that I will have to patch this function in the original location in memory, as it is hard referenced by call instructions in other parts of the code (as opposed to PLT32 relocations), but it can simply just be a call to jmp my_patched_function@PLT (5 bytes) rather than the whole function.

Peter

Sent from my iPhone

On May 31, 2024, at 10:07 PM, Peter JIN <pjin...@gmail.com> wrote:


--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-dev/30a13c6b-a1d2-4eee-bc6c-14b845cc845an%40googlegroups.com.

Ian Lance Taylor

unread,
Aug 17, 2024, 8:40:05 PMAug 17
to Peter JIN, golang-dev
On Fri, Aug 16, 2024 at 9:07 PM Peter JIN <pjin...@gmail.com> wrote:
>
> I did a bit more analysis on this, and it appears that the syscall instruction is performed in runtime/internal/syscall.Syscall6, as shown from the objdump output of the .a file created by go build -compiler gc -buildmode c-archive.
>
> I was wondering whether it might be possible to replace this function with a cgo call to the libc syscall function, or if doing so will result in an infinite loop.
>
> Note that I will have to patch this function in the original location in memory, as it is hard referenced by call instructions in other parts of the code (as opposed to PLT32 relocations), but it can simply just be a call to jmp my_patched_function@PLT (5 bytes) rather than the whole function.

It's possible in principle to replace the call in
runtime/internal/syscall with a call to the C function, but it's
awkward. We can't use cgo as that would introduce a cyclical
dependency, as the cgo generated itself depends on the runtime
package. But we could do it the way we arrange to call the C mmap
function in runtime/cgo_mmap.go.

This isn't something we would change in the standard library unless we
had to for some reason.

Ian
Reply all
Reply to author
Forward
0 new messages