github.com/Rookout/GoSDK@v0.1.48/pkg/services/assembler/builder.go (about)

     1  package assembler
     2  
     3  import (
     4  	"fmt"
     5  	"runtime"
     6  
     7  	"github.com/Rookout/GoSDK/pkg/rookoutErrors"
     8  	"github.com/Rookout/GoSDK/pkg/services/assembler/internal/asm/arch"
     9  	"github.com/Rookout/GoSDK/pkg/services/assembler/internal/obj"
    10  )
    11  
    12  var a, ctxt = func() (*arch.Arch, *obj.Link) {
    13  	a := arch.Set(runtime.GOARCH, false)
    14  	ctxt := obj.Linknew(a.LinkArch)
    15  	ctxt.DiagFunc = func(in string, args ...interface{}) {
    16  		panic(fmt.Sprintf(in, args...))
    17  	}
    18  	a.Init(ctxt)
    19  	return a, ctxt
    20  }()
    21  
    22  
    23  type Builder struct {
    24  	arch *arch.Arch
    25  
    26  	first *Instruction
    27  	last  *Instruction
    28  
    29  	
    30  	labels map[string]*Instruction
    31  }
    32  
    33  type Instruction struct {
    34  	*obj.Prog
    35  	label          string
    36  	jumpDestLabel  string
    37  	link           *Instruction
    38  	placeholderFor []byte
    39  }
    40  
    41  
    42  func (b *Builder) NewInstruction() *Instruction {
    43  	return &Instruction{Prog: ctxt.NewProg()}
    44  }
    45  
    46  func (b *Builder) insertInstruction(inst *Instruction) {
    47  	if b.first == nil {
    48  		b.first = inst
    49  		b.last = inst
    50  	} else {
    51  		b.last.link = inst
    52  		b.last = inst
    53  	}
    54  }
    55  
    56  
    57  
    58  func (b *Builder) AddInstructions(p *Instruction, instructions ...*Instruction) rookoutErrors.RookoutError {
    59  	insts := []*Instruction{p}
    60  	insts = append(insts, instructions...)
    61  
    62  	for _, inst := range insts {
    63  		if inst.label != "" {
    64  			if _, ok := b.labels[inst.label]; ok {
    65  				return rookoutErrors.NewLabelAlreadyExists(inst.label)
    66  			}
    67  
    68  			b.labels[inst.label] = inst
    69  		}
    70  
    71  		b.insertInstruction(inst)
    72  	}
    73  
    74  	return nil
    75  }
    76  
    77  func (b *Builder) fixup() rookoutErrors.RookoutError {
    78  	for inst := b.first; inst != nil; inst = inst.link {
    79  		if inst.link != nil {
    80  			inst.Prog.Link = inst.link.Prog
    81  		}
    82  
    83  		if inst.placeholderFor != nil {
    84  			if b.arch.Arch.Name == "arm64" && len(inst.placeholderFor)%4 != 0 {
    85  				return rookoutErrors.NewInvalidBytes(inst.placeholderFor)
    86  			}
    87  
    88  			progAfterPlaceholder := inst.Prog.Link
    89  			curProg := inst.Prog
    90  			
    91  			for i := 0; i < len(inst.placeholderFor)-placeholderLen; i += placeholderLen {
    92  				curProg.Link = b.placeholderProg()
    93  				curProg = curProg.Link
    94  			}
    95  			curProg.Link = progAfterPlaceholder
    96  			continue
    97  		}
    98  
    99  		if inst.jumpDestLabel != "" {
   100  			dest, ok := b.labels[inst.jumpDestLabel]
   101  			if !ok {
   102  				return rookoutErrors.NewInvalidJumpDest(inst.jumpDestLabel)
   103  			}
   104  
   105  			inst.To.Val = dest.Prog
   106  		}
   107  	}
   108  
   109  	return nil
   110  }
   111  
   112  func (b *Builder) replacePlaceholders(assembled []byte) {
   113  	for inst := b.first; inst != nil; inst = inst.link {
   114  		if inst.placeholderFor != nil {
   115  			copy(assembled[inst.Pc:], inst.placeholderFor)
   116  		}
   117  	}
   118  }
   119  
   120  
   121  func (b *Builder) Assemble() ([]byte, rookoutErrors.RookoutError) {
   122  	err := b.fixup()
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  
   127  	var funcInfo interface{} = &obj.FuncInfo{
   128  		Text: b.first.Prog,
   129  	}
   130  	s := &obj.LSym{
   131  		Extra: &funcInfo,
   132  	}
   133  
   134  	err = func() (err rookoutErrors.RookoutError) {
   135  		defer func() {
   136  			if r := recover(); r != nil {
   137  				err = rookoutErrors.NewFailedToAssemble(r)
   138  			}
   139  		}()
   140  
   141  		b.arch.Assemble(ctxt, s, ctxt.NewProg)
   142  		return nil
   143  	}()
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	assembled := s.P
   149  
   150  	
   151  	if b.arch.Arch.Name == "arm64" {
   152  		for string(assembled[len(assembled)-4:]) == "\x00\x00\x00\x00" {
   153  			assembled = assembled[:len(assembled)-4]
   154  		}
   155  	}
   156  
   157  	b.replacePlaceholders(assembled)
   158  	return assembled, nil
   159  }
   160  
   161  
   162  func NewBuilder() *Builder {
   163  	builder := &Builder{
   164  		arch:   a,
   165  		labels: make(map[string]*Instruction),
   166  	}
   167  
   168  	
   169  	if runtime.GOARCH == "arm64" {
   170  		builder.AddInstructions(
   171  			builder.PsuedoNop(),
   172  		)
   173  	}
   174  	return builder
   175  }
   176  
   177  func (b *Builder) placeholderProg() *obj.Prog {
   178  	return b.Inst(placeholderOp, NoArg, NoArg).Prog
   179  }
   180  
   181  func (i *Instruction) String() string {
   182  	if i.label != "" {
   183  		return i.label + ":"
   184  	}
   185  	s := i.Prog.String()
   186  	if i.jumpDestLabel != "" {
   187  		return s + " " + i.jumpDestLabel
   188  	}
   189  	return s
   190  }