gitee.com/quant1x/num@v0.3.2/asm/c2goasm/epilogue.go (about) 1 /* 2 * Minio Cloud Storage, (C) 2017 Minio, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "fmt" 21 "regexp" 22 "strconv" 23 ) 24 25 type Epilogue struct { 26 Pops []string // list of registers that are popped of the stack 27 SetRbpInstr bool // is there an instruction to set Rbp in epilogue? 28 StackSize uint // the size of the C stack 29 AlignedStack bool // is this an aligned stack? 30 AlignValue uint // alignment value in case of an aligned stack 31 VZeroUpper bool // is there a vzeroupper instruction in the epilogue? 32 Start, End int // start and ending lines of epilogue 33 _missingPops int // internal variable to identify first push without a corresponding pop 34 _stackGrowthSign int // direction to grow stack in case of detecting a push without a corresponding pop 35 } 36 37 var regexpAddRsp = regexp.MustCompile(`^\s*add\s*rsp, ([0-9]+)$`) 38 var regexpAndRsp = regexp.MustCompile(`^\s*and\s*rsp, \-([0-9]+)$`) 39 var regexpSubRsp = regexp.MustCompile(`^\s*sub\s*rsp, ([0-9]+)$`) 40 var regexpLeaRsp = regexp.MustCompile(`^\s*lea\s*rsp, `) 41 var regexpPop = regexp.MustCompile(`^\s*pop\s*([a-z0-9]+)$`) 42 var regexpPush = regexp.MustCompile(`^\s*push\s*([a-z0-9]+)$`) 43 var regexpMov = regexp.MustCompile(`^\s*mov\s*([a-z0-9]+), ([a-z0-9]+)$`) 44 var regexpVZeroUpper = regexp.MustCompile(`^\s*vzeroupper\s*$`) 45 var regexpReturn = regexp.MustCompile(`^\s*ret\s*$`) 46 47 type Stack struct { 48 alignedStack bool // is this an aligned stack? 49 50 goSavedSP uint // space to save a copy of the original Stack Pointer as passed in by Go (for aligned stacks) 51 goArgCopies uint // space used to store copies of golang args not passed in registers (arguments 7 and higher) 52 localSpace uint // stack space used by C code 53 freeSpace uint // free stack space used for CALLs 54 untouchedSpace uint // untouched space to prevent overwriting return address for final RET statement 55 } 56 57 func NewStack(epilogue Epilogue, arguments int, stackSpaceForCalls uint) Stack { 58 59 s := Stack{localSpace: epilogue.StackSize, alignedStack: epilogue.AlignedStack, freeSpace: stackSpaceForCalls} 60 61 if arguments-len(registers) > 0 { 62 s.goArgCopies = uint(8 * (arguments - len(registers))) 63 } 64 65 if s.alignedStack { 66 // For an aligned stack we need to save the original Stack Pointer as passed in by Go 67 s.goSavedSP = originalStackPointer 68 69 // We are rounding freeSpace to a multiple of the alignment value 70 s.freeSpace = (s.freeSpace + epilogue.AlignValue - 1) & ^(epilogue.AlignValue - 1) 71 72 // Create unused space at the bottom of the stack to guarantee alignment 73 s.untouchedSpace = epilogue.AlignValue 74 } else { 75 // Only when we are using no stack whatsoever, do we not need to reserve space to save the return address 76 if s.freeSpace+s.localSpace+s.goArgCopies+s.goSavedSP > 0 { 77 s.untouchedSpace = 8 78 } 79 } 80 81 return s 82 } 83 84 // Get total local stack frame size (for Go) used in TEXT definition 85 func (s Stack) GolangLocalStackFrameSize() uint { 86 return s.untouchedSpace + s.freeSpace + s.localSpace + s.goArgCopies + s.goSavedSP 87 } 88 89 // Get offset to adjust Stack Pointer appropriately for C code 90 func (s Stack) StackPointerOffsetForC() uint { 91 return s.untouchedSpace + s.freeSpace 92 } 93 94 // Get offset (from C Stack Pointer) for saving original Golang Stack Pointer 95 func (s Stack) OffsetForSavedSP() uint { 96 if s.goSavedSP == 0 { 97 panic("There should be space reserved for OffsetForSavedSP") 98 } 99 return s.localSpace + s.goArgCopies 100 } 101 102 // Get offset (from C Stack Pointer) for copy of Golang arguments 7 and higher 103 func (s Stack) OffsetForGoArg(iarg int) uint { 104 105 offset := uint((iarg - len(registers)) * 8) 106 if offset > s.goArgCopies { 107 panic("Offset for higher number argument asked for than reserved") 108 } 109 return s.localSpace + offset 110 } 111 112 func extractEpilogueInfo(src []string, sliceStart, sliceEnd int) Epilogue { 113 114 epilogue := Epilogue{Start: sliceStart, End: sliceEnd} 115 116 // Iterate over epilogue, starting from last instruction 117 for ipost := sliceEnd - 1; ipost >= sliceStart; ipost-- { 118 line := src[ipost] 119 120 if !epilogue.extractEpilogue(line) { 121 panic(fmt.Sprintf("Unknown line for epilogue: %s", line)) 122 } 123 } 124 125 return epilogue 126 } 127 128 func (e *Epilogue) extractEpilogue(line string) bool { 129 130 if match := regexpPop.FindStringSubmatch(line); len(match) > 1 { 131 register := match[1] 132 133 e.Pops = append(e.Pops, register) 134 if register == "rbp" { 135 e.SetRbpInstr = true 136 } 137 } else if match := regexpAddRsp.FindStringSubmatch(line); len(match) > 1 { 138 size, _ := strconv.Atoi(match[1]) 139 e.StackSize = uint(size) 140 } else if match := regexpLeaRsp.FindStringSubmatch(line); len(match) > 0 { 141 e.AlignedStack = true 142 } else if match := regexpVZeroUpper.FindStringSubmatch(line); len(match) > 0 { 143 e.VZeroUpper = true 144 } else if match := regexpMov.FindStringSubmatch(line); len(match) > 2 && match[1] == "rsp" && match[2] == "rbp" { 145 // no action to take 146 } else if match := regexpReturn.FindStringSubmatch(line); len(match) > 0 { 147 // no action to take 148 } else { 149 return false 150 } 151 152 return true 153 } 154 155 func isEpilogueInstruction(line string) bool { 156 157 return (&Epilogue{}).extractEpilogue(line) 158 } 159 160 func (e *Epilogue) isPrologueInstruction(line string) bool { 161 162 if match := regexpPush.FindStringSubmatch(line); len(match) > 1 { 163 hasCorrespondingPop := listContains(match[1], e.Pops) 164 if !hasCorrespondingPop { 165 e._missingPops++ 166 if e._missingPops == 1 { // only for first missing pop, set initial direction of growth to adapt check 167 if e.StackSize > 0 { 168 // Missing corresponding `pop` but rsp was modified directly in epilogue (see test-case pro/epilogue6) 169 e._stackGrowthSign = -1 170 } else { 171 // Missing corresponding `pop` meaning rsp is grown indirectly in prologue (see test-case pro/epilogue7) 172 e._stackGrowthSign = 1 173 } 174 } 175 e.StackSize += uint(8 * e._stackGrowthSign) 176 if e.StackSize == 0 && e._stackGrowthSign == -1 { 177 e._stackGrowthSign = 1 // flip direction once stack has shrunk to zero 178 } 179 } 180 return true 181 } else if match := regexpMov.FindStringSubmatch(line); len(match) > 2 && match[1] == "rbp" && match[2] == "rsp" { 182 if e.SetRbpInstr { 183 return true 184 } else { 185 panic(fmt.Sprintf("mov found but not expected to be set: %s", line)) 186 } 187 } else if match := regexpAndRsp.FindStringSubmatch(line); len(match) > 1 { 188 align, _ := strconv.Atoi(match[1]) 189 if e.AlignedStack && align == 8 { 190 // golang stack is already 8 byte aligned so we can effectively disable the aligned stack 191 e.AlignedStack = false 192 } else { 193 e.AlignValue = uint(align) 194 } 195 196 return true 197 } else if match := regexpSubRsp.FindStringSubmatch(line); len(match) > 1 { 198 space, _ := strconv.Atoi(match[1]) 199 if !e.AlignedStack && e.StackSize == uint(space) { 200 return true 201 } else if e.StackSize == 0 || e.StackSize == uint(space) { 202 e.StackSize = uint(space) // Update stack size when found in header (and missing in footer due to `lea` instruction) 203 return true 204 } else { 205 panic(fmt.Sprintf("'sub rsp' found but in unexpected scenario: %s", line)) 206 } 207 } 208 209 return false 210 } 211 212 func listContains(value string, list []string) bool { 213 for _, v := range list { 214 if v == value { 215 return true 216 } 217 } 218 return false 219 }