github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/packages/nuget/symbol_extractor.go (about) 1 // Copyright 2023 The GitBundle Inc. All rights reserved. 2 // Copyright 2017 The Gitea Authors. All rights reserved. 3 // Use of this source code is governed by a MIT-style 4 // license that can be found in the LICENSE file. 5 6 package nuget 7 8 import ( 9 "archive/zip" 10 "bytes" 11 "encoding/binary" 12 "errors" 13 "fmt" 14 "io" 15 "path" 16 "path/filepath" 17 "strings" 18 19 "github.com/gitbundle/modules/packages" 20 ) 21 22 var ( 23 ErrMissingPdbFiles = errors.New("Package does not contain PDB files") 24 ErrInvalidFiles = errors.New("Package contains invalid files") 25 ErrInvalidPdbMagicNumber = errors.New("Invalid Portable PDB magic number") 26 ErrMissingPdbStream = errors.New("Missing PDB stream") 27 ) 28 29 type PortablePdb struct { 30 Name string 31 ID string 32 Content *packages.HashedBuffer 33 } 34 35 type PortablePdbList []*PortablePdb 36 37 func (l PortablePdbList) Close() { 38 for _, pdb := range l { 39 pdb.Content.Close() 40 } 41 } 42 43 // ExtractPortablePdb extracts PDB files from a .snupkg file 44 func ExtractPortablePdb(r io.ReaderAt, size int64) (PortablePdbList, error) { 45 archive, err := zip.NewReader(r, size) 46 if err != nil { 47 return nil, err 48 } 49 50 var pdbs PortablePdbList 51 52 err = func() error { 53 for _, file := range archive.File { 54 if strings.HasSuffix(file.Name, "/") { 55 continue 56 } 57 ext := strings.ToLower(filepath.Ext(file.Name)) 58 59 switch ext { 60 case ".nuspec", ".xml", ".psmdcp", ".rels", ".p7s": 61 continue 62 case ".pdb": 63 f, err := archive.Open(file.Name) 64 if err != nil { 65 return err 66 } 67 68 buf, err := packages.CreateHashedBufferFromReader(f, 32*1024*1024) 69 70 f.Close() 71 72 if err != nil { 73 return err 74 } 75 76 id, err := ParseDebugHeaderID(buf) 77 if err != nil { 78 buf.Close() 79 return fmt.Errorf("Invalid PDB file: %v", err) 80 } 81 82 if _, err := buf.Seek(0, io.SeekStart); err != nil { 83 buf.Close() 84 return err 85 } 86 87 pdbs = append(pdbs, &PortablePdb{ 88 Name: path.Base(file.Name), 89 ID: id, 90 Content: buf, 91 }) 92 default: 93 return ErrInvalidFiles 94 } 95 } 96 return nil 97 }() 98 if err != nil { 99 pdbs.Close() 100 return nil, err 101 } 102 103 if len(pdbs) == 0 { 104 return nil, ErrMissingPdbFiles 105 } 106 107 return pdbs, nil 108 } 109 110 // ParseDebugHeaderID TODO 111 func ParseDebugHeaderID(r io.ReadSeeker) (string, error) { 112 var magic uint32 113 if err := binary.Read(r, binary.LittleEndian, &magic); err != nil { 114 return "", err 115 } 116 if magic != 0x424A5342 { 117 return "", ErrInvalidPdbMagicNumber 118 } 119 120 if _, err := r.Seek(8, io.SeekCurrent); err != nil { 121 return "", err 122 } 123 124 var versionStringSize int32 125 if err := binary.Read(r, binary.LittleEndian, &versionStringSize); err != nil { 126 return "", err 127 } 128 if _, err := r.Seek(int64(versionStringSize), io.SeekCurrent); err != nil { 129 return "", err 130 } 131 if _, err := r.Seek(2, io.SeekCurrent); err != nil { 132 return "", err 133 } 134 135 var streamCount int16 136 if err := binary.Read(r, binary.LittleEndian, &streamCount); err != nil { 137 return "", err 138 } 139 140 read4ByteAlignedString := func(r io.Reader) (string, error) { 141 b := make([]byte, 4) 142 var buf bytes.Buffer 143 for { 144 if _, err := r.Read(b); err != nil { 145 return "", err 146 } 147 if i := bytes.IndexByte(b, 0); i != -1 { 148 buf.Write(b[:i]) 149 return buf.String(), nil 150 } 151 buf.Write(b) 152 } 153 } 154 155 for i := 0; i < int(streamCount); i++ { 156 var offset uint32 157 if err := binary.Read(r, binary.LittleEndian, &offset); err != nil { 158 return "", err 159 } 160 if _, err := r.Seek(4, io.SeekCurrent); err != nil { 161 return "", err 162 } 163 name, err := read4ByteAlignedString(r) 164 if err != nil { 165 return "", err 166 } 167 168 if name == "#Pdb" { 169 if _, err := r.Seek(int64(offset), io.SeekStart); err != nil { 170 return "", err 171 } 172 173 b := make([]byte, 16) 174 if _, err := r.Read(b); err != nil { 175 return "", err 176 } 177 178 data1 := binary.LittleEndian.Uint32(b[0:4]) 179 data2 := binary.LittleEndian.Uint16(b[4:6]) 180 data3 := binary.LittleEndian.Uint16(b[6:8]) 181 data4 := b[8:16] 182 183 return fmt.Sprintf("%08x%04x%04x%04x%012x", data1, data2, data3, data4[:2], data4[2:]), nil 184 } 185 } 186 187 return "", ErrMissingPdbStream 188 }