github.com/bir3/gocompiler@v0.9.2202/src/debug/plan9obj/file.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 /* 6 Package plan9obj implements access to Plan 9 a.out object files. 7 8 # Security 9 10 This package is not designed to be hardened against adversarial inputs, and is 11 outside the scope of https://go.dev/security/policy. In particular, only basic 12 validation is done when parsing object files. As such, care should be taken when 13 parsing untrusted inputs, as parsing malformed files may consume significant 14 resources, or cause panics. 15 */ 16 package plan9obj 17 18 import ( 19 "encoding/binary" 20 "errors" 21 "fmt" 22 "github.com/bir3/gocompiler/src/internal/saferio" 23 "io" 24 "os" 25 ) 26 27 // A FileHeader represents a Plan 9 a.out file header. 28 type FileHeader struct { 29 Magic uint32 30 Bss uint32 31 Entry uint64 32 PtrSize int 33 LoadAddress uint64 34 HdrSize uint64 35 } 36 37 // A File represents an open Plan 9 a.out file. 38 type File struct { 39 FileHeader 40 Sections []*Section 41 closer io.Closer 42 } 43 44 // A SectionHeader represents a single Plan 9 a.out section header. 45 // This structure doesn't exist on-disk, but eases navigation 46 // through the object file. 47 type SectionHeader struct { 48 Name string 49 Size uint32 50 Offset uint32 51 } 52 53 // A Section represents a single section in a Plan 9 a.out file. 54 type Section struct { 55 SectionHeader 56 57 // Embed ReaderAt for ReadAt method. 58 // Do not embed SectionReader directly 59 // to avoid having Read and Seek. 60 // If a client wants Read and Seek it must use 61 // Open() to avoid fighting over the seek offset 62 // with other clients. 63 io.ReaderAt 64 sr *io.SectionReader 65 } 66 67 // Data reads and returns the contents of the Plan 9 a.out section. 68 func (s *Section) Data() ([]byte, error) { 69 return saferio.ReadDataAt(s.sr, uint64(s.Size), 0) 70 } 71 72 // Open returns a new ReadSeeker reading the Plan 9 a.out section. 73 func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } 74 75 // A Symbol represents an entry in a Plan 9 a.out symbol table section. 76 type Sym struct { 77 Value uint64 78 Type rune 79 Name string 80 } 81 82 /* 83 * Plan 9 a.out reader 84 */ 85 86 // formatError is returned by some operations if the data does 87 // not have the correct format for an object file. 88 type formatError struct { 89 off int 90 msg string 91 val any 92 } 93 94 func (e *formatError) Error() string { 95 msg := e.msg 96 if e.val != nil { 97 msg += fmt.Sprintf(" '%v'", e.val) 98 } 99 msg += fmt.Sprintf(" in record at byte %#x", e.off) 100 return msg 101 } 102 103 // Open opens the named file using [os.Open] and prepares it for use as a Plan 9 a.out binary. 104 func Open(name string) (*File, error) { 105 f, err := os.Open(name) 106 if err != nil { 107 return nil, err 108 } 109 ff, err := NewFile(f) 110 if err != nil { 111 f.Close() 112 return nil, err 113 } 114 ff.closer = f 115 return ff, nil 116 } 117 118 // Close closes the [File]. 119 // If the [File] was created using [NewFile] directly instead of [Open], 120 // Close has no effect. 121 func (f *File) Close() error { 122 var err error 123 if f.closer != nil { 124 err = f.closer.Close() 125 f.closer = nil 126 } 127 return err 128 } 129 130 func parseMagic(magic []byte) (uint32, error) { 131 m := binary.BigEndian.Uint32(magic) 132 switch m { 133 case Magic386, MagicAMD64, MagicARM: 134 return m, nil 135 } 136 return 0, &formatError{0, "bad magic number", magic} 137 } 138 139 // NewFile creates a new [File] for accessing a Plan 9 binary in an underlying reader. 140 // The Plan 9 binary is expected to start at position 0 in the ReaderAt. 141 func NewFile(r io.ReaderAt) (*File, error) { 142 sr := io.NewSectionReader(r, 0, 1<<63-1) 143 // Read and decode Plan 9 magic 144 var magic [4]byte 145 if _, err := r.ReadAt(magic[:], 0); err != nil { 146 return nil, err 147 } 148 _, err := parseMagic(magic[:]) 149 if err != nil { 150 return nil, err 151 } 152 153 ph := new(prog) 154 if err := binary.Read(sr, binary.BigEndian, ph); err != nil { 155 return nil, err 156 } 157 158 f := &File{FileHeader: FileHeader{ 159 Magic: ph.Magic, 160 Bss: ph.Bss, 161 Entry: uint64(ph.Entry), 162 PtrSize: 4, 163 LoadAddress: 0x1000, 164 HdrSize: 4 * 8, 165 }} 166 167 if ph.Magic&Magic64 != 0 { 168 if err := binary.Read(sr, binary.BigEndian, &f.Entry); err != nil { 169 return nil, err 170 } 171 f.PtrSize = 8 172 f.LoadAddress = 0x200000 173 f.HdrSize += 8 174 } 175 176 var sects = []struct { 177 name string 178 size uint32 179 }{ 180 {"text", ph.Text}, 181 {"data", ph.Data}, 182 {"syms", ph.Syms}, 183 {"spsz", ph.Spsz}, 184 {"pcsz", ph.Pcsz}, 185 } 186 187 f.Sections = make([]*Section, 5) 188 189 off := uint32(f.HdrSize) 190 191 for i, sect := range sects { 192 s := new(Section) 193 s.SectionHeader = SectionHeader{ 194 Name: sect.name, 195 Size: sect.size, 196 Offset: off, 197 } 198 off += sect.size 199 s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Size)) 200 s.ReaderAt = s.sr 201 f.Sections[i] = s 202 } 203 204 return f, nil 205 } 206 207 func walksymtab(data []byte, ptrsz int, fn func(sym) error) error { 208 var order binary.ByteOrder = binary.BigEndian 209 var s sym 210 p := data 211 for len(p) >= 4 { 212 // Symbol type, value. 213 if len(p) < ptrsz { 214 return &formatError{len(data), "unexpected EOF", nil} 215 } 216 // fixed-width value 217 if ptrsz == 8 { 218 s.value = order.Uint64(p[0:8]) 219 p = p[8:] 220 } else { 221 s.value = uint64(order.Uint32(p[0:4])) 222 p = p[4:] 223 } 224 225 if len(p) < 1 { 226 return &formatError{len(data), "unexpected EOF", nil} 227 } 228 typ := p[0] & 0x7F 229 s.typ = typ 230 p = p[1:] 231 232 // Name. 233 var i int 234 var nnul int 235 for i = 0; i < len(p); i++ { 236 if p[i] == 0 { 237 nnul = 1 238 break 239 } 240 } 241 switch typ { 242 case 'z', 'Z': 243 p = p[i+nnul:] 244 for i = 0; i+2 <= len(p); i += 2 { 245 if p[i] == 0 && p[i+1] == 0 { 246 nnul = 2 247 break 248 } 249 } 250 } 251 if len(p) < i+nnul { 252 return &formatError{len(data), "unexpected EOF", nil} 253 } 254 s.name = p[0:i] 255 i += nnul 256 p = p[i:] 257 258 fn(s) 259 } 260 return nil 261 } 262 263 // newTable decodes the Go symbol table in data, 264 // returning an in-memory representation. 265 func newTable(symtab []byte, ptrsz int) ([]Sym, error) { 266 var n int 267 err := walksymtab(symtab, ptrsz, func(s sym) error { 268 n++ 269 return nil 270 }) 271 if err != nil { 272 return nil, err 273 } 274 275 fname := make(map[uint16]string) 276 syms := make([]Sym, 0, n) 277 err = walksymtab(symtab, ptrsz, func(s sym) error { 278 n := len(syms) 279 syms = syms[0 : n+1] 280 ts := &syms[n] 281 ts.Type = rune(s.typ) 282 ts.Value = s.value 283 switch s.typ { 284 default: 285 ts.Name = string(s.name) 286 case 'z', 'Z': 287 for i := 0; i < len(s.name); i += 2 { 288 eltIdx := binary.BigEndian.Uint16(s.name[i : i+2]) 289 elt, ok := fname[eltIdx] 290 if !ok { 291 return &formatError{-1, "bad filename code", eltIdx} 292 } 293 if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' { 294 ts.Name += "/" 295 } 296 ts.Name += elt 297 } 298 } 299 switch s.typ { 300 case 'f': 301 fname[uint16(s.value)] = ts.Name 302 } 303 return nil 304 }) 305 if err != nil { 306 return nil, err 307 } 308 309 return syms, nil 310 } 311 312 // ErrNoSymbols is returned by [File.Symbols] if there is no such section 313 // in the File. 314 var ErrNoSymbols = errors.New("no symbol section") 315 316 // Symbols returns the symbol table for f. 317 func (f *File) Symbols() ([]Sym, error) { 318 symtabSection := f.Section("syms") 319 if symtabSection == nil { 320 return nil, ErrNoSymbols 321 } 322 323 symtab, err := symtabSection.Data() 324 if err != nil { 325 return nil, errors.New("cannot load symbol section") 326 } 327 328 return newTable(symtab, f.PtrSize) 329 } 330 331 // Section returns a section with the given name, or nil if no such 332 // section exists. 333 func (f *File) Section(name string) *Section { 334 for _, s := range f.Sections { 335 if s.Name == name { 336 return s 337 } 338 } 339 return nil 340 }