diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 48c3aa5efd..7c364db46b 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -1766,7 +1766,7 @@ func buildModeSupported(compiler, buildmode, goos, goarch string) bool { case "c-shared": switch platform { - case "linux/amd64", "linux/arm", "linux/arm64", "linux/loong64", "linux/386", "linux/ppc64le", "linux/riscv64", "linux/s390x", + case "aix/ppc64", "linux/amd64", "linux/arm", "linux/arm64", "linux/loong64", "linux/386", "linux/ppc64le", "linux/riscv64", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "darwin/amd64", "darwin/arm64", diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go index 964d6e8363..a293f5a8bf 100644 --- a/src/cmd/go/internal/work/init.go +++ b/src/cmd/go/internal/work/init.go @@ -250,7 +250,7 @@ func buildModeInit() { codegenArg = "-fPIC" } else { switch cfg.Goos { - case "linux", "android", "freebsd": + case "linux", "android", "freebsd", "aix": codegenArg = "-shared" case "windows": // Do not add usual .exe suffix to the .dll file. diff --git a/src/cmd/internal/obj/ppc64/obj9.go b/src/cmd/internal/obj/ppc64/obj9.go index bae2965639..c98b95eaca 100644 --- a/src/cmd/internal/obj/ppc64/obj9.go +++ b/src/cmd/internal/obj/ppc64/obj9.go @@ -429,7 +429,9 @@ func (c *ctxt9) rewriteToUseTOC(p *obj.Prog) { } if source.Sym.Type == objabi.STLSBSS { - return + if !c.ctxt.Flag_shared { + return + } } // Retrieve or create the TOC anchor. @@ -438,7 +440,23 @@ func (c *ctxt9) rewriteToUseTOC(p *obj.Prog) { s.Set(obj.AttrDuplicateOK, true) s.Set(obj.AttrStatic, true) c.ctxt.Data = append(c.ctxt.Data, s) - s.WriteAddr(c.ctxt, 0, 8, source.Sym, 0) + + // 1. Safely allocate 8 bytes of zeros for the 64-bit TOC pointer + s.WriteInt(c.ctxt, 0, 8, 0) + + // 2. Determine the exact relocation type needed + relType := objabi.R_ADDR + if source.Sym.Type == objabi.STLSBSS && c.ctxt.Flag_shared { + relType = objabi.R_POWER_TLS_IE + } + + // 3. Add the precise relocation cleanly + s.AddRel(c.ctxt, obj.Reloc{ + Type: relType, + Off: 0, + Siz: 8, + Sym: source.Sym, + }) }) if source.Type == obj.TYPE_ADDR { @@ -448,8 +466,21 @@ func (c *ctxt9) rewriteToUseTOC(p *obj.Prog) { p.From.Sym = symtoc p.From.Name = obj.NAME_TOCREF + last := p + + // NEW: For TLS variables, we must add the thread pointer (R13) to the offset + // MOVD symtoc, Rx becomes MOVD symtoc, Rx; ADD R13, Rx + if source.Sym.Type == objabi.STLSBSS { + q1 := obj.Appendp(last, c.newprog) + q1.As = AADD + q1.From.Type = obj.TYPE_REG + q1.From.Reg = REG_R13 + q1.To = p.To + last = q1 + } + if p.From.Offset != 0 { - q := obj.Appendp(p, c.newprog) + q := obj.Appendp(last, c.newprog) q.As = AADD q.From.Type = obj.TYPE_CONST q.From.Offset = p.From.Offset @@ -472,23 +503,40 @@ func (c *ctxt9) rewriteToUseTOC(p *obj.Prog) { q.To.Type = obj.TYPE_REG q.To.Reg = REGTMP - q = obj.Appendp(q, c.newprog) - q.As = p.As - q.From = p.From - q.To = p.To - if p.From.Name != obj.NAME_NONE { - q.From.Type = obj.TYPE_MEM - q.From.Reg = REGTMP - q.From.Name = obj.NAME_NONE - q.From.Sym = nil - } else if p.To.Name != obj.NAME_NONE { - q.To.Type = obj.TYPE_MEM - q.To.Reg = REGTMP - q.To.Name = obj.NAME_NONE - q.To.Sym = nil - } else { - c.ctxt.Diag("unreachable case in rewriteToUseTOC with %v", p) - } + last := q + + if source.Sym.Type == objabi.STLSBSS { + // TLS variables need their ADDRESS loaded, not their value. + // First, point the TOC load directly to the final destination register. + q.To.Reg = p.To.Reg + + // Add R13 to complete the absolute memory address calculation. + q1 := obj.Appendp(last, c.newprog) + q1.As = AADD + q1.From.Type = obj.TYPE_REG + q1.From.Reg = REG_R13 + q1.To.Type = obj.TYPE_REG + q1.To.Reg = p.To.Reg + + // CRITICAL: We DO NOT append q2 (the memory dereference) for TLS. + } else { + // Standard global variables require the memory dereference. + q2 := obj.Appendp(last, c.newprog) + q2.As = p.As + q2.From = p.From + q2.To = p.To + if p.From.Name != obj.NAME_NONE { + q2.From.Type = obj.TYPE_MEM + q2.From.Reg = REGTMP + q2.From.Name = obj.NAME_NONE + q2.From.Sym = nil + } else if p.To.Name != obj.NAME_NONE { + q2.To.Type = obj.TYPE_MEM + q2.To.Reg = REGTMP + q2.To.Name = obj.NAME_NONE + q2.To.Sym = nil + } + } obj.Nopout(p) } @@ -859,18 +907,26 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q.As = AWORD q.Pos = p.Pos q.From.Type = obj.TYPE_CONST - q.From.Offset = 0x3c4c0000 + if c.ctxt.Headtype == objabi.Haix { + q.From.Offset = 0x60000000 + } else { + q.From.Offset = 0x3c4c0000 + } q = obj.Appendp(q, c.newprog) q.As = AWORD q.Pos = p.Pos q.From.Type = obj.TYPE_CONST - q.From.Offset = 0x38420000 - c.cursym.AddRel(c.ctxt, obj.Reloc{ - Type: objabi.R_ADDRPOWER_PCREL, - Off: 0, - Siz: 8, - Sym: c.ctxt.Lookup(".TOC."), - }) + if c.ctxt.Headtype == objabi.Haix { + q.From.Offset = 0x60000000 + } else { + q.From.Offset = 0x38420000 + c.cursym.AddRel(c.ctxt, obj.Reloc{ + Type: objabi.R_ADDRPOWER_PCREL, + Off: 0, + Siz: 8, + Sym: c.ctxt.Lookup(".TOC."), + }) + } } if !c.cursym.Func().Text.From.Sym.NoSplit() { diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index bcad5add4a..e606aa429e 100644 @@ -1647,6 +1647,8 @@ func (ctxt *Link) hostlink() { case BuildModeCShared: if ctxt.HeadType == objabi.Hdarwin { argv = append(argv, "-dynamiclib") + } else if ctxt.HeadType == objabi.Haix { + argv = append(argv, "-shared") } else { if ctxt.UseRelro() { argv = append(argv, "-Wl,-z,relro") @@ -1827,8 +1829,10 @@ func (ctxt *Link) hostlink() { if ctxt.HeadType == objabi.Haix { // We want to have C files after Go files to remove // trampolines csects made by ld. - argv = append(argv, "-nostartfiles") - argv = append(argv, "/lib/crt0_64.o") + if ctxt.BuildMode != BuildModeCShared { + argv = append(argv, "-nostartfiles") + argv = append(argv, "/lib/crt0_64.o") + } extld := ctxt.extld() name, args := extld[0], extld[1:] diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index 5ceab3aa5a..27fe513c96 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -893,6 +893,8 @@ func xcoffreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy case objabi.R_POWER_TLS_LE: // This only supports 16b relocations. It is fixed up in archreloc. emitReloc(ld.XCOFF_R_TLS_LE|0x0F<<8, 2) + case objabi.R_POWER_TLS_IE: + emitReloc(ld.XCOFF_R_TLS_IE|0x3F<<8, 0) case objabi.R_CALLPOWER: if r.Size != 4 { return false @@ -1387,18 +1389,20 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade nExtReloc = 1 return val, nExtReloc, true case objabi.R_POWER_TLS_LE, objabi.R_POWER_TLS_IE: - if target.IsAIX() && rt == objabi.R_POWER_TLS_LE { - // Fixup val, an addis/addi pair of instructions, which generate a 32b displacement - // from the threadpointer (R13), into a 16b relocation. XCOFF only supports 16b - // TLS LE relocations. Likewise, verify this is an addis/addi sequence. - const expectedOpcodes = 0x3C00000038000000 - const expectedOpmasks = 0xFC000000FC000000 - if uint64(val)&expectedOpmasks != expectedOpcodes { - ldr.Errorf(s, "relocation for %s+%d is not an addis/addi pair: %16x", ldr.SymName(rs), r.Off(), uint64(val)) + if target.IsAIX() { + if rt == objabi.R_POWER_TLS_LE { + // Fixup val, an addis/addi pair of instructions, which generate a 32b displacement + // from the threadpointer (R13), into a 16b relocation. XCOFF only supports 16b + // TLS LE relocations. Likewise, verify this is an addis/addi sequence. + const expectedOpcodes = 0x3C00000038000000 + const expectedOpmasks = 0xFC000000FC000000 + if uint64(val)&expectedOpmasks != expectedOpcodes { + ldr.Errorf(s, "relocation for %s+%d is not an addis/addi pair: %16x", ldr.SymName(rs), r.Off(), uint64(val)) + } + nval := (int64(uint32(0x380d0000)) | val&0x03e00000) << 32 // addi rX, r13, $0 + nval |= int64(OP_NOP) // nop + val = nval } - nval := (int64(uint32(0x380d0000)) | val&0x03e00000) << 32 // addi rX, r13, $0 - nval |= int64(OP_NOP) // nop - val = nval nExtReloc = 1 } else { nExtReloc = 2 diff --git a/src/internal/platform/supported.go b/src/internal/platform/supported.go index 778d727086..421e7cc2b3 100644 --- a/src/internal/platform/supported.go +++ b/src/internal/platform/supported.go @@ -164,7 +164,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "c-shared": switch platform { - case "linux/amd64", "linux/arm", "linux/arm64", "linux/loong64", "linux/386", "linux/ppc64le", "linux/riscv64", "linux/s390x", + case "aix/ppc64", "linux/amd64", "linux/arm", "linux/arm64", "linux/loong64", "linux/386", "linux/ppc64le", "linux/riscv64", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "darwin/amd64", "darwin/arm64", diff --git a/src/runtime/asm_ppc64x.s b/src/runtime/asm_ppc64x.s index ffc4b005cb..5d46c4a1b1 100644 --- a/src/runtime/asm_ppc64x.s +++ b/src/runtime/asm_ppc64x.s @@ -35,7 +35,11 @@ TEXT _rt0_ppc64x_lib(SB),NOSPLIT|NOFRAME,$0 // See runtime/cgo/gcc_aix_ppc64.c MOVBZ runtime·isarchive(SB), R3 // Check buildmode = c-archive CMP $0, R3 - BEQ done + BNE continue_init + MOVBZ runtime·islibrary(SB), R3 // Check buildmode = c-shared + CMP $0, R3 + BEQ done // If neither, exit +continue_init: #endif MOVD $runtime·libpreinit(SB), R12 @@ -73,11 +77,19 @@ done: RET #ifdef GO_PPC64X_HAS_FUNCDESC -DEFINE_PPC64X_FUNCDESC(_rt0_ppc64x_lib_go, __rt0_ppc64x_lib_go) +// For AIX, we need a proper function descriptor that pthread_create can call +// The descriptor must point to the actual code and include the correct TOC +DATA _rt0_ppc64x_lib_go+0(SB)/8, $__rt0_ppc64x_lib_go(SB) +DATA _rt0_ppc64x_lib_go+8(SB)/8, $TOC(SB) +DATA _rt0_ppc64x_lib_go+16(SB)/8, $0 +GLOBL _rt0_ppc64x_lib_go(SB), NOPTR, $24 + TEXT __rt0_ppc64x_lib_go(SB),NOSPLIT,$0 + XOR R0, R0 #else TEXT _rt0_ppc64x_lib_go(SB),NOSPLIT,$0 #endif + MOVD R2, 40(R1) MOVD _rt0_ppc64x_lib_argc<>(SB), R3 MOVD _rt0_ppc64x_lib_argv<>(SB), R4 MOVD $runtime·rt0_go(SB), R12 diff --git a/src/runtime/os2_aix.go b/src/runtime/os2_aix.go index 51758bd304..8ed7b649e3 100644 --- a/src/runtime/os2_aix.go +++ b/src/runtime/os2_aix.go @@ -170,6 +170,11 @@ type libFunc uintptr // It's defined in sys_aix_ppc64.go. var asmsyscall6 libFunc +// callCgoNoG is an assembly bridge that directly executes a C system call +// from a naked C thread when 'g' is nil, bypassing Go's stack-switching machinery. +//go:noescape +func callCgoNoG(fn *libFunc, a0, a1, a2, a3, a4, a5 uintptr) uintptr + // syscallX functions must always be called with g != nil and m != nil, // as it relies on g.m.libcall to pass arguments to asmcgocall. // The few cases where syscalls haven't a g or a m must call their equivalent @@ -179,6 +184,9 @@ var asmsyscall6 libFunc //go:nosplit func syscall0(fn *libFunc) (r, err uintptr) { gp := getg() + if gp == nil || gp.m == nil { + return callCgoNoG(fn, 0, 0, 0, 0, 0, 0), 0 + } mp := gp.m resetLibcall := true if mp.libcallsp == 0 { @@ -210,6 +218,9 @@ func syscall0(fn *libFunc) (r, err uintptr) { //go:nosplit func syscall1(fn *libFunc, a0 uintptr) (r, err uintptr) { gp := getg() + if gp == nil || gp.m == nil { + return callCgoNoG(fn, a0, 0, 0, 0, 0, 0), 0 + } mp := gp.m resetLibcall := true if mp.libcallsp == 0 { @@ -242,6 +253,9 @@ func syscall1(fn *libFunc, a0 uintptr) (r, err uintptr) { //go:cgo_unsafe_args func syscall2(fn *libFunc, a0, a1 uintptr) (r, err uintptr) { gp := getg() + if gp == nil || gp.m == nil { + return callCgoNoG(fn, a0, a1, 0, 0, 0, 0), 0 + } mp := gp.m resetLibcall := true if mp.libcallsp == 0 { @@ -274,6 +288,9 @@ func syscall2(fn *libFunc, a0, a1 uintptr) (r, err uintptr) { //go:cgo_unsafe_args func syscall3(fn *libFunc, a0, a1, a2 uintptr) (r, err uintptr) { gp := getg() + if gp == nil || gp.m == nil { + return callCgoNoG(fn, a0, a1, a2, 0, 0, 0), 0 + } mp := gp.m resetLibcall := true if mp.libcallsp == 0 { @@ -306,6 +323,9 @@ func syscall3(fn *libFunc, a0, a1, a2 uintptr) (r, err uintptr) { //go:cgo_unsafe_args func syscall4(fn *libFunc, a0, a1, a2, a3 uintptr) (r, err uintptr) { gp := getg() + if gp == nil || gp.m == nil { + return callCgoNoG(fn, a0, a1, a2, a3, 0, 0), 0 + } mp := gp.m resetLibcall := true if mp.libcallsp == 0 { @@ -338,6 +358,9 @@ func syscall4(fn *libFunc, a0, a1, a2, a3 uintptr) (r, err uintptr) { //go:cgo_unsafe_args func syscall5(fn *libFunc, a0, a1, a2, a3, a4 uintptr) (r, err uintptr) { gp := getg() + if gp == nil || gp.m == nil { + return callCgoNoG(fn, a0, a1, a2, a3, a4, 0), 0 + } mp := gp.m resetLibcall := true if mp.libcallsp == 0 { @@ -370,6 +393,9 @@ func syscall5(fn *libFunc, a0, a1, a2, a3, a4 uintptr) (r, err uintptr) { //go:cgo_unsafe_args func syscall6(fn *libFunc, a0, a1, a2, a3, a4, a5 uintptr) (r, err uintptr) { gp := getg() + if gp == nil || gp.m == nil { + return callCgoNoG(fn, a0, a1, a2, a3, a4, a5), 0 + } mp := gp.m resetLibcall := true if mp.libcallsp == 0 { diff --git a/src/runtime/rt0_aix_ppc64.s b/src/runtime/rt0_aix_ppc64.s index 518fcb3b88..459eda23b9 100644 --- a/src/runtime/rt0_aix_ppc64.s +++ b/src/runtime/rt0_aix_ppc64.s @@ -41,7 +41,12 @@ TEXT _main(SB),NOSPLIT,$-8 MOVD R12, CTR BR (CTR) -TEXT _rt0_ppc64_aix_lib(SB),NOSPLIT,$0 +// NEW: Synthesize a 24-byte Function Descriptor in the .data section +// so the AIX OS loader can safely call this library entry point. +DEFINE_PPC64X_FUNCDESC(_rt0_ppc64_aix_lib, _rt0_ppc64_aix_lib_code<>) + +// RENAME the actual code to a private symbol using <> +TEXT _rt0_ppc64_aix_lib_code<>(SB),NOSPLIT,$0 MOVD R14, R3 // argc MOVD R15, R4 // argv JMP _rt0_ppc64x_lib(SB) diff --git a/src/runtime/sys_aix_ppc64.s b/src/runtime/sys_aix_ppc64.s index a0ef7e111a..2b2b4090c4 100644 --- a/src/runtime/sys_aix_ppc64.s +++ b/src/runtime/sys_aix_ppc64.s @@ -17,6 +17,7 @@ TEXT callCfunction<>(SB), NOSPLIT|NOFRAME,$0 MOVD R2, 40(R1) MOVD 0(R12), R0 MOVD 8(R12), R2 + MOVD 16(R12), R11 MOVD R0, CTR BR (CTR) @@ -316,3 +317,28 @@ TEXT runtime·sigaction1(SB),NOSPLIT,$0-24 MOVD $libc_sigaction(SB), R12 CSYSCALL() RET + +// callCgoNoG bypasses the stack-switch for naked C threads (Thread 1) +TEXT runtime·callCgoNoG(SB),NOSPLIT,$256-64 + MOVD fn+0(FP), R12 // R12 = pointer to Go's libFunc struct + MOVD a0+8(FP), R3 + MOVD a1+16(FP), R4 + MOVD a2+24(FP), R5 + MOVD a3+32(FP), R6 + MOVD a4+40(FP), R7 + MOVD a5+48(FP), R8 + + // Unwrap the Go struct to get the actual OS Descriptor + MOVD 0(R12), R12 + + MOVD R2, 40(R1) // Save Go's TOC in standard ABI slot + MOVD 0(R12), R0 // C Code Pointer + MOVD 8(R12), R2 // C TOC Pointer + MOVD 16(R12), R11 // C Environment Pointer (PLT) + + MOVD R0, CTR + BL (CTR) // Execute C function + + MOVD 40(R1), R2 // Restore Go's TOC + MOVD R3, ret+56(FP) // Return value + RET