github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/wasm/binary/element.go (about) 1 package binary 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 8 "github.com/wasilibs/wazerox/api" 9 "github.com/wasilibs/wazerox/internal/leb128" 10 "github.com/wasilibs/wazerox/internal/wasm" 11 ) 12 13 func ensureElementKindFuncRef(r *bytes.Reader) error { 14 elemKind, err := r.ReadByte() 15 if err != nil { 16 return fmt.Errorf("read element prefix: %w", err) 17 } 18 if elemKind != 0x0 { // ElemKind is fixed to 0x0 now: https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section 19 return fmt.Errorf("element kind must be zero but was 0x%x", elemKind) 20 } 21 return nil 22 } 23 24 func decodeElementInitValueVector(r *bytes.Reader) ([]wasm.Index, error) { 25 vs, _, err := leb128.DecodeUint32(r) 26 if err != nil { 27 return nil, fmt.Errorf("get size of vector: %w", err) 28 } 29 30 vec := make([]wasm.Index, vs) 31 for i := range vec { 32 u32, _, err := leb128.DecodeUint32(r) 33 if err != nil { 34 return nil, fmt.Errorf("read function index: %w", err) 35 } 36 37 if u32 >= wasm.MaximumFunctionIndex { 38 return nil, fmt.Errorf("too large function index in Element init: %d", u32) 39 } 40 vec[i] = u32 41 } 42 return vec, nil 43 } 44 45 func decodeElementConstExprVector(r *bytes.Reader, elemType wasm.RefType, enabledFeatures api.CoreFeatures) ([]wasm.Index, error) { 46 vs, _, err := leb128.DecodeUint32(r) 47 if err != nil { 48 return nil, fmt.Errorf("failed to get the size of constexpr vector: %w", err) 49 } 50 vec := make([]wasm.Index, vs) 51 for i := range vec { 52 var expr wasm.ConstantExpression 53 err := decodeConstantExpression(r, enabledFeatures, &expr) 54 if err != nil { 55 return nil, err 56 } 57 switch expr.Opcode { 58 case wasm.OpcodeRefFunc: 59 if elemType != wasm.RefTypeFuncref { 60 return nil, fmt.Errorf("element type mismatch: want %s, but constexpr has funcref", wasm.RefTypeName(elemType)) 61 } 62 v, _, _ := leb128.LoadUint32(expr.Data) 63 if v >= wasm.MaximumFunctionIndex { 64 return nil, fmt.Errorf("too large function index in Element init: %d", v) 65 } 66 vec[i] = v 67 case wasm.OpcodeRefNull: 68 if elemType != expr.Data[0] { 69 return nil, fmt.Errorf("element type mismatch: want %s, but constexpr has %s", 70 wasm.RefTypeName(elemType), wasm.RefTypeName(expr.Data[0])) 71 } 72 vec[i] = wasm.ElementInitNullReference 73 case wasm.OpcodeGlobalGet: 74 i32, _, _ := leb128.LoadInt32(expr.Data) 75 if elemType != wasm.RefTypeFuncref { 76 return nil, fmt.Errorf("element type mismatch: want %s, but requires funcref", wasm.RefTypeName(elemType)) 77 } 78 // Resolving the function index is done at instantiation phase. See the comment on 79 // wasm.ElementInitImportedGlobalFunctionReference. 80 vec[i] = wasm.ElementInitImportedGlobalFunctionReference | wasm.Index(i32) 81 default: 82 return nil, fmt.Errorf("const expr must be either ref.null or ref.func but was %s", wasm.InstructionName(expr.Opcode)) 83 } 84 } 85 return vec, nil 86 } 87 88 func decodeElementRefType(r *bytes.Reader) (ret wasm.RefType, err error) { 89 ret, err = r.ReadByte() 90 if err != nil { 91 err = fmt.Errorf("read element ref type: %w", err) 92 return 93 } 94 if ret != wasm.RefTypeFuncref && ret != wasm.RefTypeExternref { 95 return 0, errors.New("ref type must be funcref or externref for element as of WebAssembly 2.0") 96 } 97 return 98 } 99 100 const ( 101 // The prefix is explained at https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section 102 103 // elementSegmentPrefixLegacy is the legacy prefix and is only valid one before CoreFeatureBulkMemoryOperations. 104 elementSegmentPrefixLegacy = iota 105 // elementSegmentPrefixPassiveFuncrefValueVector is the passive element whose indexes are encoded as vec(varint), and reftype is fixed to funcref. 106 elementSegmentPrefixPassiveFuncrefValueVector 107 // elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex is the same as elementSegmentPrefixPassiveFuncrefValueVector but active and table index is encoded. 108 elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex 109 // elementSegmentPrefixDeclarativeFuncrefValueVector is the same as elementSegmentPrefixPassiveFuncrefValueVector but declarative. 110 elementSegmentPrefixDeclarativeFuncrefValueVector 111 // elementSegmentPrefixActiveFuncrefConstExprVector is active whoce reftype is fixed to funcref and indexes are encoded as vec(const_expr). 112 elementSegmentPrefixActiveFuncrefConstExprVector 113 // elementSegmentPrefixPassiveConstExprVector is passive whoce indexes are encoded as vec(const_expr), and reftype is encoded. 114 elementSegmentPrefixPassiveConstExprVector 115 // elementSegmentPrefixPassiveConstExprVector is active whoce indexes are encoded as vec(const_expr), and reftype and table index are encoded. 116 elementSegmentPrefixActiveConstExprVector 117 // elementSegmentPrefixDeclarativeConstExprVector is declarative whoce indexes are encoded as vec(const_expr), and reftype is encoded. 118 elementSegmentPrefixDeclarativeConstExprVector 119 ) 120 121 func decodeElementSegment(r *bytes.Reader, enabledFeatures api.CoreFeatures, ret *wasm.ElementSegment) error { 122 prefix, _, err := leb128.DecodeUint32(r) 123 if err != nil { 124 return fmt.Errorf("read element prefix: %w", err) 125 } 126 127 if prefix != elementSegmentPrefixLegacy { 128 if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil { 129 return fmt.Errorf("non-zero prefix for element segment is invalid as %w", err) 130 } 131 } 132 133 // Encoding depends on the prefix and described at https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section 134 switch prefix { 135 case elementSegmentPrefixLegacy: 136 // Legacy prefix which is WebAssembly 1.0 compatible. 137 err = decodeConstantExpression(r, enabledFeatures, &ret.OffsetExpr) 138 if err != nil { 139 return fmt.Errorf("read expr for offset: %w", err) 140 } 141 142 ret.Init, err = decodeElementInitValueVector(r) 143 if err != nil { 144 return err 145 } 146 147 ret.Mode = wasm.ElementModeActive 148 ret.Type = wasm.RefTypeFuncref 149 return nil 150 case elementSegmentPrefixPassiveFuncrefValueVector: 151 // Prefix 1 requires funcref. 152 if err = ensureElementKindFuncRef(r); err != nil { 153 return err 154 } 155 156 ret.Init, err = decodeElementInitValueVector(r) 157 if err != nil { 158 return err 159 } 160 ret.Mode = wasm.ElementModePassive 161 ret.Type = wasm.RefTypeFuncref 162 return nil 163 case elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex: 164 ret.TableIndex, _, err = leb128.DecodeUint32(r) 165 if err != nil { 166 return fmt.Errorf("get size of vector: %w", err) 167 } 168 169 if ret.TableIndex != 0 { 170 if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil { 171 return fmt.Errorf("table index must be zero but was %d: %w", ret.TableIndex, err) 172 } 173 } 174 175 err := decodeConstantExpression(r, enabledFeatures, &ret.OffsetExpr) 176 if err != nil { 177 return fmt.Errorf("read expr for offset: %w", err) 178 } 179 180 // Prefix 2 requires funcref. 181 if err = ensureElementKindFuncRef(r); err != nil { 182 return err 183 } 184 185 ret.Init, err = decodeElementInitValueVector(r) 186 if err != nil { 187 return err 188 } 189 190 ret.Mode = wasm.ElementModeActive 191 ret.Type = wasm.RefTypeFuncref 192 return nil 193 case elementSegmentPrefixDeclarativeFuncrefValueVector: 194 // Prefix 3 requires funcref. 195 if err = ensureElementKindFuncRef(r); err != nil { 196 return err 197 } 198 ret.Init, err = decodeElementInitValueVector(r) 199 if err != nil { 200 return err 201 } 202 ret.Type = wasm.RefTypeFuncref 203 ret.Mode = wasm.ElementModeDeclarative 204 return nil 205 case elementSegmentPrefixActiveFuncrefConstExprVector: 206 err := decodeConstantExpression(r, enabledFeatures, &ret.OffsetExpr) 207 if err != nil { 208 return fmt.Errorf("read expr for offset: %w", err) 209 } 210 211 ret.Init, err = decodeElementConstExprVector(r, wasm.RefTypeFuncref, enabledFeatures) 212 if err != nil { 213 return err 214 } 215 ret.Mode = wasm.ElementModeActive 216 ret.Type = wasm.RefTypeFuncref 217 return nil 218 case elementSegmentPrefixPassiveConstExprVector: 219 ret.Type, err = decodeElementRefType(r) 220 if err != nil { 221 return err 222 } 223 ret.Init, err = decodeElementConstExprVector(r, ret.Type, enabledFeatures) 224 if err != nil { 225 return err 226 } 227 ret.Mode = wasm.ElementModePassive 228 return nil 229 case elementSegmentPrefixActiveConstExprVector: 230 ret.TableIndex, _, err = leb128.DecodeUint32(r) 231 if err != nil { 232 return fmt.Errorf("get size of vector: %w", err) 233 } 234 235 if ret.TableIndex != 0 { 236 if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil { 237 return fmt.Errorf("table index must be zero but was %d: %w", ret.TableIndex, err) 238 } 239 } 240 err := decodeConstantExpression(r, enabledFeatures, &ret.OffsetExpr) 241 if err != nil { 242 return fmt.Errorf("read expr for offset: %w", err) 243 } 244 245 ret.Type, err = decodeElementRefType(r) 246 if err != nil { 247 return err 248 } 249 250 ret.Init, err = decodeElementConstExprVector(r, ret.Type, enabledFeatures) 251 if err != nil { 252 return err 253 } 254 255 ret.Mode = wasm.ElementModeActive 256 return nil 257 case elementSegmentPrefixDeclarativeConstExprVector: 258 ret.Type, err = decodeElementRefType(r) 259 if err != nil { 260 return err 261 } 262 ret.Init, err = decodeElementConstExprVector(r, ret.Type, enabledFeatures) 263 if err != nil { 264 return err 265 } 266 267 ret.Mode = wasm.ElementModeDeclarative 268 return nil 269 default: 270 return fmt.Errorf("invalid element segment prefix: 0x%x", prefix) 271 } 272 }