github.com/primecitizens/pcz/std@v0.2.1/rt0/rt0.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright 2023 The Prime Citizens 3 4 package rt0 5 6 import ( 7 "unsafe" 8 _ "unsafe" // for go:linkname 9 10 "github.com/primecitizens/pcz/std/algo/rand/fastrand" 11 stdgo "github.com/primecitizens/pcz/std/builtin/go" 12 stdtype "github.com/primecitizens/pcz/std/builtin/type" 13 "github.com/primecitizens/pcz/std/core/abi" 14 "github.com/primecitizens/pcz/std/core/alloc" 15 "github.com/primecitizens/pcz/std/core/arch" 16 "github.com/primecitizens/pcz/std/core/assert" 17 "github.com/primecitizens/pcz/std/core/cpu" 18 "github.com/primecitizens/pcz/std/core/stack" 19 "github.com/primecitizens/pcz/std/core/thread" 20 ) 21 22 var ( 23 g0 rt0G 24 _g0iface stdgo.G = &g0 25 26 // mainG points to the global G that is used by: 27 // - the main thread (goroutine in this std) 28 // - ffi callback 29 // 30 // It points to a global rt0G by default. 31 // 32 //go:linkname mainG 33 mainG *stdgo.GHead = &g0.head 34 35 //go:linkname runtime_inittasks runtime.runtime_inittasks 36 runtime_inittasks []*abi.InitTask 37 38 // for windows, it's actually []*uint16, see ../sys/sys_windows.go 39 // 40 //go:linkname args 41 args []*byte 42 43 stacksize uintptr 44 ) 45 46 const ( 47 offsetsOK = unsafe.Offsetof(rt0G{}.head) == 0 && 48 unsafe.Offsetof(rt0G{}.head.Stack) == 0 && 49 unsafe.Offsetof(rt0G{}.head.Stack.Lo) == 0 && 50 unsafe.Offsetof(rt0G{}.head.Stack.Hi) == arch.PtrSize 51 ) 52 53 // rt0G is the default G implementation used for early initialization of the 54 // program. 55 type rt0G struct { 56 head stdgo.GHead 57 frand fastrand.Source 58 } 59 60 //go:nosplit 61 func (gp *rt0G) ID() uint64 { return 0 } 62 63 //go:nosplit 64 func (gp *rt0G) Rand32() uint32 { return gp.frand.Rand32() } 65 66 //go:nosplit 67 func (gp *rt0G) Rand64() uint64 { return gp.frand.Rand64() } 68 69 //go:nosplit 70 func (gp *rt0G) Status() uint32 { return stdgo.StatusGrunning } 71 72 //go:nosplit 73 func (gp *rt0G) DefaultAlloc() alloc.M { return malloc() } 74 75 //go:nosplit 76 func (gp *rt0G) PersistantAlloc() alloc.P { return palloc() } 77 78 // rt0 starts the program, this function should be considered as the mstart1 79 // in the official go runtime. 80 // 81 //go:nosplit 82 //go:noinline 83 func rt0(argc int32, argv **byte) { 84 if !offsetsOK { 85 assert.Throw("invalid", "offsets", "in", "rt0G") 86 } 87 88 if argc > 0 { 89 args = unsafe.Slice(argv, argc) 90 } 91 92 // TODO: get actual size of the process stack 93 // 94 // - linux/darwin: check RLIMIT_STACK 95 if arch.IsWasm == 1 { 96 stacksize = 8192 97 } else { 98 stacksize = 4 * 1024 * 1024 99 } 100 101 mainG.Stack.Hi = stack.GetSP() 102 mainG.Stack.Lo = mainG.Stack.Hi - stacksize 103 mainG.Stackguard0 = mainG.Stack.Lo + stack.StackGuard 104 mainG.Stackguard1 = mainG.Stackguard0 105 106 // NOTE: code next to this comment is critical for dwarf generation 107 // it preserves relocation symbol `type:uintptr` for symbol `go:info.uintptr` 108 // 109 // It prevents the go tool link from throwing error: 110 // 111 // go:info.uintptr: unreachable sym in relocation: type:uintptr 112 // 113 // go linker doesn't mark `type:uintptr` reachable during deadcode analysis if it's 114 // not converted to an interface type, and that's the case for this runtime 115 mainG.Itab = stdtype.IfaceOf(&_g0iface).Itab 116 117 thread.SetG(mainG) 118 119 if !preinit() { 120 cpu.Initialize("") 121 122 for _, tsk := range runtime_inittasks { 123 doInit(tsk) 124 } 125 126 for i, iter := 0, abi.ModuleIter(0); ; i++ { 127 md, ok := iter.Nth(i) 128 if !ok { 129 break 130 } 131 132 for _, tsk := range md.InitTasks { 133 doInit(tsk) 134 } 135 } 136 } 137 138 // verify g works 139 if thread.G().G().Status() != stdgo.StatusGrunning { 140 assert.Throw("goroutine", "not", "working", "properly") 141 } 142 143 main() 144 145 exit0() 146 } 147 148 // `main.main` is the symbol of the main function from the main package 149 // created by the linker. 150 // 151 //go:linkname main main.main 152 func main() 153 154 // TODO(toolchain): create symbol main.preinit for tasks before doInit 155 func preinit() (skip bool) { 156 return false 157 } 158 159 // doInit runs all init() functions in the tsk. 160 // 161 //go:nosplit 162 func doInit(tsk *abi.InitTask) { 163 switch tsk.State { 164 case 2: // fully initialized 165 return 166 case 1: // initialization in progress 167 assert.Throw("recursive", "call", "during", "initialization", "-", "linker", "skew") 168 } 169 170 // not initialized yet 171 172 tsk.State = 1 // mark in progress 173 174 if tsk.NFns == 0 { 175 // go toolchain should have pruned all of these in the linker. 176 assert.Throw("inittask", "with", "no", "functions") 177 } 178 179 for i := uint32(0); i < tsk.NFns; i++ { 180 tsk.Nth(i)() 181 } 182 183 tsk.State = 2 // mark done 184 }