github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/wasm/binary/names.go (about) 1 package binary 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 8 "github.com/wasilibs/wazerox/internal/leb128" 9 "github.com/wasilibs/wazerox/internal/wasm" 10 ) 11 12 const ( 13 // subsectionIDModuleName contains only the module name. 14 subsectionIDModuleName = uint8(0) 15 // subsectionIDFunctionNames is a map of indices to function names, in ascending order by function index 16 subsectionIDFunctionNames = uint8(1) 17 // subsectionIDLocalNames contain a map of function indices to a map of local indices to their names, in ascending 18 // order by function and local index 19 subsectionIDLocalNames = uint8(2) 20 ) 21 22 // decodeNameSection deserializes the data associated with the "name" key in SectionIDCustom according to the 23 // standard: 24 // 25 // * ModuleName decode from subsection 0 26 // * FunctionNames decode from subsection 1 27 // * LocalNames decode from subsection 2 28 // 29 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-namesec 30 func decodeNameSection(r *bytes.Reader, limit uint64) (result *wasm.NameSection, err error) { 31 // TODO: add leb128 functions that work on []byte and offset. While using a reader allows us to reuse reader-based 32 // leb128 functions, it is less efficient, causes untestable code and in some cases more complex vs plain []byte. 33 result = &wasm.NameSection{} 34 35 // subsectionID is decoded if known, and skipped if not 36 var subsectionID uint8 37 // subsectionSize is the length to skip when the subsectionID is unknown 38 var subsectionSize uint32 39 var bytesRead uint64 40 for limit > 0 { 41 if subsectionID, err = r.ReadByte(); err != nil { 42 if err == io.EOF { 43 return result, nil 44 } 45 // TODO: untestable as this can't fail for a reason beside EOF reading a byte from a buffer 46 return nil, fmt.Errorf("failed to read a subsection ID: %w", err) 47 } 48 limit-- 49 50 if subsectionSize, bytesRead, err = leb128.DecodeUint32(r); err != nil { 51 return nil, fmt.Errorf("failed to read the size of subsection[%d]: %w", subsectionID, err) 52 } 53 limit -= bytesRead 54 55 switch subsectionID { 56 case subsectionIDModuleName: 57 if result.ModuleName, _, err = decodeUTF8(r, "module name"); err != nil { 58 return nil, err 59 } 60 case subsectionIDFunctionNames: 61 if result.FunctionNames, err = decodeFunctionNames(r); err != nil { 62 return nil, err 63 } 64 case subsectionIDLocalNames: 65 if result.LocalNames, err = decodeLocalNames(r); err != nil { 66 return nil, err 67 } 68 default: // Skip other subsections. 69 // Note: Not Seek because it doesn't err when given an offset past EOF. Rather, it leads to undefined state. 70 if _, err = io.CopyN(io.Discard, r, int64(subsectionSize)); err != nil { 71 return nil, fmt.Errorf("failed to skip subsection[%d]: %w", subsectionID, err) 72 } 73 } 74 limit -= uint64(subsectionSize) 75 } 76 return 77 } 78 79 func decodeFunctionNames(r *bytes.Reader) (wasm.NameMap, error) { 80 functionCount, err := decodeFunctionCount(r, subsectionIDFunctionNames) 81 if err != nil { 82 return nil, err 83 } 84 85 result := make(wasm.NameMap, functionCount) 86 for i := uint32(0); i < functionCount; i++ { 87 functionIndex, err := decodeFunctionIndex(r, subsectionIDFunctionNames) 88 if err != nil { 89 return nil, err 90 } 91 92 name, _, err := decodeUTF8(r, "function[%d] name", functionIndex) 93 if err != nil { 94 return nil, err 95 } 96 result[i] = wasm.NameAssoc{Index: functionIndex, Name: name} 97 } 98 return result, nil 99 } 100 101 func decodeLocalNames(r *bytes.Reader) (wasm.IndirectNameMap, error) { 102 functionCount, err := decodeFunctionCount(r, subsectionIDLocalNames) 103 if err != nil { 104 return nil, err 105 } 106 107 result := make(wasm.IndirectNameMap, functionCount) 108 for i := uint32(0); i < functionCount; i++ { 109 functionIndex, err := decodeFunctionIndex(r, subsectionIDLocalNames) 110 if err != nil { 111 return nil, err 112 } 113 114 localCount, _, err := leb128.DecodeUint32(r) 115 if err != nil { 116 return nil, fmt.Errorf("failed to read the local count for function[%d]: %w", functionIndex, err) 117 } 118 119 locals := make(wasm.NameMap, localCount) 120 for j := uint32(0); j < localCount; j++ { 121 localIndex, _, err := leb128.DecodeUint32(r) 122 if err != nil { 123 return nil, fmt.Errorf("failed to read a local index of function[%d]: %w", functionIndex, err) 124 } 125 126 name, _, err := decodeUTF8(r, "function[%d] local[%d] name", functionIndex, localIndex) 127 if err != nil { 128 return nil, err 129 } 130 locals[j] = wasm.NameAssoc{Index: localIndex, Name: name} 131 } 132 result[i] = wasm.NameMapAssoc{Index: functionIndex, NameMap: locals} 133 } 134 return result, nil 135 } 136 137 func decodeFunctionIndex(r *bytes.Reader, subsectionID uint8) (uint32, error) { 138 functionIndex, _, err := leb128.DecodeUint32(r) 139 if err != nil { 140 return 0, fmt.Errorf("failed to read a function index in subsection[%d]: %w", subsectionID, err) 141 } 142 return functionIndex, nil 143 } 144 145 func decodeFunctionCount(r *bytes.Reader, subsectionID uint8) (uint32, error) { 146 functionCount, _, err := leb128.DecodeUint32(r) 147 if err != nil { 148 return 0, fmt.Errorf("failed to read the function count of subsection[%d]: %w", subsectionID, err) 149 } 150 return functionCount, nil 151 }