support obfuscating the time package

This failed at link time because transformAsm did not know how to handle
the fact that the runtime package's assembly code implements the
`time.now` function via:

	TEXT time·now<ABIInternal>(SB),NOSPLIT,$16-24

First, we need transformAsm to happen for all packages, not just the
ones that we are obfuscating. This is because the runtime can implement
APIs in other packages which are themselves obfuscated, whereas runtime
may not itself be getting obfuscated. This is currently the case with
`GOGARBLE=*` as we do not yet support obfuscating the runtime.

Second, we need to teach replaceAsmNames to handle qualified names with
import paths. Not just to look up the right package information for the
name, but also to obfuscate the package path if necessary.

Third, we need to relax the Deps requirement on listPackage, since the
runtime package and its dependencies are always implicit dependencies.

This is a big step towards being able to obfuscate the runtime, as there
is now just one package left that we cannot obfuscate outside the runtime.

Updates #193.
pull/596/head
Daniel Martí 2 years ago committed by lu4p
parent 58b2d64784
commit e71cb69dd8

@ -587,14 +587,10 @@ var transformFuncs = map[string]func([]string) ([]string, error){
var rxIncludeHeader = regexp.MustCompile(`#include\s+"([^"]+)"`)
func transformAsm(args []string) ([]string, error) {
if !curPkg.ToObfuscate {
return args, nil // we're not obfuscating this package
}
flags, paths := splitFlagsFromFiles(args, ".s")
// When assembling, the import path can make its way into the output object file.
if curPkg.Name != "main" {
if curPkg.Name != "main" && curPkg.ToObfuscate {
flags = flagSetValue(flags, "-p", curPkg.obfuscatedImportPath())
}
@ -693,10 +689,17 @@ func replaceAsmNames(buf *bytes.Buffer, remaining []byte) {
// Luckily, all func names in Go assembly files are immediately followed
// by the unicode "middle dot", like:
//
// TEXT ·privateAdd(SB),$0-24
// TEXT ·privateAdd(SB),$0-24
// TEXT runtimeinternalsys·Ctz64(SB), NOSPLIT, $0-12
const middleDot = '·'
middleDotLen := utf8.RuneLen(middleDot)
// Note that import paths in assembly, like `runtimeinternalsys` above,
// use a Unicode slash rather than the ASCII one used by Go and `go list`.
// We need to convert to ASCII to find the right package information.
const asmPkgSlash = ''
const goPkgSlash = '/'
for {
i := bytes.IndexRune(remaining, middleDot)
if i < 0 {
@ -705,28 +708,46 @@ func replaceAsmNames(buf *bytes.Buffer, remaining []byte) {
break
}
// We want to replace "OP ·foo" and "OP $·foo",
// but not "OP somepkg·foo" just yet.
// "somepkg" is often runtime, syscall, etc.
// We don't obfuscate any of those for now.
//
// TODO: we'll likely need to deal with this
// when we start obfuscating the runtime.
// When we do, note that we can't hash with curPkg.
localName := false
if i >= 0 {
switch remaining[i-1] {
case ' ', '\t', '$', ',', '(':
localName = true
// The package name ends at the first rune which cannot be part of a Go
// import path, such as a comma or space.
pkgStart := i
for pkgStart >= 0 {
c, size := utf8.DecodeLastRune(remaining[:pkgStart])
if !unicode.IsLetter(c) && c != '_' && c != asmPkgSlash && !unicode.IsDigit(c) {
break
}
pkgStart -= size
}
asmPkgPath := string(remaining[pkgStart:i])
goPkgPath := strings.ReplaceAll(asmPkgPath, string(asmPkgSlash), string(goPkgSlash))
// Write the bytes before our unqualified `·foo` or qualified `pkg·foo`.
buf.Write(remaining[:pkgStart])
// If the name was qualified, fetch the package, and write the
// obfuscated import path if needed.
lpkg := curPkg
if asmPkgPath != "" {
var err error
lpkg, err = listPackage(goPkgPath)
if err != nil {
panic(err) // shouldn't happen
}
if lpkg.ToObfuscate {
// Note that we don't need to worry about asmPkgSlash here,
// because our obfuscated import paths contain no slashes right now.
buf.WriteString(lpkg.obfuscatedImportPath())
} else {
buf.WriteString(asmPkgPath)
}
}
i += middleDotLen
buf.Write(remaining[:i])
remaining = remaining[i:]
// Write the middle dot and advance the remaining slice.
buf.WriteRune(middleDot)
remaining = remaining[i+middleDotLen:]
// The name ends at the first rune which cannot be part
// of a Go identifier, such as a comma or space.
// The declared name ends at the first rune which cannot be part of a Go
// identifier, such as a comma or space.
nameEnd := 0
for nameEnd < len(remaining) {
c, size := utf8.DecodeRune(remaining[nameEnd:])
@ -738,16 +759,15 @@ func replaceAsmNames(buf *bytes.Buffer, remaining []byte) {
name := string(remaining[:nameEnd])
remaining = remaining[nameEnd:]
if !localName {
if lpkg.ToObfuscate {
newName := hashWithPackage(lpkg, name)
if flagDebug { // TODO(mvdan): remove once https://go.dev/issue/53465 if fixed
log.Printf("asm name %q hashed with %x to %q", name, curPkg.GarbleActionID, newName)
}
buf.WriteString(newName)
} else {
buf.WriteString(name)
continue
}
newName := hashWithPackage(curPkg, name)
if flagDebug { // TODO(mvdan): remove once https://go.dev/issue/53465 if fixed
log.Printf("asm name %q hashed with %x to %q", name, curPkg.GarbleActionID, newName)
}
buf.WriteString(newName)
}
}

@ -289,9 +289,6 @@ func appendListedPackages(packages []string, withDeps bool) error {
//
// TODO: investigate and resolve each one of these
var cannotObfuscate = map[string]bool{
// "undefined reference" errors at link time
"time": true,
// "//go:linkname must refer to declared function or variable"
"syscall": true,
@ -393,15 +390,30 @@ func listPackage(path string) (*listedPackage, error) {
log.Printf("listed %d missing runtime-linknamed packages in %s", len(missing), debugSince(startTime))
return pkg, nil
}
// Packages other than runtime can list any package,
// as long as they depend on it directly or indirectly.
if !ok {
return nil, fmt.Errorf("path not found in listed packages: %s", path)
}
// Packages other than runtime can list any package,
// as long as they depend on it directly or indirectly.
for _, dep := range curPkg.Deps {
if dep == pkg.ImportPath {
return pkg, nil
}
}
// As a special case, any package can list runtime or its dependencies,
// since those are always an implicit dependency.
// We need to handle this ourselves as runtime does not appear in Deps.
// TODO: it might be faster to bring back a "runtimeAndDeps" map or func.
if pkg.ImportPath == "runtime" {
return pkg, nil
}
for _, dep := range cache.ListedPackages["runtime"].Deps {
if dep == pkg.ImportPath {
return pkg, nil
}
}
return nil, fmt.Errorf("refusing to list non-dependency package: %s", path)
}

Loading…
Cancel
Save