github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/src/cmd/link/internal/wasm/asm.go (about) 1 // Copyright 2018 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 wasm 6 7 import ( 8 "bytes" 9 "cmd/internal/objabi" 10 "cmd/link/internal/ld" 11 "cmd/link/internal/sym" 12 "io" 13 "regexp" 14 "runtime" 15 ) 16 17 const ( 18 I32 = 0x7F 19 I64 = 0x7E 20 F32 = 0x7D 21 F64 = 0x7C 22 ) 23 24 const ( 25 sectionCustom = 0 26 sectionType = 1 27 sectionImport = 2 28 sectionFunction = 3 29 sectionTable = 4 30 sectionMemory = 5 31 sectionGlobal = 6 32 sectionExport = 7 33 sectionStart = 8 34 sectionElement = 9 35 sectionCode = 10 36 sectionData = 11 37 ) 38 39 // funcValueOffset is the offset between the PC_F value of a function and the index of the function in WebAssembly 40 const funcValueOffset = 0x1000 // TODO(neelance): make function addresses play nice with heap addresses 41 42 func gentext(ctxt *ld.Link) { 43 } 44 45 type wasmFunc struct { 46 Name string 47 Type uint32 48 Code []byte 49 } 50 51 type wasmFuncType struct { 52 Params []byte 53 Results []byte 54 } 55 56 var wasmFuncTypes = map[string]*wasmFuncType{ 57 "_rt0_wasm_js": &wasmFuncType{Params: []byte{}}, // 58 "wasm_export_run": &wasmFuncType{Params: []byte{I32, I32}}, // argc, argv 59 "wasm_export_resume": &wasmFuncType{Params: []byte{}}, // 60 "wasm_export_getsp": &wasmFuncType{Results: []byte{I32}}, // sp 61 "wasm_pc_f_loop": &wasmFuncType{Params: []byte{}}, // 62 "runtime.wasmMove": &wasmFuncType{Params: []byte{I32, I32, I32}}, // dst, src, len 63 "runtime.wasmZero": &wasmFuncType{Params: []byte{I32, I32}}, // ptr, len 64 "runtime.wasmDiv": &wasmFuncType{Params: []byte{I64, I64}, Results: []byte{I64}}, // x, y -> x/y 65 "runtime.wasmTruncS": &wasmFuncType{Params: []byte{F64}, Results: []byte{I64}}, // x -> int(x) 66 "runtime.wasmTruncU": &wasmFuncType{Params: []byte{F64}, Results: []byte{I64}}, // x -> uint(x) 67 "runtime.gcWriteBarrier": &wasmFuncType{Params: []byte{I64, I64}}, // ptr, val 68 "cmpbody": &wasmFuncType{Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}}, // a, alen, b, blen -> -1/0/1 69 "memeqbody": &wasmFuncType{Params: []byte{I64, I64, I64}, Results: []byte{I64}}, // a, b, len -> 0/1 70 "memcmp": &wasmFuncType{Params: []byte{I32, I32, I32}, Results: []byte{I32}}, // a, b, len -> <0/0/>0 71 "memchr": &wasmFuncType{Params: []byte{I32, I32, I32}, Results: []byte{I32}}, // s, c, len -> index 72 } 73 74 func assignAddress(ctxt *ld.Link, sect *sym.Section, n int, s *sym.Symbol, va uint64, isTramp bool) (*sym.Section, int, uint64) { 75 // WebAssembly functions do not live in the same address space as the linear memory. 76 // Instead, WebAssembly automatically assigns indices. Imported functions (section "import") 77 // have indices 0 to n. They are followed by native functions (sections "function" and "code") 78 // with indices n+1 and following. 79 // 80 // The following rules describe how wasm handles function indices and addresses: 81 // PC_F = funcValueOffset + WebAssembly function index (not including the imports) 82 // s.Value = PC = PC_F<<16 + PC_B 83 // 84 // The funcValueOffset is necessary to avoid conflicts with expectations 85 // that the Go runtime has about function addresses. 86 // The field "s.Value" corresponds to the concept of PC at runtime. 87 // However, there is no PC register, only PC_F and PC_B. PC_F denotes the function, 88 // PC_B the resume point inside of that function. The entry of the function has PC_B = 0. 89 s.Sect = sect 90 s.Value = int64(funcValueOffset+va/ld.MINFUNC) << 16 // va starts at zero 91 va += uint64(ld.MINFUNC) 92 return sect, n, va 93 } 94 95 // asmb writes the final WebAssembly module binary. 96 // Spec: https://webassembly.github.io/spec/core/binary/modules.html 97 func asmb(ctxt *ld.Link) { 98 if ctxt.Debugvlog != 0 { 99 ctxt.Logf("%5.2f asmb\n", ld.Cputime()) 100 } 101 102 types := []*wasmFuncType{ 103 // For normal Go functions the return value is 104 // 0 if the function returned normally or 105 // 1 if the stack needs to be unwound. 106 &wasmFuncType{Results: []byte{I32}}, 107 } 108 109 // collect host imports (functions that get imported from the WebAssembly host, usually JavaScript) 110 hostImports := []*wasmFunc{ 111 &wasmFunc{ 112 Name: "debug", 113 Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types), 114 }, 115 } 116 hostImportMap := make(map[*sym.Symbol]int64) 117 for _, fn := range ctxt.Textp { 118 for _, r := range fn.R { 119 if r.Type == objabi.R_WASMIMPORT { 120 hostImportMap[r.Sym] = int64(len(hostImports)) 121 hostImports = append(hostImports, &wasmFunc{ 122 Name: r.Sym.Name, 123 Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types), 124 }) 125 } 126 } 127 } 128 129 // collect functions with WebAssembly body 130 var buildid []byte 131 fns := make([]*wasmFunc, len(ctxt.Textp)) 132 for i, fn := range ctxt.Textp { 133 wfn := new(bytes.Buffer) 134 if fn.Name == "go.buildid" { 135 writeUleb128(wfn, 0) // number of sets of locals 136 writeI32Const(wfn, 0) 137 wfn.WriteByte(0x0b) // end 138 buildid = fn.P 139 } else { 140 // Relocations have variable length, handle them here. 141 off := int32(0) 142 for _, r := range fn.R { 143 wfn.Write(fn.P[off:r.Off]) 144 off = r.Off 145 switch r.Type { 146 case objabi.R_ADDR: 147 writeSleb128(wfn, r.Sym.Value+r.Add) 148 case objabi.R_CALL: 149 writeSleb128(wfn, int64(len(hostImports))+r.Sym.Value>>16-funcValueOffset) 150 case objabi.R_WASMIMPORT: 151 writeSleb128(wfn, hostImportMap[r.Sym]) 152 default: 153 ld.Errorf(fn, "bad reloc type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type)) 154 continue 155 } 156 } 157 wfn.Write(fn.P[off:]) 158 } 159 160 typ := uint32(0) 161 if sig, ok := wasmFuncTypes[fn.Name]; ok { 162 typ = lookupType(sig, &types) 163 } 164 165 name := nameRegexp.ReplaceAllString(fn.Name, "_") 166 fns[i] = &wasmFunc{Name: name, Type: typ, Code: wfn.Bytes()} 167 } 168 169 ctxt.Out.Write([]byte{0x00, 0x61, 0x73, 0x6d}) // magic 170 ctxt.Out.Write([]byte{0x01, 0x00, 0x00, 0x00}) // version 171 172 // Add any buildid early in the binary: 173 if len(buildid) != 0 { 174 writeBuildID(ctxt, buildid) 175 } 176 177 writeGoVersion(ctxt) 178 writeTypeSec(ctxt, types) 179 writeImportSec(ctxt, hostImports) 180 writeFunctionSec(ctxt, fns) 181 writeTableSec(ctxt, fns) 182 writeMemorySec(ctxt) 183 writeGlobalSec(ctxt) 184 writeExportSec(ctxt, len(hostImports)) 185 writeElementSec(ctxt, uint64(len(hostImports)), uint64(len(fns))) 186 writeCodeSec(ctxt, fns) 187 writeDataSec(ctxt) 188 if !*ld.FlagS { 189 writeNameSec(ctxt, len(hostImports), fns) 190 } 191 192 ctxt.Out.Flush() 193 } 194 195 func lookupType(sig *wasmFuncType, types *[]*wasmFuncType) uint32 { 196 for i, t := range *types { 197 if bytes.Equal(sig.Params, t.Params) && bytes.Equal(sig.Results, t.Results) { 198 return uint32(i) 199 } 200 } 201 *types = append(*types, sig) 202 return uint32(len(*types) - 1) 203 } 204 205 func writeSecHeader(ctxt *ld.Link, id uint8) int64 { 206 ctxt.Out.WriteByte(id) 207 sizeOffset := ctxt.Out.Offset() 208 ctxt.Out.Write(make([]byte, 5)) // placeholder for length 209 return sizeOffset 210 } 211 212 func writeSecSize(ctxt *ld.Link, sizeOffset int64) { 213 endOffset := ctxt.Out.Offset() 214 ctxt.Out.SeekSet(sizeOffset) 215 writeUleb128FixedLength(ctxt.Out, uint64(endOffset-sizeOffset-5), 5) 216 ctxt.Out.SeekSet(endOffset) 217 } 218 219 func writeBuildID(ctxt *ld.Link, buildid []byte) { 220 sizeOffset := writeSecHeader(ctxt, sectionCustom) 221 writeName(ctxt.Out, "go.buildid") 222 ctxt.Out.Write(buildid) 223 writeSecSize(ctxt, sizeOffset) 224 } 225 226 func writeGoVersion(ctxt *ld.Link) { 227 sizeOffset := writeSecHeader(ctxt, sectionCustom) 228 writeName(ctxt.Out, "go.version") 229 ctxt.Out.Write([]byte(runtime.Version())) 230 writeSecSize(ctxt, sizeOffset) 231 } 232 233 // writeTypeSec writes the section that declares all function types 234 // so they can be referenced by index. 235 func writeTypeSec(ctxt *ld.Link, types []*wasmFuncType) { 236 sizeOffset := writeSecHeader(ctxt, sectionType) 237 238 writeUleb128(ctxt.Out, uint64(len(types))) 239 240 for _, t := range types { 241 ctxt.Out.WriteByte(0x60) // functype 242 writeUleb128(ctxt.Out, uint64(len(t.Params))) 243 for _, v := range t.Params { 244 ctxt.Out.WriteByte(byte(v)) 245 } 246 writeUleb128(ctxt.Out, uint64(len(t.Results))) 247 for _, v := range t.Results { 248 ctxt.Out.WriteByte(byte(v)) 249 } 250 } 251 252 writeSecSize(ctxt, sizeOffset) 253 } 254 255 // writeImportSec writes the section that lists the functions that get 256 // imported from the WebAssembly host, usually JavaScript. 257 func writeImportSec(ctxt *ld.Link, hostImports []*wasmFunc) { 258 sizeOffset := writeSecHeader(ctxt, sectionImport) 259 260 writeUleb128(ctxt.Out, uint64(len(hostImports))) // number of imports 261 for _, fn := range hostImports { 262 writeName(ctxt.Out, "go") // provided by the import object in wasm_exec.js 263 writeName(ctxt.Out, fn.Name) 264 ctxt.Out.WriteByte(0x00) // func import 265 writeUleb128(ctxt.Out, uint64(fn.Type)) 266 } 267 268 writeSecSize(ctxt, sizeOffset) 269 } 270 271 // writeFunctionSec writes the section that declares the types of functions. 272 // The bodies of these functions will later be provided in the "code" section. 273 func writeFunctionSec(ctxt *ld.Link, fns []*wasmFunc) { 274 sizeOffset := writeSecHeader(ctxt, sectionFunction) 275 276 writeUleb128(ctxt.Out, uint64(len(fns))) 277 for _, fn := range fns { 278 writeUleb128(ctxt.Out, uint64(fn.Type)) 279 } 280 281 writeSecSize(ctxt, sizeOffset) 282 } 283 284 // writeTableSec writes the section that declares tables. Currently there is only a single table 285 // that is used by the CallIndirect operation to dynamically call any function. 286 // The contents of the table get initialized by the "element" section. 287 func writeTableSec(ctxt *ld.Link, fns []*wasmFunc) { 288 sizeOffset := writeSecHeader(ctxt, sectionTable) 289 290 numElements := uint64(funcValueOffset + len(fns)) 291 writeUleb128(ctxt.Out, 1) // number of tables 292 ctxt.Out.WriteByte(0x70) // type: anyfunc 293 ctxt.Out.WriteByte(0x00) // no max 294 writeUleb128(ctxt.Out, numElements) // min 295 296 writeSecSize(ctxt, sizeOffset) 297 } 298 299 // writeMemorySec writes the section that declares linear memories. Currently one linear memory is being used. 300 func writeMemorySec(ctxt *ld.Link) { 301 sizeOffset := writeSecHeader(ctxt, sectionMemory) 302 303 // Linear memory always starts at address zero. 304 // The unit of the sizes is "WebAssembly page size", which is 64Ki. 305 // The minimum is currently set to 1GB, which is a lot. 306 // More memory can be requested with the grow_memory instruction, 307 // but this operation currently is rather slow, so we avoid it for now. 308 // TODO(neelance): Use lower initial memory size. 309 writeUleb128(ctxt.Out, 1) // number of memories 310 ctxt.Out.WriteByte(0x00) // no maximum memory size 311 writeUleb128(ctxt.Out, 1024*16) // minimum (initial) memory size 312 313 writeSecSize(ctxt, sizeOffset) 314 } 315 316 // writeGlobalSec writes the section that declares global variables. 317 func writeGlobalSec(ctxt *ld.Link) { 318 sizeOffset := writeSecHeader(ctxt, sectionGlobal) 319 320 globalRegs := []byte{ 321 I32, // 0: PC_F 322 I32, // 1: PC_B 323 I32, // 2: SP 324 I64, // 3: CTXT 325 I64, // 4: g 326 I64, // 5: RET0 327 I64, // 6: RET1 328 I64, // 7: RET2 329 I64, // 8: RET3 330 I32, // 9: PAUSE 331 } 332 333 writeUleb128(ctxt.Out, uint64(len(globalRegs))) // number of globals 334 335 for _, typ := range globalRegs { 336 ctxt.Out.WriteByte(typ) 337 ctxt.Out.WriteByte(0x01) // var 338 switch typ { 339 case I32: 340 writeI32Const(ctxt.Out, 0) 341 case I64: 342 writeI64Const(ctxt.Out, 0) 343 } 344 ctxt.Out.WriteByte(0x0b) // end 345 } 346 347 writeSecSize(ctxt, sizeOffset) 348 } 349 350 // writeExportSec writes the section that declares exports. 351 // Exports can be accessed by the WebAssembly host, usually JavaScript. 352 // The wasm_export_* functions and the linear memory get exported. 353 func writeExportSec(ctxt *ld.Link, lenHostImports int) { 354 sizeOffset := writeSecHeader(ctxt, sectionExport) 355 356 writeUleb128(ctxt.Out, 4) // number of exports 357 358 for _, name := range []string{"run", "resume", "getsp"} { 359 idx := uint32(lenHostImports) + uint32(ctxt.Syms.ROLookup("wasm_export_"+name, 0).Value>>16) - funcValueOffset 360 writeName(ctxt.Out, name) // inst.exports.run/resume/getsp in wasm_exec.js 361 ctxt.Out.WriteByte(0x00) // func export 362 writeUleb128(ctxt.Out, uint64(idx)) // funcidx 363 } 364 365 writeName(ctxt.Out, "mem") // inst.exports.mem in wasm_exec.js 366 ctxt.Out.WriteByte(0x02) // mem export 367 writeUleb128(ctxt.Out, 0) // memidx 368 369 writeSecSize(ctxt, sizeOffset) 370 } 371 372 // writeElementSec writes the section that initializes the tables declared by the "table" section. 373 // The table for CallIndirect gets initialized in a very simple way so that each table index (PC_F value) 374 // maps linearly to the function index (numImports + PC_F). 375 func writeElementSec(ctxt *ld.Link, numImports, numFns uint64) { 376 sizeOffset := writeSecHeader(ctxt, sectionElement) 377 378 writeUleb128(ctxt.Out, 1) // number of element segments 379 380 writeUleb128(ctxt.Out, 0) // tableidx 381 writeI32Const(ctxt.Out, funcValueOffset) 382 ctxt.Out.WriteByte(0x0b) // end 383 384 writeUleb128(ctxt.Out, numFns) // number of entries 385 for i := uint64(0); i < numFns; i++ { 386 writeUleb128(ctxt.Out, numImports+i) 387 } 388 389 writeSecSize(ctxt, sizeOffset) 390 } 391 392 // writeElementSec writes the section that provides the function bodies for the functions 393 // declared by the "func" section. 394 func writeCodeSec(ctxt *ld.Link, fns []*wasmFunc) { 395 sizeOffset := writeSecHeader(ctxt, sectionCode) 396 397 writeUleb128(ctxt.Out, uint64(len(fns))) // number of code entries 398 for _, fn := range fns { 399 writeUleb128(ctxt.Out, uint64(len(fn.Code))) 400 ctxt.Out.Write(fn.Code) 401 } 402 403 writeSecSize(ctxt, sizeOffset) 404 } 405 406 // writeDataSec writes the section that provides data that will be used to initialize the linear memory. 407 func writeDataSec(ctxt *ld.Link) { 408 sizeOffset := writeSecHeader(ctxt, sectionData) 409 410 sections := []*sym.Section{ 411 ctxt.Syms.Lookup("runtime.rodata", 0).Sect, 412 ctxt.Syms.Lookup("runtime.typelink", 0).Sect, 413 ctxt.Syms.Lookup("runtime.itablink", 0).Sect, 414 ctxt.Syms.Lookup("runtime.symtab", 0).Sect, 415 ctxt.Syms.Lookup("runtime.pclntab", 0).Sect, 416 ctxt.Syms.Lookup("runtime.noptrdata", 0).Sect, 417 ctxt.Syms.Lookup("runtime.data", 0).Sect, 418 } 419 420 writeUleb128(ctxt.Out, uint64(len(sections))) // number of data entries 421 422 for _, sec := range sections { 423 writeUleb128(ctxt.Out, 0) // memidx 424 writeI32Const(ctxt.Out, int32(sec.Vaddr)) 425 ctxt.Out.WriteByte(0x0b) // end 426 writeUleb128(ctxt.Out, uint64(sec.Length)) 427 ld.Datblk(ctxt, int64(sec.Vaddr), int64(sec.Length)) 428 } 429 430 writeSecSize(ctxt, sizeOffset) 431 } 432 433 var nameRegexp = regexp.MustCompile(`[^\w\.]`) 434 435 // writeNameSec writes an optional section that assigns names to the functions declared by the "func" section. 436 // The names are only used by WebAssembly stack traces, debuggers and decompilers. 437 // TODO(neelance): add symbol table of DATA symbols 438 func writeNameSec(ctxt *ld.Link, firstFnIndex int, fns []*wasmFunc) { 439 sizeOffset := writeSecHeader(ctxt, sectionCustom) 440 writeName(ctxt.Out, "name") 441 442 sizeOffset2 := writeSecHeader(ctxt, 0x01) // function names 443 writeUleb128(ctxt.Out, uint64(len(fns))) 444 for i, fn := range fns { 445 writeUleb128(ctxt.Out, uint64(firstFnIndex+i)) 446 writeName(ctxt.Out, fn.Name) 447 } 448 writeSecSize(ctxt, sizeOffset2) 449 450 writeSecSize(ctxt, sizeOffset) 451 } 452 453 type nameWriter interface { 454 io.ByteWriter 455 io.Writer 456 } 457 458 func writeI32Const(w io.ByteWriter, v int32) { 459 w.WriteByte(0x41) // i32.const 460 writeSleb128(w, int64(v)) 461 } 462 463 func writeI64Const(w io.ByteWriter, v int64) { 464 w.WriteByte(0x42) // i64.const 465 writeSleb128(w, v) 466 } 467 468 func writeName(w nameWriter, name string) { 469 writeUleb128(w, uint64(len(name))) 470 w.Write([]byte(name)) 471 } 472 473 func writeUleb128(w io.ByteWriter, v uint64) { 474 more := true 475 for more { 476 c := uint8(v & 0x7f) 477 v >>= 7 478 more = v != 0 479 if more { 480 c |= 0x80 481 } 482 w.WriteByte(c) 483 } 484 } 485 486 func writeUleb128FixedLength(w io.ByteWriter, v uint64, length int) { 487 for i := 0; i < length; i++ { 488 c := uint8(v & 0x7f) 489 v >>= 7 490 if i < length-1 { 491 c |= 0x80 492 } 493 w.WriteByte(c) 494 } 495 if v != 0 { 496 panic("writeUleb128FixedLength: length too small") 497 } 498 } 499 500 func writeSleb128(w io.ByteWriter, v int64) { 501 more := true 502 for more { 503 c := uint8(v & 0x7f) 504 s := uint8(v & 0x40) 505 v >>= 7 506 more = !((v == 0 && s == 0) || (v == -1 && s != 0)) 507 if more { 508 c |= 0x80 509 } 510 w.WriteByte(c) 511 } 512 }