Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions _demo/go/export/export.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"fmt"
"runtime"
"unsafe"

Expand Down Expand Up @@ -134,6 +135,11 @@ func RunGoroutine(value int) int {
return <-ch
}

//export FormatValue
func FormatValue(value string, number int) string {
return fmt.Sprintf("%s:%d", value, number)
}

// Functions with small struct parameters and return values

//export CreateSmallStruct
Expand Down
6 changes: 6 additions & 0 deletions _demo/go/export/libexport.h.want
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ mul(float a, float b);
void
github_com_goplus_llgo__demo_go_export_c_init(void) GO_SYMBOL_RENAME("github.com/goplus/llgo/_demo/go/export/c.init")

intptr_t
AllThreadsSyscallStatus(void);

main_ComplexData
CreateComplexData(void);

Expand Down Expand Up @@ -163,6 +166,9 @@ CreateStringMap(void);
C_XType
CreateXType(int32_t id, GoString name, double value, _Bool flag);

GoString
FormatValue(GoString value, intptr_t number);

main_FuncInfoResult
GetFuncInfo(void);

Expand Down
28 changes: 28 additions & 0 deletions _demo/go/export/runtime_hooks_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//go:build linux

package main

import "syscall"

func gettimeofdayStatus() int {
var tv syscall.Timeval
if err := syscall.Gettimeofday(&tv); err != nil {
if errno, ok := err.(syscall.Errno); ok {
return int(errno)
}
return int(syscall.EINVAL)
}
if tv.Sec <= 0 {
return int(syscall.EINVAL)
}
return 0
}

//export AllThreadsSyscallStatus
func AllThreadsSyscallStatus() int {
if status := gettimeofdayStatus(); status != 0 {
return status
}
_, _, err := syscall.AllThreadsSyscall(syscall.SYS_GETPID, 0, 0, 0)
return int(err)
}
8 changes: 8 additions & 0 deletions _demo/go/export/runtime_hooks_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//go:build !linux

package main

//export AllThreadsSyscallStatus
func AllThreadsSyscallStatus() int {
return 0
}
3 changes: 2 additions & 1 deletion _demo/go/export/use/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ else
SHARED_EXT = so
PLATFORM_LIBS = $(shell pkg-config --libs libunwind 2>/dev/null || echo -lunwind)
endif
STATIC_STDLIB_LIBS = $(shell pkg-config --libs libffi 2>/dev/null || echo -lffi) -lresolv

# Library and flags based on link type
ifeq ($(LINK_TYPE),shared)
Expand All @@ -35,7 +36,7 @@ ifeq ($(LINK_TYPE),shared)
else
BUILDMODE = c-archive
LIBRARY = ../libexport.a
LDFLAGS = $(LIBRARY) $(RUNTIME_LIBS) $(PLATFORM_LIBS)
LDFLAGS = $(LIBRARY) $(RUNTIME_LIBS) $(PLATFORM_LIBS) $(STATIC_STDLIB_LIBS)
BUILD_MSG = "Building Go static library..."
LINK_MSG = "Linking with static library..."
endif
Expand Down
11 changes: 11 additions & 0 deletions _demo/go/export/use/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <stdlib.h>
#include <inttypes.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include "../libexport.h"

Expand Down Expand Up @@ -59,6 +60,16 @@ int main() {
HelloWorld();
printf("\n");

// Verify that a C library can initialize and call standard-library paths
// that depend on the runtime hooks supplied by LLGo.
GoString formatted = FormatValue((GoString){"answer", 6}, 42);
assert(go_string_equals(formatted, "answer:42"));
#ifdef __linux__
assert(AllThreadsSyscallStatus() == ENOTSUP);
#else
assert(AllThreadsSyscallStatus() == 0);
#endif

// Test small struct
main_SmallStruct small = CreateSmallStruct(5, 1); // 1 for true
assert(small.ID == 5);
Expand Down
62 changes: 39 additions & 23 deletions internal/build/main_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,27 @@ func genMainModule(ctx *context, rtPkgPath string, pkg *packages.Package, cfg *g
},
LPkg: mainPkg,
}
var rtInit llssa.Function
if cfg.rtInit {
rtInit = declareNoArgFunc(mainPkg, rtPkgPath+".init")
}
var abiInit llssa.Function
if cfg.abiInit != 0 {
abiInit = mainPkg.InitAbiTypesFor("init$abitypes", func(sym *llssa.AbiSymbol) bool {
if _, ok := cfg.abiSymbols[sym.Name]; !ok {
return false
}
return filterAbiSymbol(cfg.abiInit, sym)
})
}

