Skip to content

Commit

Permalink
cmd/objdump: support reading from a pipe
Browse files Browse the repository at this point in the history
It is convenient if the objdump supports reading from a pipe. I modified `cmd/internal/objfile.Open`, it would check file at first, dumps it to a temporary file if it is not seekable, and open tmp file.

For golang#41051

Change-Id: I4bf47c1b9722aee03a54147a7aa8dd44529ada89
  • Loading branch information
ethe committed Sep 11, 2020
1 parent dfdc388 commit 3db4365
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 23 deletions.
29 changes: 29 additions & 0 deletions src/cmd/internal/objfile/objfile.go
Expand Up @@ -10,8 +10,10 @@ import (
"debug/gosym"
"fmt"
"io"
"io/ioutil"
"os"
"sort"
"strings"
)

type rawFile interface {
Expand Down Expand Up @@ -64,13 +66,40 @@ var openers = []func(io.ReaderAt) (rawFile, error){
openXcoff,
}

func trySeekOrDump(f *os.File) (*os.File, error) {
_, err := f.Seek(0, 0)
if err == nil {
return f, nil
}
if !strings.Contains(err.Error(), "illegal seek") {
return nil, err
}
defer f.Close()

tempFile, err := ioutil.TempFile(os.TempDir(), "go-objfile")
if err != nil {
return nil, err
}

if _, err := io.Copy(tempFile, f); err != nil {
return nil, err
}
if _, err := tempFile.Seek(0, io.SeekStart); err != nil {
return nil, err
}
return tempFile, nil
}

// Open opens the named file.
// The caller must call f.Close when the file is no longer needed.
func Open(name string) (*File, error) {
r, err := os.Open(name)
if err != nil {
return nil, err
}
if r, err = trySeekOrDump(r); err != nil {
return nil, err
}
if f, err := openGoFile(r); err == nil {
return f, nil
}
Expand Down
103 changes: 80 additions & 23 deletions src/cmd/objdump/objdump_test.go
Expand Up @@ -283,6 +283,36 @@ func TestDisasmExtld(t *testing.T) {
testDisasm(t, "fmthello.go", false, false, "-ldflags=-linkmode=external")
}

func buildTestData(t *testing.T) string {
hello := filepath.Join(tmp, "hello.o")
args := []string{"tool", "compile", "-o", hello}
args = append(args, "testdata/fmthello.go")
out, err := exec.Command(testenv.GoToolPath(t), args...).CombinedOutput()
if err != nil {
t.Fatalf("go tool compile fmthello.go: %v\n%s", err, out)
}
return hello
}

func validateOutput(t *testing.T, text string, need []string) {
ok := true
for _, s := range need {
if !strings.Contains(text, s) {
t.Errorf("disassembly missing '%s'", s)
ok = false
}
}
if runtime.GOARCH == "386" {
if strings.Contains(text, "(IP)") {
t.Errorf("disassembly contains PC-Relative addressing on 386")
ok = false
}
}
if !ok {
t.Logf("full disassembly:\n%s", text)
}
}

func TestDisasmGoobj(t *testing.T) {
switch runtime.GOARCH {
case "mips", "mipsle", "mips64", "mips64le":
Expand All @@ -293,43 +323,70 @@ func TestDisasmGoobj(t *testing.T) {
t.Skipf("skipping on %s, issue 15255", runtime.GOARCH)
}

hello := filepath.Join(tmp, "hello.o")
args := []string{"tool", "compile", "-o", hello}
args = append(args, "testdata/fmthello.go")
out, err := exec.Command(testenv.GoToolPath(t), args...).CombinedOutput()
if err != nil {
t.Fatalf("go tool compile fmthello.go: %v\n%s", err, out)
}
hello := buildTestData(t)
need := []string{
"main(SB)",
"fmthello.go:6",
}

args = []string{
args := []string{
"-s", "main",
hello,
}

out, err = exec.Command(exe, args...).CombinedOutput()
out, err := exec.Command(exe, args...).CombinedOutput()
if err != nil {
t.Fatalf("objdump fmthello.o: %v\n%s", err, out)
}

text := string(out)
ok := true
for _, s := range need {
if !strings.Contains(text, s) {
t.Errorf("disassembly missing '%s'", s)
ok = false
}
validateOutput(t, string(out), need)
}

func TestDisasmGoobjFromStdin(t *testing.T) {
switch runtime.GOARCH {
case "mips", "mipsle", "mips64", "mips64le":
t.Skipf("skipping on %s, issue 12559", runtime.GOARCH)
case "riscv64":
t.Skipf("skipping on %s, issue 36738", runtime.GOARCH)
case "s390x":
t.Skipf("skipping on %s, issue 15255", runtime.GOARCH)
}
if runtime.GOARCH == "386" {
if strings.Contains(text, "(IP)") {
t.Errorf("disassembly contains PC-Relative addressing on 386")
ok = false
}

hello := buildTestData(t)
need := []string{
"main(SB)",
"fmthello.go:6",
}
if !ok {
t.Logf("full disassembly:\n%s", text)

args := []string{
"-s", "main", "/dev/stdin",
}

cmd := exec.Command(exe, args...)
input, err := cmd.StdinPipe()
if err != nil {
t.Fatalf("get stdin pipe error: %v\n", err)
}
f, err := os.Open(hello)
if err != nil {
t.Fatalf("open output file error: %v\n", err)
}
bytes, err := ioutil.ReadAll(f)
if err != nil {
t.Fatalf("read output file error: %v\n", err)
}
_, err = input.Write(bytes)
if err != nil {
t.Fatalf("write stdin pipe error: %v\n", err)
}
err = input.Close()
if err != nil {
t.Fatalf("close stdin pipe error: %v\n", err)
}
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("objdump /dev/stdin: %v\n%s", err, out)
}

validateOutput(t, string(out), need)
}

0 comments on commit 3db4365

Please sign in to comment.