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

     1  package x86
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strings"
     7  
     8  	"github.com/decomp/exp/bin"
     9  	"github.com/decomp/exp/disasm/x86"
    10  	"github.com/kr/pretty"
    11  	"github.com/llir/llvm/ir"
    12  	"github.com/llir/llvm/ir/constant"
    13  	"github.com/llir/llvm/ir/enum"
    14  	"github.com/llir/llvm/ir/metadata"
    15  	"github.com/llir/llvm/ir/types"
    16  	"github.com/llir/llvm/ir/value"
    17  	"golang.org/x/arch/x86/x86asm"
    18  )
    19  
    20  // === [ argument ] ============================================================
    21  
    22  // useArg returns the value held by the given argument, emitting code to f.
    23  func (f *Func) useArg(arg *x86.Arg) value.Value {
    24  	switch a := arg.Arg.(type) {
    25  	case x86asm.Reg:
    26  		reg := x86.NewReg(a, arg.Parent)
    27  		return f.useReg(reg)
    28  	case x86asm.Mem:
    29  		mem := x86.NewMem(a, arg.Parent)
    30  		return f.useMem(mem)
    31  	case x86asm.Imm:
    32  		return constant.NewInt(types.I32, int64(a))
    33  	case x86asm.Rel:
    34  		next := arg.Parent.Addr + bin.Address(arg.Parent.Len)
    35  		addr := next + bin.Address(a)
    36  		return f.useAddr(addr)
    37  	default:
    38  		panic(fmt.Errorf("support for argument type %T not yet implemented", arg.Arg))
    39  	}
    40  }
    41  
    42  // useArgElem returns a value of the specified element type held by the given
    43  // argument, emitting code to f.
    44  func (f *Func) useArgElem(arg *x86.Arg, elem types.Type) value.Value {
    45  	switch a := arg.Arg.(type) {
    46  	case x86asm.Reg:
    47  		reg := x86.NewReg(a, arg.Parent)
    48  		return f.useRegElem(reg, elem)
    49  	case x86asm.Mem:
    50  		mem := x86.NewMem(a, arg.Parent)
    51  		return f.useMemElem(mem, elem)
    52  	//case x86asm.Imm:
    53  	//case x86asm.Rel:
    54  	default:
    55  		panic(fmt.Errorf("support for argument type %T not yet implemented", arg))
    56  	}
    57  }
    58  
    59  // defArg stores the value to the given argument, emitting code to f.
    60  func (f *Func) defArg(arg *x86.Arg, v value.Value) {
    61  	switch a := arg.Arg.(type) {
    62  	case x86asm.Reg:
    63  		reg := x86.NewReg(a, arg.Parent)
    64  		f.defReg(reg, v)
    65  	case x86asm.Mem:
    66  		mem := x86.NewMem(a, arg.Parent)
    67  		f.defMem(mem, v)
    68  	//case x86asm.Imm:
    69  	//case x86asm.Rel:
    70  	default:
    71  		panic(fmt.Errorf("support for argument type %T not yet implemented", arg))
    72  	}
    73  }
    74  
    75  // defArgElem stores the value of the specified element type to the given
    76  // argument, emitting code to f.
    77  func (f *Func) defArgElem(arg *x86.Arg, v value.Value, elem types.Type) {
    78  	switch a := arg.Arg.(type) {
    79  	case x86asm.Reg:
    80  		reg := x86.NewReg(a, arg.Parent)
    81  		f.defRegElem(reg, v, elem)
    82  	case x86asm.Mem:
    83  		mem := x86.NewMem(a, arg.Parent)
    84  		f.defMemElem(mem, v, elem)
    85  	//case x86asm.Imm:
    86  	//case x86asm.Rel:
    87  	default:
    88  		panic(fmt.Errorf("support for argument type %T not yet implemented", arg))
    89  	}
    90  }
    91  
    92  // === [ register ] ============================================================
    93  
    94  // useReg loads and returns a value from the given x86 register, emitting code
    95  // to f.
    96  func (f *Func) useReg(reg *x86.Reg) value.Named {
    97  	src := f.reg(reg.Reg)
    98  	return f.cur.NewLoad(src)
    99  }
   100  
   101  // useRegElem loads and returns a value of the specified element type from the
   102  // given x86 register, emitting code to f.
   103  func (f *Func) useRegElem(reg *x86.Reg, elem types.Type) value.Value {
   104  	src := f.reg(reg.Reg)
   105  	typ := types.NewPointer(elem)
   106  	if !typ.Equal(src.Type()) {
   107  		src = f.cur.NewBitCast(src, typ)
   108  	}
   109  	return f.cur.NewLoad(src)
   110  }
   111  
   112  // defReg stores the value to the given x86 register, emitting code to f.
   113  func (f *Func) defReg(reg *x86.Reg, v value.Value) {
   114  	dst := f.reg(reg.Reg)
   115  	f.cur.NewStore(v, dst)
   116  	switch reg.Reg {
   117  	case x86asm.EAX, x86asm.EDX:
   118  		// Redefine the PSEUDO-register EDX:EAX based on change in EAX or EDX.
   119  		f.redefEDX_EAX()
   120  	}
   121  }
   122  
   123  // defRegElem stores the value of the specified element type to the given x86
   124  // register, emitting code to f.
   125  func (f *Func) defRegElem(reg *x86.Reg, v value.Value, elem types.Type) {
   126  	dst := f.reg(reg.Reg)
   127  	typ := types.NewPointer(elem)
   128  	if !typ.Equal(dst.Type()) {
   129  		dst = f.cur.NewBitCast(dst, typ)
   130  	}
   131  	f.cur.NewStore(v, dst)
   132  }
   133  
   134  // reg returns a pointer to the LLVM IR value associated with the given x86
   135  // register.
   136  func (f *Func) reg(reg x86asm.Reg) value.Value {
   137  	if v, ok := f.regs[reg]; ok {
   138  		return v
   139  	}
   140  	typ := regType(reg)
   141  	v := ir.NewAlloca(typ)
   142  	name := strings.ToLower(x86.Register(reg).String())
   143  	v.SetName(name)
   144  	f.regs[reg] = v
   145  	return v
   146  }
   147  
   148  // === [ memory reference ] ====================================================
   149  
   150  // useMem loads and returns the value of the given memory reference, emitting
   151  // code to f.
   152  func (f *Func) useMem(mem *x86.Mem) value.Named {
   153  	src := f.mem(mem)
   154  	v := f.cur.NewLoad(src)
   155  	if mem.Parent != nil && mem.Parent.MemBytes != 0 {
   156  		if t, ok := src.Type().(*types.PointerType); ok {
   157  			var indices []uint64
   158  			elemType := t.ElemType
   159  			for {
   160  				switch e := elemType.(type) {
   161  				case *types.ArrayType:
   162  					indices = append(indices, 0)
   163  					elemType = e.ElemType
   164  					continue
   165  				case *types.StructType:
   166  					indices = append(indices, 0)
   167  					elemType = e.Fields[0]
   168  					continue
   169  				}
   170  				break
   171  			}
   172  			if len(indices) > 0 {
   173  				return f.cur.NewExtractValue(v, indices...)
   174  			}
   175  		}
   176  	}
   177  	return v
   178  }
   179  
   180  // useMemElem loads and returns a value of the specified element type from the
   181  // given memory reference, emitting code to f.
   182  func (f *Func) useMemElem(mem *x86.Mem, elem types.Type) value.Value {
   183  	src := f.mem(mem)
   184  	typ := types.NewPointer(elem)
   185  	if !typ.Equal(src.Type()) {
   186  		src = f.cur.NewBitCast(src, typ)
   187  	}
   188  	return f.cur.NewLoad(src)
   189  }
   190  
   191  // defMem stores the value to the given memory reference, emitting code to f.
   192  func (f *Func) defMem(mem *x86.Mem, v value.Value) {
   193  	dst := f.mem(mem)
   194  	// Bitcast pointer to appropriate size.
   195  	dst = f.castToPtr(dst, mem.Parent)
   196  	f.cur.NewStore(v, dst)
   197  }
   198  
   199  // defMemElem stores the value of the specified element type to the given memory
   200  // reference, emitting code to f.
   201  func (f *Func) defMemElem(mem *x86.Mem, v value.Value, elem types.Type) {
   202  	dst := f.mem(mem)
   203  	typ := types.NewPointer(elem)
   204  	if !typ.Equal(dst.Type()) {
   205  		dst = f.cur.NewBitCast(dst, typ)
   206  	}
   207  	f.cur.NewStore(v, dst)
   208  }
   209  
   210  // mem returns a pointer to the LLVM IR value associated with the given memory
   211  // argument, emitting code to f.
   212  func (f *Func) mem(mem *x86.Mem) value.Value {
   213  	// Segment:[Base+Scale*Index+Disp].
   214  	var (
   215  		segment value.Value
   216  		base    value.Value
   217  		index   value.Value
   218  		disp    value.Value
   219  	)
   220  	if mem.Mem.Segment != 0 {
   221  		segment = f.useReg(mem.Segment())
   222  	}
   223  
   224  	// Parse Base register.
   225  	var rel bin.Address
   226  	switch mem.Mem.Base {
   227  	case 0:
   228  		// no base register.
   229  	case x86asm.IP, x86asm.EIP, x86asm.RIP:
   230  		// Handle IP-relative addressing; common in 64-bit x86.
   231  		rel = mem.Parent.Addr + bin.Address(mem.Parent.Len)
   232  	default:
   233  		base = f.useReg(mem.Base())
   234  	}
   235  	if mem.Mem.Index != 0 {
   236  		index = f.useReg(mem.Index())
   237  	}
   238  
   239  	// TODO: Add proper support for memory references.
   240  	//    Segment Reg
   241  	//    Base    Reg
   242  	//    Scale   uint8
   243  	//    Index   Reg
   244  	//    Disp    int64
   245  
   246  	// Handle local variables.
   247  	if segment == nil && index == nil {
   248  		// Stack local memory access.
   249  		switch mem.Mem.Base {
   250  		case x86asm.ESP, x86asm.EBP:
   251  			name := fmt.Sprintf("%s_%d", strings.ToLower(x86.Register(mem.Mem.Base).String()), f.espDisp+mem.Disp)
   252  			if v, ok := f.locals[name]; ok {
   253  				return v
   254  			}
   255  			v := ir.NewAlloca(types.I32)
   256  			v.SetName(name)
   257  			f.locals[name] = v
   258  			dbg.Printf("local %v of %q: %v\n", name, f.Ident(), v)
   259  			return v
   260  		}
   261  	}
   262  
   263  	// Handle disposition.
   264  	if mem.Disp != 0 {
   265  		if context, ok := f.l.Contexts[mem.Parent.Addr]; ok {
   266  			if c, ok := context.Args[mem.OpIndex]; ok {
   267  				if o, ok := c["Mem.offset"]; ok {
   268  					offset := o.Int64()
   269  					addr := rel + bin.Address(mem.Disp-offset)
   270  					v, ok := f.addr(addr)
   271  					if !ok {
   272  						panic(fmt.Errorf("unable to locate value at address %v; referenced from %v instruction at %v", addr, mem.Parent.Op, mem.Parent.Addr))
   273  					}
   274  					// TODO: Figure out how to handle negative offsets.
   275  					if offset < 0 {
   276  						disp = v
   277  					} else {
   278  						disp = f.getElementPtr(v, uint64(offset))
   279  					}
   280  				}
   281  			}
   282  		}
   283  		if disp == nil {
   284  			addr := rel + bin.Address(mem.Disp)
   285  			v, ok := f.addr(addr)
   286  			if !ok {
   287  				warn.Printf("unable to locate value at address %v; referenced from %v instruction at %v", addr, mem.Parent.Op, mem.Parent.Addr)
   288  			}
   289  			disp = v
   290  		}
   291  	}
   292  
   293  	// Early return for direct memory access.
   294  	if segment == nil && base == nil && index == nil {
   295  		if disp == nil {
   296  			addr := rel + bin.Address(mem.Disp)
   297  			// TODO: Remove once the lift library matures a bit.
   298  			warn.Printf("unknown global variable type at address %v; guessing i32", addr)
   299  			name := fmt.Sprintf("g_%06X", uint64(addr))
   300  			contentType := types.I32
   301  			typ := types.NewPointer(contentType)
   302  			g := &ir.Global{
   303  				Typ:         typ,
   304  				ContentType: contentType,
   305  				Init:        constant.NewZeroInitializer(contentType),
   306  			}
   307  			g.SetName(name)
   308  			md := &metadata.Attachment{
   309  				Name: "addr",
   310  				Node: &metadata.Tuple{
   311  					Fields: []metadata.Field{&metadata.String{Value: addr.String()}},
   312  				},
   313  			}
   314  			g.Metadata = append(g.Metadata, md)
   315  			// TODO: don't write to f.l from here as it should be read-only to
   316  			// allow for concurrent execution.
   317  			f.l.Globals[addr] = g
   318  			return g
   319  			panic(fmt.Errorf("unable to locate value at address %v; referenced from %v instruction at %v", addr, mem.Parent.Op, mem.Parent.Addr))
   320  		}
   321  		return disp
   322  	}
   323  
   324  	// TODO: Handle Segment.
   325  	src := disp
   326  	if segment != nil {
   327  		// Ignore segments for now, assume byte addressing.
   328  		//pretty.Println(mem)
   329  		//panic("support for memory reference segment not yet implemented")
   330  	}
   331  
   332  	// Handle Base.
   333  	if base != nil {
   334  		if src == nil {
   335  			src = base
   336  		} else {
   337  			src = f.castToPtr(src, mem.Parent)
   338  			indices := []value.Value{base}
   339  			src = f.cur.NewGetElementPtr(src, indices...)
   340  		}
   341  	}
   342  
   343  	// TODO: Handle Scale*Index.
   344  	if index != nil {
   345  		// TODO: Figure out how to handle scale. If we can validate that gep
   346  		// indexes into elements of size `scale`, the scale can be safely ignored.
   347  		if src == nil {
   348  			src = index
   349  		} else {
   350  			src = f.castToPtr(src, mem.Parent)
   351  			indices := []value.Value{index}
   352  			src = f.cur.NewGetElementPtr(src, indices...)
   353  		}
   354  	}
   355  
   356  	// Handle dynamic memory reference.
   357  	if src == nil {
   358  		pretty.Println(mem)
   359  		panic("unable to locate memory reference")
   360  	}
   361  
   362  	// TODO: Cast into proper type, once type analysis information is available.
   363  
   364  	// Force bitcast into pointer type.
   365  	return f.castToPtr(src, mem.Parent)
   366  }
   367  
   368  // castToPtr casts the given value into a pointer, where the element type is
   369  // derrived from src and instruction prefixes, with instruction prefix takes
   370  // precedence.
   371  func (f *Func) castToPtr(src value.Value, parent *x86.Inst) value.Value {
   372  	elem := src.Type()
   373  	var preBitSize uint64
   374  	if typ, ok := src.Type().(*types.PointerType); ok {
   375  		elem = typ.ElemType
   376  		if elem, ok := elem.(*types.IntType); ok {
   377  			preBitSize = elem.BitSize
   378  		}
   379  	}
   380  	// Derive element size from the parent instruction.
   381  	var bitSize uint64
   382  	if parent != nil {
   383  		if parent.MemBytes != 0 {
   384  			bitSize = uint64(parent.MemBytes) * 8
   385  		}
   386  		for _, prefix := range parent.Prefix[:] {
   387  			// The first zero in the array marks the end of the prefixes.
   388  			if prefix == 0 {
   389  				break
   390  			}
   391  			switch prefix &^ x86asm.PrefixImplicit {
   392  			case x86asm.PrefixData16:
   393  				bitSize = 16
   394  			case x86asm.PrefixREP, x86asm.PrefixREPN:
   395  				// nothing to do.
   396  			case x86asm.PrefixREX | x86asm.PrefixREXW:
   397  				// TODO: Implement support for REX.W
   398  			default:
   399  				panic(fmt.Errorf("support for prefix %v (0x%04X) not yet implemented", prefix, uint16(prefix)))
   400  			}
   401  		}
   402  	}
   403  	if bitSize != 0 {
   404  		elem = types.NewInt(bitSize)
   405  	}
   406  	needCast := !types.IsPointer(src.Type())
   407  	if bitSize != 0 && preBitSize != 0 && bitSize != preBitSize {
   408  		needCast = true
   409  	}
   410  	if needCast {
   411  		typ := types.NewPointer(elem)
   412  		var s string
   413  		if v, ok := src.(value.Named); ok {
   414  			if name := v.Name(); len(name) > 0 {
   415  				s = fmt.Sprintf(" %q", name)
   416  			}
   417  		}
   418  		dbg.Printf("casting%s to pointer type: %v", s, typ)
   419  		return f.cur.NewBitCast(src, typ)
   420  	}
   421  	return src
   422  }
   423  
   424  // === [ status flag ] =========================================================
   425  
   426  // StatusFlag represents the set of status flags.
   427  type StatusFlag uint
   428  
   429  // Status flags.
   430  const (
   431  	firstStatusFlag = CF
   432  
   433  	CF StatusFlag = iota // Carry Flag
   434  	PF                   // Parity Flag
   435  	AF                   // Auxiliary Carry Flag
   436  	ZF                   // Zero Flag
   437  	SF                   // Sign Flag
   438  	OF                   // Overflow Flag
   439  
   440  	lastStatusFlag = OF
   441  )
   442  
   443  // String returns the string representation of the status flag.
   444  func (status StatusFlag) String() string {
   445  	m := map[StatusFlag]string{
   446  		CF: "CF",
   447  		PF: "PF",
   448  		AF: "AF",
   449  		ZF: "ZF",
   450  		SF: "SF",
   451  		OF: "OF",
   452  	}
   453  	if s, ok := m[status]; ok {
   454  		return s
   455  	}
   456  	return fmt.Sprintf("unknown status flag %d", uint(status))
   457  }
   458  
   459  // useStatus loads and returns the value of the given x86 status flag, emitting
   460  // code to f.
   461  func (f *Func) useStatus(status StatusFlag) value.Value {
   462  	src := f.status(status)
   463  	return f.cur.NewLoad(src)
   464  }
   465  
   466  // defStatus stores the value to the given x86 status flag, emitting code to f.
   467  func (f *Func) defStatus(status StatusFlag, v value.Value) {
   468  	dst := f.status(status)
   469  	f.cur.NewStore(v, dst)
   470  }
   471  
   472  // status returns a pointer to the LLVM IR value associated with the given x86
   473  // status flag.
   474  func (f *Func) status(status StatusFlag) value.Value {
   475  	if v, ok := f.statusFlags[status]; ok {
   476  		return v
   477  	}
   478  	v := ir.NewAlloca(types.I1)
   479  	name := strings.ToLower(status.String())
   480  	v.SetName(name)
   481  	f.statusFlags[status] = v
   482  	return v
   483  }
   484  
   485  // === [ FPU status flag ] =====================================================
   486  
   487  // FStatusFlag represents the set of FPU status flags.
   488  type FStatusFlag uint
   489  
   490  // FPU status flags.
   491  const (
   492  	fpuFirstStatusFlag = Busy
   493  
   494  	Busy       FStatusFlag = iota // FPU Busy
   495  	C0                            // Condition Code 0
   496  	C1                            // Condition Code 1
   497  	C2                            // Condition Code 2
   498  	C3                            // Condition Code 3
   499  	ES                            // Exception Summary Status
   500  	StackFault                    // Stack Fault
   501  	// Exception Flags.
   502  	PE // Precision
   503  	UE // Underflow
   504  	OE // Overflow
   505  	ZE // Zero Divide
   506  	DE // Denormalized Operand
   507  	IE // Invalid Operation
   508  
   509  	fpuLastStatusFlag = IE
   510  )
   511  
   512  // String returns the string representation of the status flag.
   513  func (fstatus FStatusFlag) String() string {
   514  	m := map[FStatusFlag]string{
   515  		Busy:       "x87_B",
   516  		C0:         "x87_C0",
   517  		C1:         "x87_C1",
   518  		C2:         "x87_C2",
   519  		C3:         "x87_C3",
   520  		ES:         "x87_ES",
   521  		StackFault: "x87_SF",
   522  		PE:         "x87_PE",
   523  		UE:         "x87_UE",
   524  		OE:         "x87_OE",
   525  		ZE:         "x87_ZE",
   526  		DE:         "x87_DE",
   527  		IE:         "x87_IE",
   528  	}
   529  	if s, ok := m[fstatus]; ok {
   530  		return s
   531  	}
   532  	return fmt.Sprintf("unknown status flag %d", uint(fstatus))
   533  }
   534  
   535  // useFStatus loads and returns the value of the given x87 FPU status flag,
   536  // emitting code to f.
   537  func (f *Func) useFStatus(fstatus FStatusFlag) value.Value {
   538  	src := f.fstatus(fstatus)
   539  	return f.cur.NewLoad(src)
   540  }
   541  
   542  // defFStatus stores the value to the given x87 FPU status flag, emitting code
   543  // to f.
   544  func (f *Func) defFStatus(fstatus FStatusFlag, v value.Value) {
   545  	dst := f.fstatus(fstatus)
   546  	f.cur.NewStore(v, dst)
   547  }
   548  
   549  // fstatus returns a pointer to the LLVM IR value associated with the given x87
   550  // FPU status flag.
   551  func (f *Func) fstatus(fstatus FStatusFlag) value.Value {
   552  	if v, ok := f.fstatusFlags[fstatus]; ok {
   553  		return v
   554  	}
   555  	v := ir.NewAlloca(types.I1)
   556  	name := strings.ToLower(fstatus.String())
   557  	v.SetName(name)
   558  	f.fstatusFlags[fstatus] = v
   559  	return v
   560  }
   561  
   562  // === [ address ] =============================================================
   563  
   564  // useAddr loads and returns the value of the given address, emitting code to f.
   565  func (f *Func) useAddr(addr bin.Address) value.Named {
   566  	src, ok := f.addr(addr)
   567  	if !ok {
   568  		panic(fmt.Errorf("unable to locate value at address %v", addr))
   569  	}
   570  	return f.cur.NewLoad(src)
   571  }
   572  
   573  // defAddr stores the value to the given address, emitting code to f.
   574  func (f *Func) defAddr(addr bin.Address, v value.Value) {
   575  	dst, ok := f.addr(addr)
   576  	if !ok {
   577  		panic(fmt.Errorf("unable to locate value at address %v", addr))
   578  	}
   579  	f.cur.NewStore(v, dst)
   580  }
   581  
   582  // addr returns a pointer to the LLVM IR value associated with the given
   583  // address, emitting code to f. The returned value is one of *ir.BasicBlock,
   584  // *ir.Global and *ir.Function, and the boolean value indicates success
   585  func (f *Func) addr(addr bin.Address) (value.Named, bool) {
   586  	if block, ok := f.blocks[addr]; ok {
   587  		return block, true
   588  	}
   589  	// Direct or indirect access to global variable.
   590  	if g, ok := f.global(addr); ok {
   591  		return g, true
   592  	}
   593  	if fn, ok := f.l.Funcs[addr]; ok {
   594  		return fn.Func, true
   595  	}
   596  	// TODO: Add support for lookup of more globally addressable values.
   597  	return nil, false
   598  }
   599  
   600  // global returns a pointer to the LLVM IR value associated with the given
   601  // global variable address, and a boolean value indicating success.
   602  func (f *Func) global(addr bin.Address) (value.Named, bool) {
   603  	// Early return if direct access to global variable.
   604  	if src, ok := f.l.Globals[addr]; ok {
   605  		return src, true
   606  	}
   607  
   608  	// Use binary search if indirect access to global variable (e.g. struct
   609  	// field, array element).
   610  	var globalAddrs []bin.Address
   611  	for globalAddr := range f.l.Globals {
   612  		globalAddrs = append(globalAddrs, globalAddr)
   613  	}
   614  	sort.Sort(bin.Addresses(globalAddrs))
   615  	less := func(i int) bool {
   616  		return addr < globalAddrs[i]
   617  	}
   618  	index := sort.Search(len(globalAddrs), less)
   619  	index--
   620  	if 0 <= index && index < len(globalAddrs) {
   621  		start := globalAddrs[index]
   622  		g := f.l.Globals[start]
   623  		size := f.l.sizeOfType(g.Typ.ElemType)
   624  		end := start + bin.Address(size)
   625  		if start <= addr && addr < end {
   626  			offset := uint64(addr - start)
   627  			return f.getElementPtr(g, offset), true
   628  		}
   629  	}
   630  	return nil, false
   631  }
   632  
   633  // ### [ helpers ] #############################################################
   634  
   635  // getAddr returns the static address represented by the given argument, and a
   636  // boolean indicating success.
   637  func (f *Func) getAddr(arg *x86.Arg) (bin.Address, bool) {
   638  	switch a := arg.Arg.(type) {
   639  	case x86asm.Reg:
   640  		if context, ok := f.l.Contexts[arg.Parent.Addr]; ok {
   641  			if c, ok := context.Regs[x86.Register(a)]; ok {
   642  				if addr, ok := c["addr"]; ok {
   643  					return addr.Addr(), true
   644  				}
   645  				panic(fmt.Errorf("support for register context `%v` not yet implemented", c))
   646  			}
   647  		}
   648  	case x86asm.Rel:
   649  		next := arg.Parent.Addr + bin.Address(arg.Parent.Len)
   650  		addr := next + bin.Address(a)
   651  		return addr, true
   652  	case x86asm.Mem:
   653  		if a.Segment == 0 && a.Base == 0 && a.Scale == 0 && a.Index == 0 {
   654  			return bin.Address(a.Disp), true
   655  		}
   656  	}
   657  	return 0, false
   658  }
   659  
   660  // getFunc resolves the function, function type, and calling convention of the
   661  // given argument. The boolean return value indicates success.
   662  func (f *Func) getFunc(arg *x86.Arg) (value.Named, *types.FuncType, enum.CallingConv, bool) {
   663  	// Check if register symbol context present.
   664  	switch a := arg.Arg.(type) {
   665  	case x86asm.Reg:
   666  		if context, ok := f.l.Contexts[arg.Parent.Addr]; ok {
   667  			if c, ok := context.Regs[x86.Register(a)]; ok {
   668  				if symbol, ok := c["symbol"]; ok {
   669  					fname := symbol.String()
   670  					fn, ok := f.l.FuncByName[fname]
   671  					if !ok {
   672  						panic(fmt.Errorf("unable to locate external function %q", fname))
   673  					}
   674  					return fn, fn.Sig, fn.CallingConv, true
   675  				}
   676  				// TODO: Remove poor man's type propagation once the type analysis and
   677  				// data flow analysis phases have been properly implemented.
   678  				if param, ok := c["param"]; ok {
   679  					p := param.Int64()
   680  					if p >= int64(len(f.Params)) {
   681  						panic(fmt.Errorf("invalid function parameter index; expected < %d, got %d", len(f.Params), p))
   682  					}
   683  					v := f.Params[p]
   684  					typ := v.Type()
   685  					ptr, ok := typ.(*types.PointerType)
   686  					if !ok {
   687  						panic(fmt.Errorf("invalid function pointer type of function parameter %q referenced from instruction at address %v; expected *types.PointerType, got %T; ", f.Params[p].Ident(), arg.Parent.Addr, typ))
   688  					}
   689  					sig, ok := ptr.ElemType.(*types.FuncType)
   690  					if !ok {
   691  						panic(fmt.Errorf("invalid function type of function parameter %q referenced from instruction at address %v; expected *types.FuncType, got %T; ", f.Params[p].Ident(), arg.Parent.Addr, ptr.ElemType))
   692  					}
   693  					// TODO: Figure out how to recover calling convention.
   694  					// Perhaps through context.json at call sites?
   695  					return v, sig, enum.CallingConvNone, true
   696  				}
   697  			}
   698  		}
   699  	}
   700  
   701  	if addr, ok := f.getAddr(arg); ok {
   702  		if fn, ok := f.l.Funcs[addr]; ok {
   703  			v := fn.Func
   704  			return v, v.Sig, v.CallingConv, true
   705  		}
   706  		if g, ok := f.l.Globals[addr]; ok {
   707  			ptr, ok := g.Typ.ElemType.(*types.PointerType)
   708  			if !ok {
   709  				panic(fmt.Errorf("invalid function pointer type of global variable at address %v referenced from instruction at address %v; expected *types.PointerType, got %T; ", addr, arg.Parent.Addr, g.Typ.ElemType))
   710  			}
   711  			sig, ok := ptr.ElemType.(*types.FuncType)
   712  			if !ok {
   713  				panic(fmt.Errorf("invalid function type of global variable at address %v referenced from instruction at address %v; expected *types.FuncType, got %T; ", addr, arg.Parent.Addr, ptr.ElemType))
   714  			}
   715  			v := f.cur.NewLoad(g)
   716  			// TODO: Figure out how to recover calling convention.
   717  			// Perhaps through context.json at call sites?
   718  			return v, sig, enum.CallingConvNone, true
   719  		}
   720  		panic(fmt.Errorf("unable to locate function at address %v referenced from instruction at address %v", addr, arg.Parent.Addr))
   721  	}
   722  
   723  	// Handle function pointers in structures.
   724  	switch a := arg.Arg.(type) {
   725  	case x86asm.Mem:
   726  		if a.Base != 0 {
   727  			context, ok := f.l.Contexts[arg.Parent.Addr]
   728  			if !ok {
   729  				pretty.Println(arg.Arg)
   730  				panic(fmt.Errorf("unable to locate context for %v register used at %v", a.Base, arg.Parent.Addr))
   731  			}
   732  			if c, ok := context.Regs[x86.Register(a.Base)]; ok {
   733  				if typStr, ok := c["type"]; ok {
   734  					typ := f.l.parseType(typStr.String())
   735  					dbg.Println("context type:", typ)
   736  					reg := f.reg(a.Base)
   737  					var v value.Named = f.cur.NewBitCast(reg, typ)
   738  					v = f.cur.NewLoad(v)
   739  					// TODO: Figure out how to handle negative offsets.
   740  					v = f.getElementPtr(v, uint64(a.Disp))
   741  					v = f.cur.NewLoad(v)
   742  					if typ, ok := v.Type().(*types.PointerType); ok {
   743  						if sig := typ.ElemType.(*types.FuncType); ok {
   744  							// TODO: Figure out how to recover calling convention.
   745  							// Perhaps through context.json at call sites?
   746  							return v, sig, enum.CallingConvNone, true
   747  						}
   748  					}
   749  					panic(fmt.Errorf("invalid callee type; expected pointer to function type, got %v", v.Type()))
   750  				}
   751  				if addr, ok := c["addr"]; ok {
   752  					v := f.useAddr(addr.Addr())
   753  					// HACK: Remove once proper type and data flow analysis has been
   754  					// implemented.
   755  					if extractvalue, ok := c["extractvalue"]; ok && extractvalue.Bool() {
   756  						dbg.Println("extractvalue:", v)
   757  						dbg.Println("extractvalue.Type():", v.Type())
   758  						// TODO: Handle index based on Index regster if present.
   759  						v = f.cur.NewExtractValue(v, 0)
   760  					}
   761  					// TODO: Figure out how to handle negative offsets.
   762  					v = f.getElementPtr(v, uint64(a.Disp))
   763  					v = f.cur.NewLoad(v)
   764  					if typ, ok := v.Type().(*types.PointerType); ok {
   765  						if sig := typ.ElemType.(*types.FuncType); ok {
   766  							// TODO: Figure out how to recover calling convention.
   767  							// Perhaps through context.json at call sites?
   768  							return v, sig, enum.CallingConvNone, true
   769  						}
   770  					}
   771  					panic(fmt.Errorf("invalid callee type; expected pointer to function type, got %v", v.Type()))
   772  				}
   773  				if min, ok := c["min"]; ok {
   774  					addr := bin.Address(a.Disp + min.Int64())
   775  					v := f.useAddr(addr)
   776  					if typ, ok := v.Type().(*types.PointerType); ok {
   777  						if sig := typ.ElemType.(*types.FuncType); ok {
   778  							// TODO: Figure out how to recover calling convention.
   779  							// Perhaps through context.json at call sites?
   780  							return v, sig, enum.CallingConvNone, true
   781  						}
   782  					}
   783  					panic(fmt.Errorf("invalid callee type; expected pointer to function type, got %v", v.Type()))
   784  				}
   785  			}
   786  
   787  			if c, ok := context.Args[arg.OpIndex]; ok {
   788  				// TODO: Remove poor man's type propagation once the type analysis and
   789  				// data flow analysis phases have been properly implemented.
   790  				if param, ok := c["param"]; ok {
   791  					p := param.Int64()
   792  					if p >= int64(len(f.Params)) {
   793  						panic(fmt.Errorf("invalid function parameter index; expected < %d, got %d", len(f.Params), p))
   794  					}
   795  					v := f.Params[p]
   796  					typ := v.Type()
   797  					ptr, ok := typ.(*types.PointerType)
   798  					if !ok {
   799  						panic(fmt.Errorf("invalid function pointer type of function parameter %q referenced from instruction at address %v; expected *types.PointerType, got %T; ", f.Params[p].Ident(), arg.Parent.Addr, typ))
   800  					}
   801  					sig, ok := ptr.ElemType.(*types.FuncType)
   802  					if !ok {
   803  						panic(fmt.Errorf("invalid function type of function parameter %q referenced from instruction at address %v; expected *types.FuncType, got %T; ", f.Params[p].Ident(), arg.Parent.Addr, ptr.ElemType))
   804  					}
   805  					// TODO: Figure out how to recover calling convention.
   806  					// Perhaps through context.json at call sites?
   807  					return v, sig, enum.CallingConvNone, true
   808  				}
   809  			}
   810  
   811  		}
   812  		if a.Index != 0 {
   813  			context, ok := f.l.Contexts[arg.Parent.Addr]
   814  			if !ok {
   815  				pretty.Println(arg.Arg)
   816  				panic(fmt.Errorf("unable to locate context for %v register used at %v", a.Index, arg.Parent.Addr))
   817  			}
   818  			if c, ok := context.Regs[x86.Register(a.Index)]; ok {
   819  				if min, ok := c["min"]; ok {
   820  					addr := bin.Address(a.Disp + int64(a.Scale)*min.Int64())
   821  					v := f.useAddr(addr)
   822  					if typ, ok := v.Type().(*types.PointerType); ok {
   823  						if sig := typ.ElemType.(*types.FuncType); ok {
   824  							// TODO: Figure out how to recover calling convention.
   825  							// Perhaps through context.json at call sites?
   826  							return v, sig, enum.CallingConvNone, true
   827  						}
   828  					}
   829  					// HACK: Use gep as a fallback for 0 element offsets.
   830  					fallback, ok := f.addr(addr)
   831  					if !ok {
   832  						panic(fmt.Sprintf("unable to locate variable associated with address %v", addr))
   833  					}
   834  					fallback = f.getElementPtr(fallback, 0)
   835  					v = f.cur.NewLoad(fallback)
   836  					if typ, ok := v.Type().(*types.PointerType); ok {
   837  						if sig := typ.ElemType.(*types.FuncType); ok {
   838  							// TODO: Figure out how to recover calling convention.
   839  							// Perhaps through context.json at call sites?
   840  							return v, sig, enum.CallingConvNone, true
   841  						}
   842  					}
   843  					panic(fmt.Errorf("invalid callee type; expected pointer to function type, got %v", v.Type()))
   844  				}
   845  			}
   846  		}
   847  	}
   848  
   849  	dbg.Printf("unable to locate function for argument %v of instruction at address %v\n", arg.Arg, arg.Parent.Addr)
   850  	switch a := arg.Arg.(type) {
   851  	case x86asm.Rel:
   852  		next := arg.Parent.Addr + bin.Address(arg.Parent.Len)
   853  		addr := next + bin.Address(a)
   854  		dbg.Println("   addr:", addr)
   855  	case x86asm.Mem:
   856  		addr := bin.Address(a.Disp)
   857  		dbg.Println("   addr:", addr)
   858  	}
   859  	panic("not yet implemented")
   860  }
   861  
   862  // redefEDX_EAX redefines the 64-bit pseudo-register EDX:EAX based on the value
   863  // of EAX and EDX.
   864  func (f *Func) redefEDX_EAX() {
   865  	if !f.usesEDX_EAX {
   866  		return
   867  	}
   868  	edx := f.useReg(x86.EDX)
   869  	eax := f.useReg(x86.EAX)
   870  	tmp1 := f.cur.NewSExt(edx, types.I64)
   871  	tmp2 := f.cur.NewZExt(eax, types.I64)
   872  	tmp := f.cur.NewOr(tmp1, tmp2)
   873  	f.defReg(x86.EDX_EAX, tmp)
   874  }