Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement unexported func name removing #633

Merged
merged 4 commits into from
Jan 9, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 23 additions & 26 deletions internal/linker/linker.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,19 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/bluekeyes/go-gitdiff/gitdiff"
"github.com/rogpeppe/go-internal/lockedfile"
mvdan marked this conversation as resolved.
Show resolved Hide resolved
"io"
"io/fs"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"

"github.com/bluekeyes/go-gitdiff/gitdiff"
"github.com/rogpeppe/go-internal/lockedfile"
)

const (
MagicValueEnv = "GARBLE_LINKER_MAGIC"
MagicValueEnv = "GARBLE_LINK_MAGIC"
TinyEnv = "GARBLE_LINK_TINY"
mvdan marked this conversation as resolved.
Show resolved Hide resolved

cacheDirName = "garble"
versionExt = ".version"
Expand All @@ -36,10 +35,10 @@ var (
linkerPatchesFS embed.FS
)

func loadLinkerPatches() (string, map[string]string, error) {
func loadLinkerPatches() (version string, modFiles map[string]bool, patches [][]byte, err error) {
modFiles = make(map[string]bool)
versionHash := sha256.New()
patches := make(map[string]string)
err := fs.WalkDir(linkerPatchesFS, ".", func(path string, d fs.DirEntry, err error) error {
err = fs.WalkDir(linkerPatchesFS, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
Expand All @@ -64,23 +63,17 @@ func loadLinkerPatches() (string, map[string]string, error) {
if file.IsNew || file.IsDelete || file.IsCopy || file.IsRename {
panic("only modification patch is supported")
}
patches[file.OldName] = string(patchBytes)
modFiles[file.OldName] = true
}
patches = append(patches, patchBytes)
return nil
})

if err != nil {
return "", nil, err
return
}
return base64.RawStdEncoding.EncodeToString(versionHash.Sum(nil)), patches, nil
}

// TODO(pagran): Remove git dependency in future
// more information in README.md
func applyPatch(workingDir, patch string) error {
cmd := exec.Command("git", "-C", workingDir, "apply")
cmd.Stdin = strings.NewReader(patch)
return cmd.Run()
version = base64.RawStdEncoding.EncodeToString(versionHash.Sum(nil))
return
}

func copyFile(src, target string) error {
Expand Down Expand Up @@ -111,20 +104,24 @@ func fileExists(path string) bool {
return !stat.IsDir()
}

func applyPatches(srcDir, workingDir string, patches map[string]string) (map[string]string, error) {
// TODO(pagran): Remove git dependency in future
// more information in README.md
func applyPatches(srcDir, workingDir string, modFiles map[string]bool, patches [][]byte) (map[string]string, error) {
mod := make(map[string]string)
for fileName, patch := range patches {
for fileName := range modFiles {
oldPath := filepath.Join(srcDir, fileName)
newPath := filepath.Join(workingDir, fileName)
mod[oldPath] = newPath

if err := copyFile(oldPath, newPath); err != nil {
return nil, err
}
}

if err := applyPatch(workingDir, patch); err != nil {
return nil, fmt.Errorf("apply patch for %s failed: %v", fileName, err)
}
cmd := exec.Command("git", "-C", workingDir, "apply")
cmd.Stdin = bytes.NewReader(bytes.Join(patches, []byte("\n")))
if err := cmd.Run(); err != nil {
return nil, err
}
return mod, nil
}
Expand Down Expand Up @@ -199,7 +196,7 @@ func buildLinker(workingDir string, overlay map[string]string, outputLinkPath st
}

func PatchLinker(goRoot, goVersion, goExe, tempDir string) (string, func(), error) {
patchesVer, patches, err := loadLinkerPatches()
patchesVer, modFiles, patches, err := loadLinkerPatches()
if err != nil {
panic(fmt.Errorf("cannot retrieve linker patches: %v", err))
}
Expand Down Expand Up @@ -235,7 +232,7 @@ func PatchLinker(goRoot, goVersion, goExe, tempDir string) (string, func(), erro
srcDir := filepath.Join(goRoot, baseSrcSubdir)
workingDir := filepath.Join(tempDir, "linker-src")

overlay, err := applyPatches(srcDir, workingDir, patches)
overlay, err := applyPatches(srcDir, workingDir, modFiles, patches)
if err != nil {
return "", nil, err
}
Expand Down
10 changes: 5 additions & 5 deletions internal/linker/patches/0001-add-custom-magic-value.patch
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
From de93a968f1bb3500088b30cbdce439e6a0d95e58 Mon Sep 17 00:00:00 2001
From 444ff9310865ff5e4b367d28edfd386ec795524e Mon Sep 17 00:00:00 2001
From: pagran <pagran@protonmail.com>
Date: Sun, 8 Jan 2023 14:12:51 +0100
Subject: [PATCH 1/1] add custom magic value
Date: Mon, 9 Jan 2023 10:32:32 +0100
Subject: [PATCH] add custom magic value

---
cmd/link/internal/ld/pcln.go | 13 +++++++++++++
1 file changed, 13 insertions(+)

diff --git a/cmd/link/internal/ld/pcln.go b/cmd/link/internal/ld/pcln.go
index 34ab86cf12..1ec237ffc8 100644
index 34ab86cf12..b89a4d650c 100644
--- a/cmd/link/internal/ld/pcln.go
+++ b/cmd/link/internal/ld/pcln.go
@@ -249,6 +249,19 @@ func (state *pclntab) generatePCHeader(ctxt *Link) {
Expand All @@ -17,7 +17,7 @@ index 34ab86cf12..1ec237ffc8 100644
}
+
+ // Use garble prefix in variable names to minimize collision risk
+ garbleMagicStr := os.Getenv("GARBLE_LINKER_MAGIC")
+ garbleMagicStr := os.Getenv("GARBLE_LINK_MAGIC")
+ if garbleMagicStr == "" {
+ panic("[garble] magic value must be set")
+ }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
From aad38f7aa37d00c723c3540bd8a907b92353d97d Mon Sep 17 00:00:00 2001
From: pagran <pagran@protonmail.com>
Date: Mon, 9 Jan 2023 10:27:41 +0100
Subject: [PATCH] add unexported function name removing

---
cmd/link/internal/ld/pcln.go | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)

diff --git a/cmd/link/internal/ld/pcln.go b/cmd/link/internal/ld/pcln.go
index 1ec237ffc8..e1bea2032c 100644
--- a/cmd/link/internal/ld/pcln.go
+++ b/cmd/link/internal/ld/pcln.go
@@ -321,10 +321,19 @@ func (state *pclntab) generateFuncnametab(ctxt *Link, funcs []loader.Sym) map[lo
return name[:i], "[...]", name[j+1:]
}

+ garbleIsRemove := os.Getenv("GARBLE_LINK_TINY") == "true"
+
// Write the null terminated strings.
writeFuncNameTab := func(ctxt *Link, s loader.Sym) {
symtab := ctxt.loader.MakeSymbolUpdater(s)
+ if garbleIsRemove {
+ symtab.AddStringAt(0, "")
+ }
+
for s, off := range nameOffsets {
+ if garbleIsRemove && off == 0 {
+ continue
+ }
a, b, c := nameParts(ctxt.loader.SymName(s))
o := int64(off)
o = symtab.AddStringAt(o, a)
@@ -335,7 +344,25 @@ func (state *pclntab) generateFuncnametab(ctxt *Link, funcs []loader.Sym) map[lo

// Loop through the CUs, and calculate the size needed.
var size int64
+
+ if garbleIsRemove {
+ size = 1 // first byte is reserved for empty string used for all non-exportable method names
+ }
+ garbleIsUnexported := func(s loader.Sym) bool {
+ name, _, _ := nameParts(ctxt.loader.SymName(s))
+ if name[len(name)-1] == '.' {
+ return true
+ }
+ c := name[strings.LastIndexByte(name, '.')+1]
+ return 'a' <= c && c <= 'z'
+ }
+
walkFuncs(ctxt, funcs, func(s loader.Sym) {
+ if garbleIsRemove && garbleIsUnexported(s) {
+ nameOffsets[s] = 0 // redirect name to empty string
+ return
+ }
+
nameOffsets[s] = uint32(size)
a, b, c := nameParts(ctxt.loader.SymName(s))
size += int64(len(a) + len(b) + len(c) + 1) // NULL terminate
--
2.38.1.windows.1

3 changes: 3 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,9 @@ func mainErr(args []string) error {

executablePath = modifiedLinkPath
os.Setenv(linker.MagicValueEnv, strconv.FormatUint(uint64(magicValue()), 10))
if flagTiny {
os.Setenv(linker.TinyEnv, "true")
}

log.Printf("replaced linker with: %s", executablePath)
}
Expand Down
30 changes: 28 additions & 2 deletions testdata/script/tiny.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ stderr '^caller: \?\? 1$' # position info is removed
stderr '^recovered: ya like jazz?'
! stderr '^init runtime' # GODEBUG prints are hidden, like inittrace=1
! stderr 'panic: oh noes' # panics are hidden

stderr 'funcExported false funcUnexported true'
stderr 'funcStructExported false funcStructUnexported true'

[short] stop # no need to verify this with -short

Expand All @@ -21,16 +22,41 @@ garble build
stderr '^caller: [0-9a-zA-Z_]+\.go [1-9]'
stderr '^recovered: ya like jazz?'
stderr 'panic: oh noes'
stderr 'funcExported false funcUnexported false'
stderr 'funcStructExported false funcStructUnexported false'
-- go.mod --
module test/main

go 1.19
-- garble_main.go --
package main

import "runtime"
import (
"reflect"
"runtime"
)

type testStruct struct {}

func (testStruct) unexportedFunc() { println("dummy") }

func (testStruct) ExportedFunc() { println("dummy") }

func ExportedFunc() { println("dummy") }

func unexportedFunc() { println("dummy") }

func isEmptyFuncName(i interface{}) bool {
name := runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
return len(name) == 0
}

func main() {
println("funcExported", isEmptyFuncName(ExportedFunc), "funcUnexported", isEmptyFuncName(unexportedFunc))

var s testStruct
println("funcStructExported", isEmptyFuncName(s.ExportedFunc), "funcStructUnexported", isEmptyFuncName(s.unexportedFunc))

var v any = "tada"
println(v)

Expand Down