mirror of
https://github.com/sliverarmory/reflektor
synced 2026-08-25 09:14:48 +00:00
191 lines
5.6 KiB
Go
191 lines
5.6 KiB
Go
package reflektor_test
|
|
|
|
import (
|
|
"bytes"
|
|
"debug/elf"
|
|
"debug/macho"
|
|
"debug/pe"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
const rustHTTPMarker = "ok:200"
|
|
|
|
func buildOneRustSharedLib(t *testing.T, outDir string, goos string, goarch string) string {
|
|
t.Helper()
|
|
requireCommand(t, "cargo")
|
|
|
|
target, ok := rustTargetFor(goos, goarch)
|
|
if !ok {
|
|
t.Fatalf("unsupported Rust shared-library target %s/%s", goos, goarch)
|
|
}
|
|
|
|
manifestPath, err := filepath.Abs(filepath.Join("..", "testdata", "rust", "Cargo.toml"))
|
|
if err != nil {
|
|
t.Fatalf("resolve Rust fixture manifest: %v", err)
|
|
}
|
|
targetDir := filepath.Join(outDir, "cargo-target")
|
|
cmd := exec.Command(
|
|
"cargo",
|
|
"build",
|
|
"--manifest-path", manifestPath,
|
|
"--locked",
|
|
"--release",
|
|
"--target", target,
|
|
"--target-dir", targetDir,
|
|
)
|
|
cmd.Env = overrideEnv(os.Environ(), map[string]string{
|
|
"CARGO_TERM_COLOR": "never",
|
|
})
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("build Rust shared lib target=%s/%s (%s): %v\n%s", goos, goarch, target, err, output)
|
|
}
|
|
|
|
artifactName, err := rustSharedLibName(goos)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
artifactPath := filepath.Join(targetDir, target, "release", artifactName)
|
|
info, err := os.Stat(artifactPath)
|
|
if err != nil {
|
|
t.Fatalf("stat Rust shared lib %s: %v\ncargo output:\n%s", artifactPath, err, output)
|
|
}
|
|
if info.Size() == 0 {
|
|
t.Fatalf("empty Rust shared lib: %s", artifactPath)
|
|
}
|
|
|
|
assertRustSharedLibIsLoadable(t, artifactPath, goos)
|
|
return artifactPath
|
|
}
|
|
|
|
func rustTargetFor(goos string, goarch string) (string, bool) {
|
|
switch {
|
|
case goos == "darwin" && goarch == "amd64":
|
|
return "x86_64-apple-darwin", true
|
|
case goos == "darwin" && goarch == "arm64":
|
|
return "aarch64-apple-darwin", true
|
|
case goos == "linux" && goarch == "386":
|
|
return "i686-unknown-linux-gnu", true
|
|
case goos == "linux" && goarch == "amd64":
|
|
return "x86_64-unknown-linux-gnu", true
|
|
case goos == "linux" && goarch == "arm":
|
|
return "armv7-unknown-linux-gnueabihf", true
|
|
case goos == "linux" && goarch == "arm64":
|
|
return "aarch64-unknown-linux-gnu", true
|
|
case goos == "linux" && goarch == "ppc64le":
|
|
return "powerpc64le-unknown-linux-gnu", true
|
|
case goos == "linux" && goarch == "riscv64":
|
|
return "riscv64gc-unknown-linux-gnu", true
|
|
case goos == "windows" && goarch == "386":
|
|
return "i686-pc-windows-msvc", true
|
|
case goos == "windows" && goarch == "amd64":
|
|
return "x86_64-pc-windows-msvc", true
|
|
case goos == "windows" && goarch == "arm64":
|
|
return "aarch64-pc-windows-msvc", true
|
|
default:
|
|
return "", false
|
|
}
|
|
}
|
|
|
|
func rustSharedLibName(goos string) (string, error) {
|
|
switch goos {
|
|
case "darwin":
|
|
return "libreflektor_http_fixture.dylib", nil
|
|
case "linux":
|
|
return "libreflektor_http_fixture.so", nil
|
|
case "windows":
|
|
return "reflektor_http_fixture.dll", nil
|
|
default:
|
|
return "", fmt.Errorf("unsupported Rust shared-library OS: %s", goos)
|
|
}
|
|
}
|
|
|
|
func newRustBuildDir(t *testing.T) string {
|
|
t.Helper()
|
|
dir, err := os.MkdirTemp("", "reflektor-rust-fixture-*")
|
|
if err != nil {
|
|
t.Fatalf("create Rust fixture build dir: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
_ = os.RemoveAll(dir)
|
|
})
|
|
return dir
|
|
}
|
|
|
|
func assertRustHTTPMarker(t *testing.T, markerPath string) {
|
|
t.Helper()
|
|
got, err := os.ReadFile(markerPath)
|
|
if err != nil {
|
|
t.Fatalf("read Rust HTTP marker %s: %v", markerPath, err)
|
|
}
|
|
if !bytes.Equal(got, []byte(rustHTTPMarker)) {
|
|
t.Fatalf("Rust HTTPS GET failed: marker=%q want=%q", got, rustHTTPMarker)
|
|
}
|
|
}
|
|
|
|
func assertRustSharedLibIsLoadable(t *testing.T, path string, goos string) {
|
|
t.Helper()
|
|
switch goos {
|
|
case "darwin":
|
|
file, err := macho.Open(path)
|
|
if err != nil {
|
|
t.Fatalf("open Rust Mach-O fixture: %v", err)
|
|
}
|
|
defer file.Close()
|
|
const (
|
|
machoSectionTypeMask = 0x000000ff
|
|
machoThreadLocalRegular = 0x11
|
|
machoThreadLocalInitFunctionPtrs = 0x15
|
|
)
|
|
for _, section := range file.Sections {
|
|
if section.Name == "__la_symbol_ptr" && section.Size != 0 {
|
|
t.Fatalf("Rust Mach-O fixture contains unsupported lazy bindings in %s", section.Name)
|
|
}
|
|
sectionType := section.Flags & machoSectionTypeMask
|
|
if strings.HasPrefix(section.Name, "__thread") ||
|
|
(sectionType >= machoThreadLocalRegular && sectionType <= machoThreadLocalInitFunctionPtrs) {
|
|
t.Fatalf("Rust Mach-O fixture contains unsupported TLS section %s (type=%#x)", section.Name, sectionType)
|
|
}
|
|
}
|
|
case "linux":
|
|
file, err := elf.Open(path)
|
|
if err != nil {
|
|
t.Fatalf("open Rust ELF fixture: %v", err)
|
|
}
|
|
defer file.Close()
|
|
for _, program := range file.Progs {
|
|
if program.Type == elf.PT_TLS && program.Memsz != 0 {
|
|
t.Fatalf("Rust ELF fixture contains unsupported PT_TLS segment (filesz=%d memsz=%d)", program.Filesz, program.Memsz)
|
|
}
|
|
}
|
|
case "windows":
|
|
file, err := pe.Open(path)
|
|
if err != nil {
|
|
t.Fatalf("open Rust PE fixture: %v", err)
|
|
}
|
|
defer file.Close()
|
|
const imageDirectoryEntryTLS = 9
|
|
var virtualAddress, size uint32
|
|
switch optional := file.OptionalHeader.(type) {
|
|
case *pe.OptionalHeader32:
|
|
virtualAddress = optional.DataDirectory[imageDirectoryEntryTLS].VirtualAddress
|
|
size = optional.DataDirectory[imageDirectoryEntryTLS].Size
|
|
case *pe.OptionalHeader64:
|
|
virtualAddress = optional.DataDirectory[imageDirectoryEntryTLS].VirtualAddress
|
|
size = optional.DataDirectory[imageDirectoryEntryTLS].Size
|
|
default:
|
|
t.Fatalf("Rust PE fixture has unsupported optional header %T", file.OptionalHeader)
|
|
}
|
|
if virtualAddress != 0 || size != 0 {
|
|
t.Fatalf("Rust PE fixture contains unsupported TLS directory (rva=%#x size=%d)", virtualAddress, size)
|
|
}
|
|
default:
|
|
t.Fatalf("unsupported Rust fixture OS validation: %s", goos)
|
|
}
|
|
}
|