| // Copyright 2012 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package ld |
| |
| import ( |
| "bytes" |
| "cmd/internal/obj" |
| "cmd/internal/sys" |
| "debug/elf" |
| "fmt" |
| ) |
| |
| // Decoding the type.* symbols. This has to be in sync with |
| // ../../runtime/type.go, or more specifically, with what |
| // ../gc/reflect.c stuffs in these. |
| |
| // tflag is documented in reflect/type.go. |
| // |
| // tflag values must be kept in sync with copies in: |
| // cmd/compile/internal/gc/reflect.go |
| // cmd/link/internal/ld/decodesym.go |
| // reflect/type.go |
| // runtime/type.go |
| const ( |
| tflagUncommon = 1 << 0 |
| tflagExtraStar = 1 << 1 |
| ) |
| |
| func decodeReloc(s *Symbol, off int32) *Reloc { |
| for i := range s.R { |
| if s.R[i].Off == off { |
| return &s.R[i] |
| } |
| } |
| return nil |
| } |
| |
| func decodeRelocSym(s *Symbol, off int32) *Symbol { |
| r := decodeReloc(s, off) |
| if r == nil { |
| return nil |
| } |
| return r.Sym |
| } |
| |
| func decodeInuxi(arch *sys.Arch, p []byte, sz int) uint64 { |
| switch sz { |
| case 2: |
| return uint64(arch.ByteOrder.Uint16(p)) |
| case 4: |
| return uint64(arch.ByteOrder.Uint32(p)) |
| case 8: |
| return arch.ByteOrder.Uint64(p) |
| default: |
| Exitf("dwarf: decode inuxi %d", sz) |
| panic("unreachable") |
| } |
| } |
| |
| func commonsize() int { return 4*SysArch.PtrSize + 8 + 8 } // runtime._type |
| func structfieldSize() int { return 3 * SysArch.PtrSize } // runtime.structfield |
| func uncommonSize() int { return 4 + 2 + 2 + 4 + 4 } // runtime.uncommontype |
| |
| // Type.commonType.kind |
| func decodetypeKind(s *Symbol) uint8 { |
| return s.P[2*SysArch.PtrSize+7] & obj.KindMask // 0x13 / 0x1f |
| } |
| |
| // Type.commonType.kind |
| func decodetypeUsegcprog(s *Symbol) uint8 { |
| return s.P[2*SysArch.PtrSize+7] & obj.KindGCProg // 0x13 / 0x1f |
| } |
| |
| // Type.commonType.size |
| func decodetypeSize(arch *sys.Arch, s *Symbol) int64 { |
| return int64(decodeInuxi(arch, s.P, SysArch.PtrSize)) // 0x8 / 0x10 |
| } |
| |
| // Type.commonType.ptrdata |
| func decodetypePtrdata(arch *sys.Arch, s *Symbol) int64 { |
| return int64(decodeInuxi(arch, s.P[SysArch.PtrSize:], SysArch.PtrSize)) // 0x8 / 0x10 |
| } |
| |
| // Type.commonType.tflag |
| func decodetypeHasUncommon(s *Symbol) bool { |
| return s.P[2*SysArch.PtrSize+4]&tflagUncommon != 0 |
| } |
| |
| // Find the elf.Section of a given shared library that contains a given address. |
| func findShlibSection(ctxt *Link, path string, addr uint64) *elf.Section { |
| for _, shlib := range ctxt.Shlibs { |
| if shlib.Path == path { |
| for _, sect := range shlib.File.Sections { |
| if sect.Addr <= addr && addr <= sect.Addr+sect.Size { |
| return sect |
| } |
| } |
| } |
| } |
| return nil |
| } |
| |
| // Type.commonType.gc |
| func decodetypeGcprog(ctxt *Link, s *Symbol) []byte { |
| if s.Type == obj.SDYNIMPORT { |
| addr := decodetypeGcprogShlib(ctxt, s) |
| sect := findShlibSection(ctxt, s.File, addr) |
| if sect != nil { |
| // A gcprog is a 4-byte uint32 indicating length, followed by |
| // the actual program. |
| progsize := make([]byte, 4) |
| sect.ReadAt(progsize, int64(addr-sect.Addr)) |
| progbytes := make([]byte, ctxt.Arch.ByteOrder.Uint32(progsize)) |
| sect.ReadAt(progbytes, int64(addr-sect.Addr+4)) |
| return append(progsize, progbytes...) |
| } |
| Exitf("cannot find gcprog for %s", s.Name) |
| return nil |
| } |
| return decodeRelocSym(s, 2*int32(SysArch.PtrSize)+8+1*int32(SysArch.PtrSize)).P |
| } |
| |
| func decodetypeGcprogShlib(ctxt *Link, s *Symbol) uint64 { |
| if SysArch.Family == sys.ARM64 { |
| for _, shlib := range ctxt.Shlibs { |
| if shlib.Path == s.File { |
| return shlib.gcdataAddresses[s] |
| } |
| } |
| return 0 |
| } |
| return decodeInuxi(ctxt.Arch, s.P[2*int32(SysArch.PtrSize)+8+1*int32(SysArch.PtrSize):], SysArch.PtrSize) |
| } |
| |
| func decodetypeGcmask(ctxt *Link, s *Symbol) []byte { |
| if s.Type == obj.SDYNIMPORT { |
| addr := decodetypeGcprogShlib(ctxt, s) |
| ptrdata := decodetypePtrdata(ctxt.Arch, s) |
| sect := findShlibSection(ctxt, s.File, addr) |
| if sect != nil { |
| r := make([]byte, ptrdata/int64(SysArch.PtrSize)) |
| sect.ReadAt(r, int64(addr-sect.Addr)) |
| return r |
| } |
| Exitf("cannot find gcmask for %s", s.Name) |
| return nil |
| } |
| mask := decodeRelocSym(s, 2*int32(SysArch.PtrSize)+8+1*int32(SysArch.PtrSize)) |
| return mask.P |
| } |
| |
| // Type.ArrayType.elem and Type.SliceType.Elem |
| func decodetypeArrayElem(s *Symbol) *Symbol { |
| return decodeRelocSym(s, int32(commonsize())) // 0x1c / 0x30 |
| } |
| |
| func decodetypeArrayLen(arch *sys.Arch, s *Symbol) int64 { |
| return int64(decodeInuxi(arch, s.P[commonsize()+2*SysArch.PtrSize:], SysArch.PtrSize)) |
| } |
| |
| // Type.PtrType.elem |
| func decodetypePtrElem(s *Symbol) *Symbol { |
| return decodeRelocSym(s, int32(commonsize())) // 0x1c / 0x30 |
| } |
| |
| // Type.MapType.key, elem |
| func decodetypeMapKey(s *Symbol) *Symbol { |
| return decodeRelocSym(s, int32(commonsize())) // 0x1c / 0x30 |
| } |
| |
| func decodetypeMapValue(s *Symbol) *Symbol { |
| return decodeRelocSym(s, int32(commonsize())+int32(SysArch.PtrSize)) // 0x20 / 0x38 |
| } |
| |
| // Type.ChanType.elem |
| func decodetypeChanElem(s *Symbol) *Symbol { |
| return decodeRelocSym(s, int32(commonsize())) // 0x1c / 0x30 |
| } |
| |
| // Type.FuncType.dotdotdot |
| func decodetypeFuncDotdotdot(arch *sys.Arch, s *Symbol) bool { |
| return uint16(decodeInuxi(arch, s.P[commonsize()+2:], 2))&(1<<15) != 0 |
| } |
| |
| // Type.FuncType.inCount |
| func decodetypeFuncInCount(arch *sys.Arch, s *Symbol) int { |
| return int(decodeInuxi(arch, s.P[commonsize():], 2)) |
| } |
| |
| func decodetypeFuncOutCount(arch *sys.Arch, s *Symbol) int { |
| return int(uint16(decodeInuxi(arch, s.P[commonsize()+2:], 2)) & (1<<15 - 1)) |
| } |
| |
| func decodetypeFuncInType(s *Symbol, i int) *Symbol { |
| uadd := commonsize() + 4 |
| if SysArch.PtrSize == 8 { |
| uadd += 4 |
| } |
| if decodetypeHasUncommon(s) { |
| uadd += uncommonSize() |
| } |
| return decodeRelocSym(s, int32(uadd+i*SysArch.PtrSize)) |
| } |
| |
| func decodetypeFuncOutType(arch *sys.Arch, s *Symbol, i int) *Symbol { |
| return decodetypeFuncInType(s, i+decodetypeFuncInCount(arch, s)) |
| } |
| |
| // Type.StructType.fields.Slice::length |
| func decodetypeStructFieldCount(arch *sys.Arch, s *Symbol) int { |
| return int(decodeInuxi(arch, s.P[commonsize()+2*SysArch.PtrSize:], SysArch.IntSize)) |
| } |
| |
| func decodetypeStructFieldArrayOff(s *Symbol, i int) int { |
| off := commonsize() + 2*SysArch.PtrSize + 2*SysArch.IntSize |
| if decodetypeHasUncommon(s) { |
| off += uncommonSize() |
| } |
| off += i * structfieldSize() |
| return off |
| } |
| |
| // decodetypeStr returns the contents of an rtype's str field (a nameOff). |
| func decodetypeStr(s *Symbol) string { |
| str := decodetypeName(s, 4*SysArch.PtrSize+8) |
| if s.P[2*SysArch.PtrSize+4]&tflagExtraStar != 0 { |
| return str[1:] |
| } |
| return str |
| } |
| |
| // decodetypeName decodes the name from a reflect.name. |
| func decodetypeName(s *Symbol, off int) string { |
| r := decodeReloc(s, int32(off)) |
| if r == nil { |
| return "" |
| } |
| |
| data := r.Sym.P |
| namelen := int(uint16(data[1])<<8 | uint16(data[2])) |
| return string(data[3 : 3+namelen]) |
| } |
| |
| func decodetypeStructFieldName(s *Symbol, i int) string { |
| off := decodetypeStructFieldArrayOff(s, i) |
| return decodetypeName(s, off) |
| } |
| |
| func decodetypeStructFieldType(s *Symbol, i int) *Symbol { |
| off := decodetypeStructFieldArrayOff(s, i) |
| return decodeRelocSym(s, int32(off+SysArch.PtrSize)) |
| } |
| |
| func decodetypeStructFieldOffs(arch *sys.Arch, s *Symbol, i int) int64 { |
| off := decodetypeStructFieldArrayOff(s, i) |
| return int64(decodeInuxi(arch, s.P[off+2*SysArch.PtrSize:], SysArch.IntSize)) |
| } |
| |
| // InterfaceType.methods.length |
| func decodetypeIfaceMethodCount(arch *sys.Arch, s *Symbol) int64 { |
| return int64(decodeInuxi(arch, s.P[commonsize()+2*SysArch.PtrSize:], SysArch.IntSize)) |
| } |
| |
| // methodsig is a fully qualified typed method signature, like |
| // "Visit(type.go/ast.Node) (type.go/ast.Visitor)". |
| type methodsig string |
| |
| // Matches runtime/typekind.go and reflect.Kind. |
| const ( |
| kindArray = 17 |
| kindChan = 18 |
| kindFunc = 19 |
| kindInterface = 20 |
| kindMap = 21 |
| kindPtr = 22 |
| kindSlice = 23 |
| kindStruct = 25 |
| kindMask = (1 << 5) - 1 |
| ) |
| |
| // decodeMethodSig decodes an array of method signature information. |
| // Each element of the array is size bytes. The first 4 bytes is a |
| // nameOff for the method name, and the next 4 bytes is a typeOff for |
| // the function type. |
| // |
| // Conveniently this is the layout of both runtime.method and runtime.imethod. |
| func decodeMethodSig(arch *sys.Arch, s *Symbol, off, size, count int) []methodsig { |
| var buf bytes.Buffer |
| var methods []methodsig |
| for i := 0; i < count; i++ { |
| buf.WriteString(decodetypeName(s, off)) |
| mtypSym := decodeRelocSym(s, int32(off+4)) |
| |
| buf.WriteRune('(') |
| inCount := decodetypeFuncInCount(arch, mtypSym) |
| for i := 0; i < inCount; i++ { |
| if i > 0 { |
| buf.WriteString(", ") |
| } |
| buf.WriteString(decodetypeFuncInType(mtypSym, i).Name) |
| } |
| buf.WriteString(") (") |
| outCount := decodetypeFuncOutCount(arch, mtypSym) |
| for i := 0; i < outCount; i++ { |
| if i > 0 { |
| buf.WriteString(", ") |
| } |
| buf.WriteString(decodetypeFuncOutType(arch, mtypSym, i).Name) |
| } |
| buf.WriteRune(')') |
| |
| off += size |
| methods = append(methods, methodsig(buf.String())) |
| buf.Reset() |
| } |
| return methods |
| } |
| |
| func decodeIfaceMethods(arch *sys.Arch, s *Symbol) []methodsig { |
| if decodetypeKind(s)&kindMask != kindInterface { |
| panic(fmt.Sprintf("symbol %q is not an interface", s.Name)) |
| } |
| r := decodeReloc(s, int32(commonsize()+SysArch.PtrSize)) |
| if r == nil { |
| return nil |
| } |
| if r.Sym != s { |
| panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", s.Name)) |
| } |
| off := int(r.Add) // array of reflect.imethod values |
| numMethods := int(decodetypeIfaceMethodCount(arch, s)) |
| sizeofIMethod := 4 + 4 |
| return decodeMethodSig(arch, s, off, sizeofIMethod, numMethods) |
| } |
| |
| func decodetypeMethods(arch *sys.Arch, s *Symbol) []methodsig { |
| if !decodetypeHasUncommon(s) { |
| panic(fmt.Sprintf("no methods on %q", s.Name)) |
| } |
| off := commonsize() // reflect.rtype |
| switch decodetypeKind(s) & kindMask { |
| case kindStruct: // reflect.structType |
| off += 2*SysArch.PtrSize + 2*SysArch.IntSize |
| case kindPtr: // reflect.ptrType |
| off += SysArch.PtrSize |
| case kindFunc: // reflect.funcType |
| off += SysArch.PtrSize // 4 bytes, pointer aligned |
| case kindSlice: // reflect.sliceType |
| off += SysArch.PtrSize |
| case kindArray: // reflect.arrayType |
| off += 3 * SysArch.PtrSize |
| case kindChan: // reflect.chanType |
| off += 2 * SysArch.PtrSize |
| case kindMap: // reflect.mapType |
| off += 4*SysArch.PtrSize + 8 |
| case kindInterface: // reflect.interfaceType |
| off += SysArch.PtrSize + 2*SysArch.IntSize |
| default: |
| // just Sizeof(rtype) |
| } |
| |
| mcount := int(decodeInuxi(arch, s.P[off+4:], 2)) |
| moff := int(decodeInuxi(arch, s.P[off+4+2+2:], 4)) |
| off += moff // offset to array of reflect.method values |
| const sizeofMethod = 4 * 4 // sizeof reflect.method in program |
| return decodeMethodSig(arch, s, off, sizeofMethod, mcount) |
| } |