github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/modules/wasm/runtime/wasmtime/debug.go (about)

     1  package wasmtime
     2  
     3  import (
     4  	"bytes"
     5  	"debug/dwarf"
     6  	"fmt"
     7  	"log"
     8  
     9  	"github.com/pkg/errors"
    10  )
    11  
    12  const (
    13  	SecCustom   byte = 0
    14  	SecType     byte = 1
    15  	SecImport   byte = 2
    16  	SecFunction byte = 3
    17  	SecTable    byte = 4
    18  	SecMemory   byte = 5
    19  	SecGlobal   byte = 6
    20  	SecExport   byte = 7
    21  	SecStart    byte = 8
    22  	SecElement  byte = 9
    23  	SecCode     byte = 10
    24  	SecData     byte = 11
    25  )
    26  
    27  var secTypeNames = map[byte]string{
    28  	SecCustom:   "CUSTOM",
    29  	SecType:     "TYPE",
    30  	SecImport:   "IMPORT",
    31  	SecFunction: "FUNCTION",
    32  	SecTable:    "TABLE",
    33  	SecMemory:   "MEMORY",
    34  	SecGlobal:   "GLOBAL",
    35  	SecExport:   "EXPORT",
    36  	SecStart:    "START",
    37  	SecElement:  "ELEMENT",
    38  	SecCode:     "CODE",
    39  	SecData:     "DATA",
    40  }
    41  
    42  type DwarfInfo struct {
    43  	data              *dwarf.Data
    44  	codeSectionOffset int
    45  	lineReader        *dwarf.LineReader
    46  }
    47  
    48  func (d *DwarfInfo) GetLineReader() *dwarf.LineReader {
    49  	if d.lineReader != nil {
    50  		d.lineReader.Reset()
    51  		return d.lineReader
    52  	}
    53  
    54  	// Get the line table for the first CU.
    55  	cu, err := d.data.Reader().Next()
    56  	if err != nil {
    57  		log.Printf("[wasmtime][debug] getLineReader fail to do d.Reader().Next(), err: %v", err)
    58  	}
    59  
    60  	lineReader, err := d.data.LineReader(cu)
    61  	if err != nil {
    62  		log.Printf("[wasmtime][debug] getLineReader fail to do d.LineReader(), err: %v", err)
    63  	}
    64  
    65  	d.lineReader = lineReader
    66  
    67  	return d.lineReader
    68  }
    69  
    70  func (d *DwarfInfo) SeekPC(pc uint64) *dwarf.LineEntry {
    71  	var line dwarf.LineEntry
    72  
    73  	lr := d.GetLineReader()
    74  	if lr == nil {
    75  		return nil
    76  	}
    77  
    78  	// https://yurydelendik.github.io/webassembly-dwarf/#pc
    79  	pc -= uint64(d.codeSectionOffset)
    80  
    81  	if err := lr.SeekPC(pc, &line); err != nil {
    82  		return nil
    83  	}
    84  
    85  	return &line
    86  }
    87  
    88  func ParseDwarf(data []byte) *DwarfInfo {
    89  	customSections, codeSectionOffset, err := LoadCustomSections(data)
    90  	if err != nil {
    91  		return nil
    92  	}
    93  
    94  	dwarfData, err := dwarf.New(
    95  		customSections[".debug_abbrev"].data,
    96  		customSections[".debug_aranges"].data,
    97  		customSections[".debug_frame"].data,
    98  		customSections[".debug_info"].data,
    99  		customSections[".debug_line"].data,
   100  		customSections[".debug_pubnames"].data,
   101  		customSections[".debug_ranges"].data,
   102  		customSections[".debug_str"].data)
   103  	if err != nil {
   104  		log.Printf("[wasmtime][debug] dwarf.New failed: %v", err)
   105  		return nil
   106  	}
   107  
   108  	return &DwarfInfo{
   109  		data:              dwarfData,
   110  		codeSectionOffset: codeSectionOffset,
   111  	}
   112  }
   113  
   114  type CustomSection struct {
   115  	name string
   116  	data []byte
   117  }
   118  
   119  func LoadCustomSections(data []byte) (map[string]CustomSection, int, error) {
   120  	// Index of first section
   121  	i := 8
   122  	codeSectionOffset := 0
   123  
   124  	// Skip preamble
   125  	if !bytes.Equal(data[0:i], []byte{0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00}) {
   126  		return nil, 0, errors.New("invalid preamble. Only Wasm v1 files are supported")
   127  	}
   128  
   129  	customSections := map[string]CustomSection{}
   130  	// Read all sections
   131  	for i < len(data) {
   132  		sectionType := data[i]
   133  		if _, ok := secTypeNames[sectionType]; !ok {
   134  			return nil, 0, fmt.Errorf("section type 0x%02X not supported", sectionType)
   135  		}
   136  		i++
   137  
   138  		sectionDataSize, n := ReadULEB128(data[i:])
   139  		i += n
   140  
   141  		sectionName := ""
   142  
   143  		if sectionType == SecCustom {
   144  			customSection := NewCustomSection(data[i : i+sectionDataSize])
   145  			sectionName = customSection.name
   146  			customSections[customSection.name] = customSection
   147  		}
   148  
   149  		if sectionType == SecCode {
   150  			codeSectionOffset = i
   151  		}
   152  
   153  		if true /* debug mode */ {
   154  			if len(sectionName) > 0 {
   155  				log.Printf("[wasmtime][debug] LoadCustomSections %v(%X) %v: data start at 0x%X, data size 0x%X", secTypeNames[sectionType], sectionType, sectionName, i, sectionDataSize)
   156  			} else {
   157  				log.Printf("[wasmtime][debug] LoadCustomSections %v(%X): data start at 0x%X, data size 0x%X", secTypeNames[sectionType], sectionType, i, sectionDataSize)
   158  			}
   159  		}
   160  
   161  		i += sectionDataSize
   162  	}
   163  
   164  	return customSections, codeSectionOffset, nil
   165  }
   166  
   167  // NewCustomSection properly read name and data from a custom section.
   168  func NewCustomSection(data []byte) CustomSection {
   169  	nameLength, n := ReadULEB128(data)
   170  
   171  	return CustomSection{
   172  		name: string(data[n : n+nameLength]),
   173  		data: data[nameLength+n:],
   174  	}
   175  }
   176  
   177  // ReadULEB128 read a uLEB128 at the starting position. Returns (number read, number of bytes read).
   178  func ReadULEB128(data []byte) (int, int) {
   179  	length := 1
   180  	for data[length-1]&0x80 > 0 {
   181  		length++
   182  	}
   183  
   184  	n := int(DecodeULeb128(data[:length+1]))
   185  
   186  	return n, length
   187  }
   188  
   189  // DecodeULeb128 decodes an unsigned LEB128 value to an unsigned int32 value. Returns the result as a uint32.
   190  func DecodeULeb128(value []byte) uint32 {
   191  	var result uint32
   192  	var ctr uint
   193  	var cur byte = 0x80
   194  
   195  	for (cur&0x80 == 0x80) && ctr < 5 {
   196  		cur = value[ctr] & 0xff
   197  		result += uint32(cur&0x7f) << (ctr * 7)
   198  		ctr++
   199  	}
   200  
   201  	return result
   202  }