github.com/bir3/gocompiler@v0.9.2202/src/cmd/internal/pkgpath/pkgpath.go (about) 1 // Copyright 2020 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 pkgpath determines the package path used by gccgo/GoLLVM symbols. 6 // This package is not used for the gc compiler. 7 package pkgpath 8 9 import ( 10 "bytes" 11 "errors" 12 "fmt" 13 "os" 14 "github.com/bir3/gocompiler/exec" 15 "strings" 16 ) 17 18 // ToSymbolFunc returns a function that may be used to convert a 19 // package path into a string suitable for use as a symbol. 20 // cmd is the gccgo/GoLLVM compiler in use, and tmpdir is a temporary 21 // directory to pass to os.CreateTemp(). 22 // For example, this returns a function that converts "net/http" 23 // into a string like "net..z2fhttp". The actual string varies for 24 // different gccgo/GoLLVM versions, which is why this returns a function 25 // that does the conversion appropriate for the compiler in use. 26 func ToSymbolFunc(cmd, tmpdir string) (func(string) string, error) { 27 // To determine the scheme used by cmd, we compile a small 28 // file and examine the assembly code. Older versions of gccgo 29 // use a simple mangling scheme where there can be collisions 30 // between packages whose paths are different but mangle to 31 // the same string. More recent versions use a new mangler 32 // that avoids these collisions. 33 const filepat = "*_gccgo_manglechck.go" 34 f, err := os.CreateTemp(tmpdir, filepat) 35 if err != nil { 36 return nil, err 37 } 38 gofilename := f.Name() 39 f.Close() 40 defer os.Remove(gofilename) 41 42 if err := os.WriteFile(gofilename, []byte(mangleCheckCode), 0644); err != nil { 43 return nil, err 44 } 45 46 command := exec.Command(cmd, "-S", "-o", "-", gofilename) 47 buf, err := command.Output() 48 if err != nil { 49 return nil, err 50 } 51 52 // Original mangling: go.l__ufer.Run 53 // Mangling v2: go.l..u00e4ufer.Run 54 // Mangling v3: go_0l_u00e4ufer.Run 55 if bytes.Contains(buf, []byte("go_0l_u00e4ufer.Run")) { 56 return toSymbolV3, nil 57 } else if bytes.Contains(buf, []byte("go.l..u00e4ufer.Run")) { 58 return toSymbolV2, nil 59 } else if bytes.Contains(buf, []byte("go.l__ufer.Run")) { 60 return toSymbolV1, nil 61 } else { 62 return nil, errors.New(cmd + ": unrecognized mangling scheme") 63 } 64 } 65 66 // mangleCheckCode is the package we compile to determine the mangling scheme. 67 const mangleCheckCode = ` 68 package läufer 69 func Run(x int) int { 70 return 1 71 } 72 ` 73 74 // toSymbolV1 converts a package path using the original mangling scheme. 75 func toSymbolV1(ppath string) string { 76 clean := func(r rune) rune { 77 switch { 78 case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z', 79 '0' <= r && r <= '9': 80 return r 81 } 82 return '_' 83 } 84 return strings.Map(clean, ppath) 85 } 86 87 // toSymbolV2 converts a package path using the second mangling scheme. 88 func toSymbolV2(ppath string) string { 89 var bsl strings.Builder 90 changed := false 91 for _, c := range ppath { 92 if ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || c == '_' { 93 bsl.WriteByte(byte(c)) 94 continue 95 } 96 var enc string 97 switch { 98 case c == '.': 99 enc = ".x2e" 100 case c < 0x80: 101 enc = fmt.Sprintf("..z%02x", c) 102 case c < 0x10000: 103 enc = fmt.Sprintf("..u%04x", c) 104 default: 105 enc = fmt.Sprintf("..U%08x", c) 106 } 107 bsl.WriteString(enc) 108 changed = true 109 } 110 if !changed { 111 return ppath 112 } 113 return bsl.String() 114 } 115 116 // v3UnderscoreCodes maps from a character that supports an underscore 117 // encoding to the underscore encoding character. 118 var v3UnderscoreCodes = map[byte]byte{ 119 '_': '_', 120 '.': '0', 121 '/': '1', 122 '*': '2', 123 ',': '3', 124 '{': '4', 125 '}': '5', 126 '[': '6', 127 ']': '7', 128 '(': '8', 129 ')': '9', 130 '"': 'a', 131 ' ': 'b', 132 ';': 'c', 133 } 134 135 // toSymbolV3 converts a package path using the third mangling scheme. 136 func toSymbolV3(ppath string) string { 137 var bsl strings.Builder 138 changed := false 139 for _, c := range ppath { 140 if ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') { 141 bsl.WriteByte(byte(c)) 142 continue 143 } 144 145 if c < 0x80 { 146 if u, ok := v3UnderscoreCodes[byte(c)]; ok { 147 bsl.WriteByte('_') 148 bsl.WriteByte(u) 149 changed = true 150 continue 151 } 152 } 153 154 var enc string 155 switch { 156 case c < 0x80: 157 enc = fmt.Sprintf("_x%02x", c) 158 case c < 0x10000: 159 enc = fmt.Sprintf("_u%04x", c) 160 default: 161 enc = fmt.Sprintf("_U%08x", c) 162 } 163 bsl.WriteString(enc) 164 changed = true 165 } 166 if !changed { 167 return ppath 168 } 169 return bsl.String() 170 }