github.com/decomp/exp@v0.0.0-20210624183419-6d058f5e1da6/lift/x86/lift_test.go (about)

     1  package x86
     2  
     3  import (
     4  	"io/ioutil"
     5  	"log"
     6  	"os"
     7  	"path/filepath"
     8  	"testing"
     9  
    10  	"github.com/decomp/exp/bin"
    11  	_ "github.com/decomp/exp/bin/elf" // register ELF decoder
    12  	_ "github.com/decomp/exp/bin/pe"  // register PE decoder
    13  	_ "github.com/decomp/exp/bin/pef" // register PEF decoder
    14  	"github.com/decomp/exp/bin/raw"
    15  	"github.com/llir/llvm/ir"
    16  	"github.com/mewkiz/pkg/diffutil"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  func TestLift(t *testing.T) {
    21  	golden := []struct {
    22  		// Base directory; which may contain decomp JSON files.
    23  		dir string
    24  		// Path to input binary executable or object file.
    25  		in string
    26  		// Path to output LLVM IR assembly file.
    27  		out string
    28  		// Raw machine architecture; or 0 if any format other than raw.
    29  		arch bin.Arch
    30  	}{
    31  		// File formats.
    32  		//
    33  		//   * .bin  - raw executable files
    34  		//   * .o    - ELF object files
    35  		//   * .so   - ELF shared object files
    36  		//   * .out  - ELF executable files
    37  		//   * .coff - COFF object files
    38  		{dir: "testdata/x86_32/format", in: "format.bin", out: "format_bin.ll", arch: bin.ArchX86_32},
    39  		{dir: "testdata/x86_32/format", in: "format_elf.o", out: "format_o.ll"},
    40  		{dir: "testdata/x86_32/format", in: "format_elf.so", out: "format_so.ll"},
    41  		{dir: "testdata/x86_32/format", in: "format_elf.out", out: "format_out.ll"},
    42  		{dir: "testdata/x86_64/format", in: "format.bin", out: "format_bin.ll", arch: bin.ArchX86_32},
    43  		{dir: "testdata/x86_64/format", in: "format_elf.o", out: "format_o.ll"},
    44  		{dir: "testdata/x86_64/format", in: "format_elf.so", out: "format_so.ll"},
    45  		{dir: "testdata/x86_64/format", in: "format_elf.out", out: "format_out.ll"},
    46  		// TODO: Add support for COFF files.
    47  		//{in: "testdata/format.coff", out: "testdata/format_coff.ll"},
    48  
    49  		// Arithmetic instructions.
    50  		{dir: "testdata/x86_32/arithmetic", in: "arithmetic.so", out: "arithmetic.ll"},
    51  		{dir: "testdata/x86_64/arithmetic", in: "arithmetic.so", out: "arithmetic.ll"},
    52  
    53  		// Import functions from dynamic libraries.
    54  		{dir: "testdata/x86_32/import", in: "import.out", out: "import.ll"},
    55  		{dir: "testdata/x86_64/import", in: "import.out", out: "import.ll"},
    56  
    57  		// === [ FPU instructions ] ==============================================
    58  		//
    59  		// --- [ x87 FPU Data Transfer Instructions ] ----------------------------
    60  		//
    61  		//    * FBLD
    62  		//    * FBSTP
    63  		//    * FILD
    64  		{dir: "testdata/x86_32/fpu/fild", in: "fild.so", out: "fild.ll"},
    65  		{dir: "testdata/x86_64/fpu/fild", in: "fild.so", out: "fild.ll"},
    66  		//    * FIST
    67  		//    * FISTP
    68  		//    * FLD
    69  		{dir: "testdata/x86_32/fpu/fld", in: "fld.so", out: "fld.ll"},
    70  		{dir: "testdata/x86_64/fpu/fld", in: "fld.so", out: "fld.ll"},
    71  		//    * FST
    72  		//    * FSTP
    73  		//    * FXCH
    74  		//
    75  		// ___ [ FCMOVcc - Floating-Point Conditional Move Instructions ] ________
    76  		//
    77  		//    * FCMOVB
    78  		//    * FCMOVBE
    79  		//    * FCMOVE
    80  		//    * FCMOVNB
    81  		//    * FCMOVNBE
    82  		//    * FCMOVNE
    83  		//    * FCMOVNU
    84  		//    * FCMOVU
    85  		//
    86  		// --- [ x87 FPU Load Constants Instructions ] ---------------------------
    87  		//
    88  		//    * FLD1
    89  		{dir: "testdata/x86_32/fpu/fld1", in: "fld1.so", out: "fld1.ll"},
    90  		{dir: "testdata/x86_64/fpu/fld1", in: "fld1.so", out: "fld1.ll"},
    91  		//    * FLDL2E
    92  		{dir: "testdata/x86_32/fpu/fldl2e", in: "fldl2e.so", out: "fldl2e.ll"},
    93  		{dir: "testdata/x86_64/fpu/fldl2e", in: "fldl2e.so", out: "fldl2e.ll"},
    94  		//    * FLDL2T
    95  		{dir: "testdata/x86_32/fpu/fldl2t", in: "fldl2t.so", out: "fldl2t.ll"},
    96  		{dir: "testdata/x86_64/fpu/fldl2t", in: "fldl2t.so", out: "fldl2t.ll"},
    97  		//    * FLDLG2
    98  		{dir: "testdata/x86_32/fpu/fldlg2", in: "fldlg2.so", out: "fldlg2.ll"},
    99  		{dir: "testdata/x86_64/fpu/fldlg2", in: "fldlg2.so", out: "fldlg2.ll"},
   100  		//    * FLDLN2
   101  		{dir: "testdata/x86_32/fpu/fldln2", in: "fldln2.so", out: "fldln2.ll"},
   102  		{dir: "testdata/x86_64/fpu/fldln2", in: "fldln2.so", out: "fldln2.ll"},
   103  		//    * FLDPI
   104  		{dir: "testdata/x86_32/fpu/fldpi", in: "fldpi.so", out: "fldpi.ll"},
   105  		{dir: "testdata/x86_64/fpu/fldpi", in: "fldpi.so", out: "fldpi.ll"},
   106  		//    * FLDZ
   107  		{dir: "testdata/x86_32/fpu/fldz", in: "fldz.so", out: "fldz.ll"},
   108  		{dir: "testdata/x86_64/fpu/fldz", in: "fldz.so", out: "fldz.ll"},
   109  	}
   110  	wd, err := os.Getwd()
   111  	if err != nil {
   112  		t.Fatalf("unable to retrieve current working directory; %+v", err)
   113  	}
   114  	for _, g := range golden {
   115  		in := filepath.Join(g.dir, g.in)
   116  		log.Printf("testing: %q", in)
   117  		if err := os.Chdir(wd); err != nil {
   118  			t.Errorf("%q: unable to change working directory; %+v", in, err)
   119  			continue
   120  		}
   121  		if err := os.Chdir(g.dir); err != nil {
   122  			t.Errorf("%q: unable to change working directory; %+v", in, err)
   123  			continue
   124  		}
   125  		l, err := newLifter(g.in, g.arch)
   126  		if err != nil {
   127  			t.Errorf("%q: unable to prepare lifter; %+v", in, err)
   128  			continue
   129  		}
   130  
   131  		// Create function lifters.
   132  		for _, funcAddr := range l.FuncAddrs {
   133  			asmFunc, err := l.DecodeFunc(funcAddr)
   134  			if err != nil {
   135  				t.Errorf("%q: unable to decode function; %+v", in, err)
   136  				continue
   137  			}
   138  			f := l.NewFunc(asmFunc)
   139  			l.Funcs[funcAddr] = f
   140  		}
   141  
   142  		// Lift functions.
   143  		module := &ir.Module{}
   144  		for _, funcAddr := range l.FuncAddrs {
   145  			f, ok := l.Funcs[funcAddr]
   146  			if !ok {
   147  				continue
   148  			}
   149  			f.Lift()
   150  			module.Funcs = append(module.Funcs, f.Func)
   151  		}
   152  		buf, err := ioutil.ReadFile(g.out)
   153  		if err != nil {
   154  			t.Errorf("%q: unable to read file: %+v", in, err)
   155  			continue
   156  		}
   157  		got := module.String()
   158  		want := string(buf)
   159  		if got != want {
   160  			diffutil.Diff(want, got, false, g.in)
   161  			t.Errorf("%q: module mismatch; expected `%v`, got `%v`", in, want, got)
   162  			continue
   163  		}
   164  	}
   165  }
   166  
   167  // newLifter returns a new x86 to LLVM IR lifter for the given binary
   168  // executable.
   169  func newLifter(path string, arch bin.Arch) (*Lifter, error) {
   170  	if arch != 0 {
   171  		file, err := raw.ParseFile(path, arch)
   172  		if err != nil {
   173  			return nil, errors.WithStack(err)
   174  		}
   175  		return NewLifter(file)
   176  	}
   177  	file, err := bin.ParseFile(path)
   178  	if err != nil {
   179  		return nil, errors.WithStack(err)
   180  	}
   181  	return NewLifter(file)
   182  }