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