1
2
3
4
5
6
7 package pkgpath
8
9 import (
10 "bytes"
11 "errors"
12 "fmt"
13 "os"
14 "os/exec"
15 "strings"
16 )
17
18
19
20
21
22
23
24
25
26 func ToSymbolFunc(cmd, tmpdir string) (func(string) string, error) {
27
28
29
30
31
32
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
53
54
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
67 const mangleCheckCode = `
68 package läufer
69 func Run(x int) int {
70 return 1
71 }
72 `
73
74
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
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
117
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
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 }
171
View as plain text