github.com/Rookout/GoSDK@v0.1.48/pkg/services/instrumentation/hooker/prologue/prologue.go (about)

     1  package prologue
     2  
     3  import (
     4  	"github.com/Rookout/GoSDK/pkg/logger"
     5  	"github.com/Rookout/GoSDK/pkg/rookoutErrors"
     6  	"github.com/Rookout/GoSDK/pkg/services/assembler"
     7  	"github.com/Rookout/GoSDK/pkg/services/disassembler"
     8  	"github.com/Rookout/GoSDK/pkg/services/instrumentation/hooker/regbackup"
     9  )
    10  
    11  
    12  var ForceLongPrologue = false
    13  
    14  const startLabel = "prologueStart"
    15  const endLabel = "prologueEnd"
    16  const fallbackLabel = "fallback"
    17  
    18  type Generator struct {
    19  	stackUsage           int
    20  	fallbackAddr         uintptr
    21  	goPrologueExists     bool
    22  	epilogueInstructions []*disassembler.Instruction
    23  	getRegsUsed          func() ([]assembler.Reg, rookoutErrors.RookoutError)
    24  	morestackAddr        uintptr
    25  }
    26  
    27  
    28  func NewGenerator(funcEntry uintptr, funcEnd uintptr, stackUsage int, fallbackAddr uintptr, getRegsUsed func() ([]assembler.Reg, rookoutErrors.RookoutError)) (*Generator, rookoutErrors.RookoutError) {
    29  	instructions, err := disassembler.Decode(funcEntry, funcEnd, true)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	g := &Generator{stackUsage: stackUsage, fallbackAddr: fallbackAddr, getRegsUsed: getRegsUsed}
    35  	g.epilogueInstructions, g.morestackAddr, g.goPrologueExists = getOriginalEpilogue(instructions)
    36  	if g.morestackAddr == 0 {
    37  		g.morestackAddr = morestackAddr
    38  	}
    39  	return g, nil
    40  }
    41  
    42  func (g *Generator) Generate() ([]byte, rookoutErrors.RookoutError) {
    43  	if !g.goPrologueExists || ForceLongPrologue {
    44  		logger.Logger().Debug("Generating long prologue")
    45  		return g.generateLongPrologue()
    46  	}
    47  	logger.Logger().Debug("Generating short prologue")
    48  	return g.generateShortPrologue()
    49  }
    50  
    51  func (g *Generator) generateShortPrologue() ([]byte, rookoutErrors.RookoutError) {
    52  	regBackup, regRestore := g.getOriginalRegBackup()
    53  	b := assembler.NewBuilder()
    54  
    55  	err := g.generateCheckStackUsage(b)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  	err = b.AddInstructions(b.Bytes(regBackup))
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	err = g.generateCallMorestack(b)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  	err = b.AddInstructions(b.Bytes(regRestore))
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	err = g.generateJumpToStart(b)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  
    76  	return b.Assemble()
    77  }
    78  
    79  func (g *Generator) generateLongPrologue() ([]byte, rookoutErrors.RookoutError) {
    80  	regsToUpdate, err := g.getRegsUsed()
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	logger.Logger().Debugf("Updating regs: %v\n", regsToUpdate)
    85  
    86  	regBackupGenerator := regbackup.NewGenerator(regsBackupBuffer, fallbackLabel, regsToUpdate)
    87  	b := assembler.NewBuilder()
    88  
    89  	err = g.generateCheckStackUsage(b)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  
    94  	err = regBackupGenerator.GenerateRegBackup(b)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	err = g.generateCallMorestack(b)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	err = regBackupGenerator.GenerateRegRestore(b)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  
   107  	err = g.generateCallFallback(b)
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	err = g.generateJumpToStart(b)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	return b.Assemble()
   117  }
   118  
   119  
   120  func findFirstJmp(instructions []*disassembler.Instruction, startIndex int) (jmpIndex int, jmpDestIndex int, ok bool) {
   121  	for i := startIndex; i < len(instructions); i++ {
   122  		jmp, jmpIndex, ok := disassembler.GetFirstInstruction(instructions[i:], disassembler.IsDirectJump)
   123  		if !ok {
   124  			return 0, 0, false
   125  		}
   126  		jmpIndex += i
   127  
   128  		jmpDest, err := jmp.GetDestPC()
   129  		if err != nil {
   130  			logger.Logger().WithError(err).Warningf("Failed to get first jmp dest PC")
   131  			return 0, 0, false
   132  		}
   133  
   134  		destFound := false
   135  		for j, inst := range instructions {
   136  			if inst.PC == jmpDest {
   137  				destFound = true
   138  				jmpDestIndex = j
   139  				break
   140  			}
   141  		}
   142  		if !destFound {
   143  			logger.Logger().Warningf("Unable to find destination instruction of jump. Jump PC: 0x%x, dest PC: 0x%x", jmp.PC, jmpDest)
   144  			return 0, 0, false
   145  		}
   146  
   147  		
   148  		if jmpDestIndex == jmpIndex+1 {
   149  			continue
   150  		}
   151  
   152  		return jmpIndex, jmpDestIndex, true
   153  	}
   154  
   155  	return 0, 0, false
   156  }
   157  
   158  func findMorestackCall(instructions []*disassembler.Instruction, startIndex int) (callIndex int, callDest uintptr, ok bool) {
   159  	call, callIndex, ok := disassembler.GetFirstInstruction(instructions[startIndex:], disassembler.IsDirectCall)
   160  	if !ok {
   161  		logger.Logger().Debug("Epilogue not found: no call in epilogue")
   162  		return 0, 0, false
   163  	}
   164  	callIndex += startIndex
   165  
   166  	callDest, err := call.GetDestPC()
   167  	if err != nil {
   168  		logger.Logger().WithError(err).Warningf("Failed to get call dest PC")
   169  		return 0, 0, false
   170  	}
   171  	if _, ok := morestackAddrs[callDest]; !ok {
   172  		logger.Logger().Debugf("Epilogue not found: call is not to morestack. callDest: %x, morestackAddrs: %x", callDest, morestackAddrs)
   173  		return 0, 0, false
   174  	}
   175  
   176  	return callIndex, callDest, true
   177  }
   178  
   179  func findJmpToStart(instructions []*disassembler.Instruction, epilogueStart int) (jmpIndex int, ok bool) {
   180  	jmpIndex, jmpDestIndex, ok := findFirstJmp(instructions, epilogueStart)
   181  	if !ok || jmpDestIndex != 0 {
   182  		return 0, false
   183  	}
   184  	return jmpIndex, true
   185  }
   186  
   187  func getOriginalEpilogue(instructions []*disassembler.Instruction) ([]*disassembler.Instruction, uintptr, bool) {
   188  	_, epilogueStart, ok := findFirstJmp(instructions, 0)
   189  	if !ok {
   190  		return nil, 0, false
   191  	}
   192  
   193  	morestackCallIndex, morestackAddr, ok := findMorestackCall(instructions, epilogueStart)
   194  	if !ok {
   195  		return nil, 0, false
   196  	}
   197  
   198  	epilogueEnd, ok := findJmpToStart(instructions, morestackCallIndex)
   199  	if !ok {
   200  		return nil, 0, false
   201  	}
   202  
   203  	
   204  	return instructions[epilogueStart:epilogueEnd], morestackAddr, true
   205  }