github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/obj/plist.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package obj 6 7 import ( 8 "fmt" 9 "strings" 10 11 "github.com/go-asm/go/abi" 12 "github.com/go-asm/go/cmd/objabi" 13 "github.com/go-asm/go/cmd/src" 14 ) 15 16 type Plist struct { 17 Firstpc *Prog 18 Curfn Func 19 } 20 21 // ProgAlloc is a function that allocates Progs. 22 // It is used to provide access to cached/bulk-allocated Progs to the assemblers. 23 type ProgAlloc func() *Prog 24 25 func Flushplist(ctxt *Link, plist *Plist, newprog ProgAlloc) { 26 if ctxt.Pkgpath == "" { 27 panic("Flushplist called without Pkgpath") 28 } 29 30 // Build list of symbols, and assign instructions to lists. 31 var curtext *LSym 32 var etext *Prog 33 var text []*LSym 34 35 var plink *Prog 36 for p := plist.Firstpc; p != nil; p = plink { 37 if ctxt.Debugasm > 0 && ctxt.Debugvlog { 38 fmt.Printf("obj: %v\n", p) 39 } 40 plink = p.Link 41 p.Link = nil 42 43 switch p.As { 44 case AEND: 45 continue 46 47 case ATEXT: 48 s := p.From.Sym 49 if s == nil { 50 // func _() { } 51 curtext = nil 52 continue 53 } 54 text = append(text, s) 55 etext = p 56 curtext = s 57 continue 58 59 case AFUNCDATA: 60 // Rewrite reference to go_args_stackmap(SB) to the Go-provided declaration information. 61 if curtext == nil { // func _() {} 62 continue 63 } 64 switch p.To.Sym.Name { 65 case "go_args_stackmap": 66 if p.From.Type != TYPE_CONST || p.From.Offset != abi.FUNCDATA_ArgsPointerMaps { 67 ctxt.Diag("%s: FUNCDATA use of go_args_stackmap(SB) without FUNCDATA_ArgsPointerMaps", p.Pos) 68 } 69 p.To.Sym = ctxt.LookupDerived(curtext, curtext.Name+".args_stackmap") 70 case "no_pointers_stackmap": 71 if p.From.Type != TYPE_CONST || p.From.Offset != abi.FUNCDATA_LocalsPointerMaps { 72 ctxt.Diag("%s: FUNCDATA use of no_pointers_stackmap(SB) without FUNCDATA_LocalsPointerMaps", p.Pos) 73 } 74 // funcdata for functions with no local variables in frame. 75 // Define two zero-length bitmaps, because the same index is used 76 // for the local variables as for the argument frame, and assembly 77 // frames have two argument bitmaps, one without results and one with results. 78 // Write []uint32{2, 0}. 79 b := make([]byte, 8) 80 ctxt.Arch.ByteOrder.PutUint32(b, 2) 81 s := ctxt.GCLocalsSym(b) 82 if !s.OnList() { 83 ctxt.Globl(s, int64(len(s.P)), int(RODATA|DUPOK)) 84 } 85 p.To.Sym = s 86 } 87 88 } 89 90 if curtext == nil { 91 etext = nil 92 continue 93 } 94 etext.Link = p 95 etext = p 96 } 97 98 if newprog == nil { 99 newprog = ctxt.NewProg 100 } 101 102 // Add reference to Go arguments for assembly functions without them. 103 if ctxt.IsAsm { 104 pkgPrefix := objabi.PathToPrefix(ctxt.Pkgpath) + "." 105 for _, s := range text { 106 if !strings.HasPrefix(s.Name, pkgPrefix) { 107 continue 108 } 109 // The current args_stackmap generation in the compiler assumes 110 // that the function in question is ABI0, so avoid introducing 111 // an args_stackmap reference if the func is not ABI0 (better to 112 // have no stackmap than an incorrect/lying stackmap). 113 if s.ABI() != ABI0 { 114 continue 115 } 116 // runtime.addmoduledata is a host ABI function, so it doesn't 117 // need FUNCDATA anyway. Moreover, cmd/link has special logic 118 // for linking it in eccentric build modes, which breaks if it 119 // has FUNCDATA references (e.g., github.com/go-asm/go/cmd/cgo/testplugin). 120 // 121 // TODO(cherryyz): Fix cmd/link's handling of plugins (see 122 // discussion on CL 523355). 123 if s.Name == "runtime.addmoduledata" { 124 continue 125 } 126 foundArgMap, foundArgInfo := false, false 127 for p := s.Func().Text; p != nil; p = p.Link { 128 if p.As == AFUNCDATA && p.From.Type == TYPE_CONST { 129 if p.From.Offset == abi.FUNCDATA_ArgsPointerMaps { 130 foundArgMap = true 131 } 132 if p.From.Offset == abi.FUNCDATA_ArgInfo { 133 foundArgInfo = true 134 } 135 if foundArgMap && foundArgInfo { 136 break 137 } 138 } 139 } 140 if !foundArgMap { 141 p := Appendp(s.Func().Text, newprog) 142 p.As = AFUNCDATA 143 p.From.Type = TYPE_CONST 144 p.From.Offset = abi.FUNCDATA_ArgsPointerMaps 145 p.To.Type = TYPE_MEM 146 p.To.Name = NAME_EXTERN 147 p.To.Sym = ctxt.LookupDerived(s, s.Name+".args_stackmap") 148 } 149 if !foundArgInfo { 150 p := Appendp(s.Func().Text, newprog) 151 p.As = AFUNCDATA 152 p.From.Type = TYPE_CONST 153 p.From.Offset = abi.FUNCDATA_ArgInfo 154 p.To.Type = TYPE_MEM 155 p.To.Name = NAME_EXTERN 156 p.To.Sym = ctxt.LookupDerived(s, fmt.Sprintf("%s.arginfo%d", s.Name, s.ABI())) 157 } 158 } 159 } 160 161 // Turn functions into machine code images. 162 for _, s := range text { 163 mkfwd(s) 164 if ctxt.Arch.ErrorCheck != nil { 165 ctxt.Arch.ErrorCheck(ctxt, s) 166 } 167 linkpatch(ctxt, s, newprog) 168 ctxt.Arch.Preprocess(ctxt, s, newprog) 169 ctxt.Arch.Assemble(ctxt, s, newprog) 170 if ctxt.Errors > 0 { 171 continue 172 } 173 linkpcln(ctxt, s) 174 ctxt.populateDWARF(plist.Curfn, s) 175 if ctxt.Headtype == objabi.Hwindows && ctxt.Arch.SEH != nil { 176 s.Func().sehUnwindInfoSym = ctxt.Arch.SEH(ctxt, s) 177 } 178 } 179 } 180 181 func (ctxt *Link) InitTextSym(s *LSym, flag int, start src.XPos) { 182 if s == nil { 183 // func _() { } 184 return 185 } 186 if s.Func() != nil { 187 ctxt.Diag("%s: symbol %s redeclared\n\t%s: other declaration of symbol %s", ctxt.PosTable.Pos(start), s.Name, ctxt.PosTable.Pos(s.Func().Text.Pos), s.Name) 188 return 189 } 190 s.NewFuncInfo() 191 if s.OnList() { 192 ctxt.Diag("%s: symbol %s redeclared", ctxt.PosTable.Pos(start), s.Name) 193 return 194 } 195 if strings.HasPrefix(s.Name, `"".`) { 196 ctxt.Diag("%s: unqualified symbol name: %s", ctxt.PosTable.Pos(start), s.Name) 197 } 198 199 // startLine should be the same line number that would be displayed via 200 // pcln, etc for the declaration (i.e., relative line number, as 201 // adjusted by //line). 202 _, startLine := ctxt.getFileIndexAndLine(start) 203 204 s.Func().FuncID = objabi.GetFuncID(s.Name, flag&WRAPPER != 0 || flag&ABIWRAPPER != 0) 205 s.Func().FuncFlag = ctxt.toFuncFlag(flag) 206 s.Func().StartLine = startLine 207 s.Set(AttrOnList, true) 208 s.Set(AttrDuplicateOK, flag&DUPOK != 0) 209 s.Set(AttrNoSplit, flag&NOSPLIT != 0) 210 s.Set(AttrReflectMethod, flag&REFLECTMETHOD != 0) 211 s.Set(AttrWrapper, flag&WRAPPER != 0) 212 s.Set(AttrABIWrapper, flag&ABIWRAPPER != 0) 213 s.Set(AttrNeedCtxt, flag&NEEDCTXT != 0) 214 s.Set(AttrNoFrame, flag&NOFRAME != 0) 215 s.Set(AttrPkgInit, flag&PKGINIT != 0) 216 s.Type = objabi.STEXT 217 ctxt.Text = append(ctxt.Text, s) 218 219 // Set up DWARF entries for s 220 ctxt.dwarfSym(s) 221 } 222 223 func (ctxt *Link) toFuncFlag(flag int) abi.FuncFlag { 224 var out abi.FuncFlag 225 if flag&TOPFRAME != 0 { 226 out |= abi.FuncFlagTopFrame 227 } 228 if ctxt.IsAsm { 229 out |= abi.FuncFlagAsm 230 } 231 return out 232 } 233 234 func (ctxt *Link) Globl(s *LSym, size int64, flag int) { 235 ctxt.GloblPos(s, size, flag, src.NoXPos) 236 } 237 func (ctxt *Link) GloblPos(s *LSym, size int64, flag int, pos src.XPos) { 238 if s.OnList() { 239 // TODO: print where the first declaration was. 240 ctxt.Diag("%s: symbol %s redeclared", ctxt.PosTable.Pos(pos), s.Name) 241 } 242 s.Set(AttrOnList, true) 243 ctxt.Data = append(ctxt.Data, s) 244 s.Size = size 245 if s.Type == 0 { 246 s.Type = objabi.SBSS 247 } 248 if flag&DUPOK != 0 { 249 s.Set(AttrDuplicateOK, true) 250 } 251 if flag&RODATA != 0 { 252 s.Type = objabi.SRODATA 253 } else if flag&NOPTR != 0 { 254 if s.Type == objabi.SDATA { 255 s.Type = objabi.SNOPTRDATA 256 } else { 257 s.Type = objabi.SNOPTRBSS 258 } 259 } else if flag&TLSBSS != 0 { 260 s.Type = objabi.STLSBSS 261 } 262 } 263 264 // EmitEntryLiveness generates PCDATA Progs after p to switch to the 265 // liveness map active at the entry of function s. It returns the last 266 // Prog generated. 267 func (ctxt *Link) EmitEntryLiveness(s *LSym, p *Prog, newprog ProgAlloc) *Prog { 268 pcdata := ctxt.EmitEntryStackMap(s, p, newprog) 269 pcdata = ctxt.EmitEntryUnsafePoint(s, pcdata, newprog) 270 return pcdata 271 } 272 273 // Similar to EmitEntryLiveness, but just emit stack map. 274 func (ctxt *Link) EmitEntryStackMap(s *LSym, p *Prog, newprog ProgAlloc) *Prog { 275 pcdata := Appendp(p, newprog) 276 pcdata.Pos = s.Func().Text.Pos 277 pcdata.As = APCDATA 278 pcdata.From.Type = TYPE_CONST 279 pcdata.From.Offset = abi.PCDATA_StackMapIndex 280 pcdata.To.Type = TYPE_CONST 281 pcdata.To.Offset = -1 // pcdata starts at -1 at function entry 282 283 return pcdata 284 } 285 286 // Similar to EmitEntryLiveness, but just emit unsafe point map. 287 func (ctxt *Link) EmitEntryUnsafePoint(s *LSym, p *Prog, newprog ProgAlloc) *Prog { 288 pcdata := Appendp(p, newprog) 289 pcdata.Pos = s.Func().Text.Pos 290 pcdata.As = APCDATA 291 pcdata.From.Type = TYPE_CONST 292 pcdata.From.Offset = abi.PCDATA_UnsafePoint 293 pcdata.To.Type = TYPE_CONST 294 pcdata.To.Offset = -1 295 296 return pcdata 297 } 298 299 // StartUnsafePoint generates PCDATA Progs after p to mark the 300 // beginning of an unsafe point. The unsafe point starts immediately 301 // after p. 302 // It returns the last Prog generated. 303 func (ctxt *Link) StartUnsafePoint(p *Prog, newprog ProgAlloc) *Prog { 304 pcdata := Appendp(p, newprog) 305 pcdata.As = APCDATA 306 pcdata.From.Type = TYPE_CONST 307 pcdata.From.Offset = abi.PCDATA_UnsafePoint 308 pcdata.To.Type = TYPE_CONST 309 pcdata.To.Offset = abi.UnsafePointUnsafe 310 311 return pcdata 312 } 313 314 // EndUnsafePoint generates PCDATA Progs after p to mark the end of an 315 // unsafe point, restoring the register map index to oldval. 316 // The unsafe point ends right after p. 317 // It returns the last Prog generated. 318 func (ctxt *Link) EndUnsafePoint(p *Prog, newprog ProgAlloc, oldval int64) *Prog { 319 pcdata := Appendp(p, newprog) 320 pcdata.As = APCDATA 321 pcdata.From.Type = TYPE_CONST 322 pcdata.From.Offset = abi.PCDATA_UnsafePoint 323 pcdata.To.Type = TYPE_CONST 324 pcdata.To.Offset = oldval 325 326 return pcdata 327 } 328 329 // MarkUnsafePoints inserts PCDATAs to mark nonpreemptible and restartable 330 // instruction sequences, based on isUnsafePoint and isRestartable predicate. 331 // p0 is the start of the instruction stream. 332 // isUnsafePoint(p) returns true if p is not safe for async preemption. 333 // isRestartable(p) returns true if we can restart at the start of p (this Prog) 334 // upon async preemption. (Currently multi-Prog restartable sequence is not 335 // supported.) 336 // isRestartable can be nil. In this case it is treated as always returning false. 337 // If isUnsafePoint(p) and isRestartable(p) are both true, it is treated as 338 // an unsafe point. 339 func MarkUnsafePoints(ctxt *Link, p0 *Prog, newprog ProgAlloc, isUnsafePoint, isRestartable func(*Prog) bool) { 340 if isRestartable == nil { 341 // Default implementation: nothing is restartable. 342 isRestartable = func(*Prog) bool { return false } 343 } 344 prev := p0 345 prevPcdata := int64(-1) // entry PC data value 346 prevRestart := int64(0) 347 for p := prev.Link; p != nil; p, prev = p.Link, p { 348 if p.As == APCDATA && p.From.Offset == abi.PCDATA_UnsafePoint { 349 prevPcdata = p.To.Offset 350 continue 351 } 352 if prevPcdata == abi.UnsafePointUnsafe { 353 continue // already unsafe 354 } 355 if isUnsafePoint(p) { 356 q := ctxt.StartUnsafePoint(prev, newprog) 357 q.Pc = p.Pc 358 q.Link = p 359 // Advance to the end of unsafe point. 360 for p.Link != nil && isUnsafePoint(p.Link) { 361 p = p.Link 362 } 363 if p.Link == nil { 364 break // Reached the end, don't bother marking the end 365 } 366 p = ctxt.EndUnsafePoint(p, newprog, prevPcdata) 367 p.Pc = p.Link.Pc 368 continue 369 } 370 if isRestartable(p) { 371 val := int64(abi.UnsafePointRestart1) 372 if val == prevRestart { 373 val = abi.UnsafePointRestart2 374 } 375 prevRestart = val 376 q := Appendp(prev, newprog) 377 q.As = APCDATA 378 q.From.Type = TYPE_CONST 379 q.From.Offset = abi.PCDATA_UnsafePoint 380 q.To.Type = TYPE_CONST 381 q.To.Offset = val 382 q.Pc = p.Pc 383 q.Link = p 384 385 if p.Link == nil { 386 break // Reached the end, don't bother marking the end 387 } 388 if isRestartable(p.Link) { 389 // Next Prog is also restartable. No need to mark the end 390 // of this sequence. We'll just go ahead mark the next one. 391 continue 392 } 393 p = Appendp(p, newprog) 394 p.As = APCDATA 395 p.From.Type = TYPE_CONST 396 p.From.Offset = abi.PCDATA_UnsafePoint 397 p.To.Type = TYPE_CONST 398 p.To.Offset = prevPcdata 399 p.Pc = p.Link.Pc 400 } 401 } 402 }