func (b *Binutils) openELF(name string, start, limit, offset uint64) (plugin.ObjFile, error) { ef, err := elf.Open(name) if err != nil { return nil, fmt.Errorf("Parsing %s: %v", name, err) } defer ef.Close() var stextOffset *uint64 var pageAligned = func(addr uint64) bool { return addr%4096 == 0 } if strings.Contains(name, "vmlinux") || !pageAligned(start) || !pageAligned(limit) || !pageAligned(offset) { // Reading all Symbols is expensive, and we only rarely need it so // we don't want to do it every time. But if _stext happens to be // page-aligned but isn't the same as Vaddr, we would symbolize // wrong. So if the name the addresses aren't page aligned, or if // the name is "vmlinux" we read _stext. We can be wrong if: (1) // someone passes a kernel path that doesn't contain "vmlinux" AND // (2) _stext is page-aligned AND (3) _stext is not at Vaddr symbols, err := ef.Symbols() if err != nil { return nil, err } for _, s := range symbols { if s.Name == "_stext" { // The kernel may use _stext as the mapping start address. stextOffset = &s.Value break } } } base, err := elfexec.GetBase(&ef.FileHeader, nil, stextOffset, start, limit, offset) if err != nil { return nil, fmt.Errorf("Could not identify base for %s: %v", name, err) } buildID := "" if f, err := os.Open(name); err == nil { if id, err := elfexec.GetBuildID(f); err == nil { buildID = fmt.Sprintf("%x", id) } } if b.fast || (!b.addr2lineFound && !b.llvmSymbolizerFound) { return &fileNM{file: file{b, name, base, buildID}}, nil } return &fileAddr2Line{file: file{b, name, base, buildID}}, nil }
// Open satisfies the plugin.ObjTool interface. func (b *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFile, error) { if b.addr2line == "" { // Update the command invocations if not initialized. b.SetTools("") } // Make sure file is a supported executable. // The pprof driver uses Open to sniff the difference // between an executable and a profile. // For now, only ELF is supported. // Could read the first few bytes of the file and // use a table of prefixes if we need to support other // systems at some point. f, err := os.Open(name) if err != nil { // For testing, do not require file name to exist. if strings.Contains(b.addr2line, "testdata/") { return &fileAddr2Line{file: file{b: b, name: name}}, nil } return nil, err } defer f.Close() ef, err := elf.NewFile(f) if err != nil { return nil, fmt.Errorf("Parsing %s: %v", name, err) } var stextOffset *uint64 var pageAligned = func(addr uint64) bool { return addr%4096 == 0 } if strings.Contains(name, "vmlinux") || !pageAligned(start) || !pageAligned(limit) || !pageAligned(offset) { // Reading all Symbols is expensive, and we only rarely need it so // we don't want to do it every time. But if _stext happens to be // page-aligned but isn't the same as Vaddr, we would symbolize // wrong. So if the name the addresses aren't page aligned, or if // the name is "vmlinux" we read _stext. We can be wrong if: (1) // someone passes a kernel path that doesn't contain "vmlinux" AND // (2) _stext is page-aligned AND (3) _stext is not at Vaddr symbols, err := ef.Symbols() if err != nil { return nil, err } for _, s := range symbols { if s.Name == "_stext" { // The kernel may use _stext as the mapping start address. stextOffset = &s.Value break } } } base, err := elfexec.GetBase(&ef.FileHeader, nil, stextOffset, start, limit, offset) if err != nil { return nil, fmt.Errorf("Could not identify base for %s: %v", name, err) } // Find build ID, while we have the file open. buildID := "" if id, err := elfexec.GetBuildID(f); err == nil { buildID = fmt.Sprintf("%x", id) } if b.fast { return &fileNM{file: file{b, name, base, buildID}}, nil } return &fileAddr2Line{file: file{b, name, base, buildID}}, nil }