Skip to content

Commit

Permalink
clarify internal/abi name code a bit
Browse files Browse the repository at this point in the history
Use the term "original name" rather than "real name" for the code
as it is clearer that we mean the unobfuscated name.

Update the comments at the top of the file to be clearer with the
explanation of what kinds of inputs we can expect.

While here, use fmt.Appendf to simplify the generation code a bit.
  • Loading branch information
mvdan committed Dec 1, 2024
1 parent 0f1a77e commit 67d2e06
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 33 deletions.
14 changes: 7 additions & 7 deletions bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,23 +168,23 @@ func BenchmarkBuild(b *testing.B) {
b.ReportMetric(float64(info.Size()), "bin-B")
}

func BenchmarkAbiRealName(b *testing.B) {
// Benchmark two thousand obfuscated names in _realNamePairs
func BenchmarkAbiOriginalNames(b *testing.B) {
// Benchmark two thousand obfuscated names in _originalNamePairs
// and a variety of input strings to reverse.
// As an example, the cmd/go binary ends up with about 2200 entries
// in _realNamePairs as of November 2024, so it's a realistic figure.
// in _originalNamePairs as of November 2024, so it's a realistic figure.
// Structs with tens of fields are also relatively normal.
salt := []byte("some salt bytes")
for n := range 2000 {
name := fmt.Sprintf("name_%d", n)
garbled := hashWithCustomSalt(salt, name)
_realNamePairs = append(_realNamePairs, [2]string{garbled, name})
_originalNamePairs = append(_originalNamePairs, [2]string{garbled, name})
}
// Pick twenty names at random to use as inputs below.
// Use a deterministic random source so it's stable between benchmark runs.
rnd := rand.New(rand.NewPCG(1, 2))
var chosen []string
for _, pair := range _realNamePairs {
for _, pair := range _originalNamePairs {
chosen = append(chosen, pair[0])
}
rnd.Shuffle(len(chosen), func(i, j int) {
Expand Down Expand Up @@ -219,9 +219,9 @@ func BenchmarkAbiRealName(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
for _, input := range inputs {
_realName(input)
_originalNames(input)
}
}
})
_realNamePairs = [][2]string{}
_originalNamePairs = [][2]string{}
}
29 changes: 14 additions & 15 deletions reflect_abi_code.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
package main

// The "name" internal/abi passes to this function doesn't have to be a simple "someName"
// it can also be for function names:
// "*pkgName.FuncName" (obfuscated)
// or for structs the entire struct definition:
// "*struct { AQ45rr68K string; ipq5aQSIqN string; hNfiW5O5LVq struct { gPTbGR00hu string } }"

// it can also be for function names like "*pkgName.FuncName" (obfuscated)
// or for structs the entire struct definition, like
//
// Therefore all obfuscated names which occur within name need to be replaced with their "real" equivalents.
// *struct { AQ45rr68K string; ipq5aQSIqN string; hNfiW5O5LVq struct { gPTbGR00hu string } }
//
// Therefore all obfuscated names which occur within name need to be replaced with their original equivalents.
// The code below does a more efficient version of:
//
// func _realName(name string) string {
// for obfName, real := range _nameMap {
// name = strings.ReplaceAll(name, obfName, real)
// }
//
// return name
// func _originalNames(name string) string {
// for _, pair := range _originalNamePairs {
// name = strings.ReplaceAll(name, pair[0], pair[1])
// }
// return name
// }
//
// The linknames below are only turned on when the code is injected,
// so that we can test and benchmark this code normally.

// Injected code below this line.

//disabledgo:linkname _realName internal/abi._realName
func _realName(name string) string {
//disabledgo:linkname _originalNames internal/abi._originalNames
func _originalNames(name string) string {
if len(name) < minHashLength {
// The name is too short to be obfuscated.
return name
Expand All @@ -39,7 +38,7 @@ func _realName(name string) string {
}
remLen := len(name[i:])
found := false
for _, pair := range _realNamePairs {
for _, pair := range _originalNamePairs {
obfName := pair[0]
real := pair[1]
keyLen := len(obfName)
Expand All @@ -64,4 +63,4 @@ func _realName(name string) string {

// Each pair is the obfuscated and then the real name.
// The slice is sorted from shortest to longest obfuscated name.
var _realNamePairs = [][2]string{}
var _originalNamePairs = [][2]string{}
22 changes: 11 additions & 11 deletions reflect_abi_patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,21 @@ func abiNamePatch(path string) (string, error) {
}

find := `return unsafe.String(n.DataChecked(1+i, "non-empty string"), l)`
replace := `return _realName(unsafe.String(n.DataChecked(1+i, "non-empty string"), l))`
replace := `return _originalNames(unsafe.String(n.DataChecked(1+i, "non-empty string"), l))`

str := strings.Replace(string(data), find, replace, 1)

realname := `
//go:linkname _realName
func _realName(name string) string
originalNames := `
//go:linkname _originalNames
func _originalNames(name string) string
`

return str + realname, nil
return str + originalNames, nil
}

var reflectPatchFile = ""

// reflectMainPrePatch adds the initial empty name mapping and _realName implementation
// reflectMainPrePatch adds the initial empty name mapping and _originalNames implementation
// to a file in the main package. The name mapping will be populated later after
// analyzing the main package, since we need to know all obfuscated names that need mapping.
// We split this into pre/post steps so that all variable names in the generated code
Expand All @@ -59,18 +59,18 @@ func reflectMainPrePatch(path string) ([]byte, error) {
// reflectMainPostPatch populates the name mapping with the final obfuscated->real name
// mappings after all packages have been analyzed.
func reflectMainPostPatch(file []byte, lpkg *listedPackage, pkg pkgCache) []byte {
obfVarName := hashWithPackage(lpkg, "_realNamePairs")
nameMap := fmt.Sprintf("%s = [][2]string{", obfVarName)
obfVarName := hashWithPackage(lpkg, "_originalNamePairs")
namePairs := fmt.Appendf(nil, "%s = [][2]string{", obfVarName)

var b strings.Builder
keys := slices.SortedFunc(maps.Keys(pkg.ReflectObjectNames), func(a, b string) int {
return cmp.Compare(len(a), len(b))
})
namePairsFilled := bytes.Clone(namePairs)
for _, obf := range keys {
b.WriteString(fmt.Sprintf("{%q, %q},", obf, pkg.ReflectObjectNames[obf]))
namePairsFilled = fmt.Appendf(namePairsFilled, "{%q, %q},", obf, pkg.ReflectObjectNames[obf])
}

return bytes.Replace(file, []byte(nameMap), []byte(nameMap+b.String()), 1)
return bytes.Replace(file, namePairs, namePairsFilled, 1)
}

//go:embed reflect_abi_code.go
Expand Down

0 comments on commit 67d2e06

Please sign in to comment.