if ctx.buildConf.BuildMode != BuildModeExe {
if cfg.rtInit {
defineLibraryRuntimeInit(mainPkg, declareNoArgFunc(mainPkg, rtPkgPath+".init"))
if rtInit != nil || abiInit != nil {
initArraySection := ""
if ctx.buildConf.BuildMode == BuildModeCShared && ctx.buildConf.Goos == "linux" {
initArraySection = ".init_array"
}
defineLibraryRuntimeInit(mainPkg, rtInit, abiInit, initArraySection)
}
return mainAPkg
}
Expand All @@ -95,21 +112,6 @@ func genMainModule(ctx *context, rtPkgPath string, pkg *packages.Package, cfg *g
pyFinalize = declareNoArgFunc(mainPkg, "Py_Finalize")
}

var rtInit llssa.Function
if cfg.rtInit {
rtInit = declareNoArgFunc(mainPkg, rtPkgPath+".init")
}

var abiInit llssa.Function
if cfg.abiInit != 0 {
abiInit = mainPkg.InitAbiTypesFor("init$abitypes", func(sym *llssa.AbiSymbol) bool {
if _, ok := cfg.abiSymbols[sym.Name]; !ok {
return false
}
return filterAbiSymbol(cfg.abiInit, sym)
})
}

mainInit := declareNoArgFunc(mainPkg, pkg.PkgPath+".init")
mainMain := declareNoArgFunc(mainPkg, pkg.PkgPath+".main")

Expand All @@ -130,20 +132,34 @@ func genMainModule(ctx *context, rtPkgPath string, pkg *packages.Package, cfg *g
return mainAPkg
}

// defineLibraryRuntimeInit arranges for the LLGo runtime to be initialized
// before a C program calls an exported Go function. llvm.global_ctors is
// lowered to the platform's native constructor mechanism for both shared
// libraries and archive members.
func defineLibraryRuntimeInit(pkg llssa.Package, rtInit llssa.Function) {
// defineLibraryRuntimeInit arranges for the LLGo runtime and reflection ABI
// tables to be initialized before a C program calls an exported Go function.
// Linux shared libraries use .init_array explicitly because the raw LLVM
// target machine lowers llvm.global_ctors to legacy .ctors; other library
// formats use LLVM's platform-specific constructor lowering.
func defineLibraryRuntimeInit(pkg llssa.Package, rtInit, abiInit llssa.Function, initArraySection string) {
const ctorName = "__llgo_runtime_ctor"
ctor := pkg.NewFunc(ctorName, llssa.NoArgsNoRet, llssa.InC)
ctorValue := pkg.Module().NamedFunction(ctorName)
ctorValue.SetLinkage(llvm.InternalLinkage)
b := ctor.MakeBody(1)
b.Call(rtInit.Expr)
if rtInit != nil {
b.Call(rtInit.Expr)
}
if abiInit != nil {
b.Call(abiInit.Expr)
}
b.Return()

mod := pkg.Module()
if initArraySection != "" {
initEntry := llvm.AddGlobal(mod, ctorValue.Type(), "__llgo_runtime_ctor_init")
initEntry.SetInitializer(ctorValue)
initEntry.SetGlobalConstant(true)
initEntry.SetSection(initArraySection)
initEntry.SetVisibility(llvm.HiddenVisibility)
return
}
llvmCtx := mod.Context()
priority := llvm.ConstInt(llvmCtx.Int32Type(), 65535, false)
entry := llvmCtx.ConstStruct([]llvm.Value{
Expand Down
21 changes: 20 additions & 1 deletion internal/build/main_module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,14 @@ func TestGenMainModuleLibraryInitializesRuntime(t *testing.T) {
mod := genMainModule(ctx, llssa.PkgRuntime, pkg, &genConfig{rtInit: true})
ir := mod.LPkg.String()
checks := []string{
"@llvm.global_ctors = appending global",
"define internal void @__llgo_runtime_ctor()",
"call void @\"github.com/goplus/llgo/runtime/internal/runtime.init\"()",
}
if mode == BuildModeCShared {
checks = append(checks, `@__llgo_runtime_ctor_init = hidden constant ptr @__llgo_runtime_ctor, section ".init_array"`)
} else {
checks = append(checks, "@llvm.global_ctors = appending global")
}
for _, want := range checks {
if !strings.Contains(ir, want) {
t.Fatalf("library module IR missing %q:\n%s", want, ir)
Expand All @@ -113,6 +117,21 @@ func TestGenMainModuleLibraryInitializesRuntime(t *testing.T) {
}
}

func TestDefineLibraryRuntimeInitInitializesAbiTypes(t *testing.T) {
prog := llssa.NewProgram(nil)
pkg := prog.NewPackage("", "example.com/foo.main")
rtInit := declareNoArgFunc(pkg, llssa.PkgRuntime+".init")
abiInit := declareNoArgFunc(pkg, "init$abitypes")

defineLibraryRuntimeInit(pkg, rtInit, abiInit, "")

ir := pkg.String()
assertInOrder(t, ir,
"call void @\"github.com/goplus/llgo/runtime/internal/runtime.init\"()",
"call void @\"init$abitypes\"()",
)
}

func assertInOrder(t *testing.T, s string, wants ...string) {
t.Helper()
offset := 0
Expand Down
26 changes: 26 additions & 0 deletions runtime/internal/lib/runtime/gettimeofday_linux_amd64_llgo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//go:build linux && amd64 && !baremetal

package runtime

import (
"unsafe"

c "github.com/goplus/llgo/runtime/internal/clite"
cliteos "github.com/goplus/llgo/runtime/internal/clite/os"
clitesyscall "github.com/goplus/llgo/runtime/internal/clite/syscall"
)

//go:linkname c_gettimeofday C.gettimeofday
func c_gettimeofday(tv *clitesyscall.Timeval, tz unsafe.Pointer) c.Int

// syscall.Gettimeofday and syscall.Time call an amd64 assembly helper. LLGo's
// replacement syscall package does not include that symbol, so forward it to
// libc while preserving syscall's errno result.
//
//go:linkname syscall_gettimeofday syscall.gettimeofday
func syscall_gettimeofday(tv *clitesyscall.Timeval) clitesyscall.Errno {
if c_gettimeofday(tv, nil) != 0 {
return clitesyscall.Errno(cliteos.Errno())
}
return 0
}
16 changes: 16 additions & 0 deletions runtime/internal/lib/runtime/os_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,27 @@ func sysctlbynameInt32(name []byte) (int32, int32) {
return ret, out
}

func sysctlbynameBytes(name, out []byte) int32 {
nout := uintptr(len(out))
return sysctlbyname(&name[0], &out[0], &nout, nil, 0)
}

//go:linkname internal_cpu_getsysctlbyname internal/cpu.getsysctlbyname
func internal_cpu_getsysctlbyname(name []byte) (int32, int32) {
return sysctlbynameInt32(name)
}

//go:linkname internal_cpu_sysctlbynameInt32 internal/cpu.sysctlbynameInt32
func internal_cpu_sysctlbynameInt32(name []byte) (int32, int32) {
return sysctlbynameInt32(name)
}

//go:linkname internal_cpu_sysctlbynameBytes internal/cpu.sysctlbynameBytes
func internal_cpu_sysctlbynameBytes(name, out []byte) int32 {
return sysctlbynameBytes(name, out)
}

//go:cgo_import_dynamic libc_sysctlbyname sysctlbyname "/usr/lib/libSystem.B.dylib"
var libc_sysctlbyname_trampoline_addr uintptr

// adapted from runtime/sys_darwin.go in the pattern of sysctl() above, as defined in x/sys/unix
Expand Down
25 changes: 25 additions & 0 deletions runtime/internal/lib/runtime/syscall_linux_llgo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//go:build linux

package runtime

import (
"unsafe"

clitesyscall "github.com/goplus/llgo/runtime/internal/clite/syscall"
)

// LLGo may run alongside threads created by C libraries, which the Go runtime
// cannot enumerate. Match Go's cgo behavior and report AllThreadsSyscall as
// unsupported rather than changing process credentials on only one thread.
//
//go:linkname syscall_runtime_doAllThreadsSyscall syscall.runtime_doAllThreadsSyscall
//go:uintptrescapes
func syscall_runtime_doAllThreadsSyscall(_, _, _, _, _, _, _ uintptr) (r1, r2, err uintptr) {
return ^uintptr(0), ^uintptr(0), uintptr(clitesyscall.ENOTSUP)
}

//go:linkname syscall_cgocaller syscall.cgocaller
//go:uintptrescapes
func syscall_cgocaller(_ unsafe.Pointer, _ ...uintptr) uintptr {
return uintptr(clitesyscall.ENOTSUP)
}
39 changes: 0 additions & 39 deletions runtime/internal/runtime/defer_tls_baremetal.go

This file was deleted.

Loading
Loading