// readELFGoBuildID the Go build ID string from an ELF binary. // The Go build ID is stored in a note described by an ELF PT_NOTE prog header. // The caller has already opened filename, to get f, and read the first 4 kB out, in data. func readELFGoBuildID(filename string, f *os.File, data []byte) (buildid string, err error) { // Assume the note content is in the first 4 kB, already read. // Rewrite the ELF header to set shnum to 0, so that we can pass // the data to elf.NewFile and it will decode the Prog list but not // try to read the section headers and the string table from disk. // That's a waste of I/O when all we care about is the Prog list // and the one ELF note. switch elf.Class(data[elf.EI_CLASS]) { case elf.ELFCLASS32: data[48] = 0 data[49] = 0 case elf.ELFCLASS64: data[60] = 0 data[61] = 0 } const elfGoBuildIDTag = 4 ef, err := elf.NewFile(bytes.NewReader(data)) if err != nil { return "", &os.PathError{Path: filename, Op: "parse", Err: err} } for _, p := range ef.Progs { if p.Type != elf.PT_NOTE || p.Off >= uint64(len(data)) || p.Off+p.Filesz >= uint64(len(data)) || p.Filesz < 16 { continue } note := data[p.Off : p.Off+p.Filesz] nameSize := ef.ByteOrder.Uint32(note) valSize := ef.ByteOrder.Uint32(note[4:]) tag := ef.ByteOrder.Uint32(note[8:]) name := note[12:16] if nameSize != 4 || 16+valSize > uint32(len(note)) || tag != elfGoBuildIDTag || !bytes.Equal(name, elfGoNote) { continue } return string(note[16 : 16+valSize]), nil } // No note. Treat as successful but build ID empty. return "", nil }
func ReadElfHeader(buf []byte) ElfFileHeader { class := elf.Class(buf[4]) data := elf.Data(buf[5]) ei_ver := elf.Version(buf[6]) osabi := elf.OSABI(buf[7]) abi_ver := uint8(buf[8]) byte_order := ToByteOrder(data) // Initialize part of the struct for now (the non-byte-order dependent bits) header := ElfFileHeader{ Class: class, Data: data, EI_Version: ei_ver, OSABI: osabi, ABIVersion: abi_ver} byte_reader := bytes.NewReader(buf[16:]) err1 := binary.Read(byte_reader, byte_order, &header.Type) err2 := binary.Read(byte_reader, byte_order, &header.Machine) err3 := binary.Read(byte_reader, byte_order, &header.E_Version) if err1 != nil || err2 != nil || err3 != nil { panic("Failed to read ELF machine") } header.Entry, header.Phoff, header.Shoff = ReadElfHeaderWithClass( byte_reader, class, byte_order) err1 = binary.Read(byte_reader, byte_order, &header.Flags) err2 = binary.Read(byte_reader, byte_order, &header.FileHeaderSize) err3 = binary.Read(byte_reader, byte_order, &header.Phentsize) if err1 != nil || err2 != nil || err3 != nil { panic("Failed to read ELF machine") } err1 = binary.Read(byte_reader, byte_order, &header.Phnum) err2 = binary.Read(byte_reader, byte_order, &header.Shentsize) err3 = binary.Read(byte_reader, byte_order, &header.Shnum) err4 := binary.Read(byte_reader, byte_order, &header.Shstrndx) if err1 != nil || err2 != nil || err3 != nil || err4 != nil { panic("Failed to read ELF machine") } return header }
// The Go build ID is stored in a note described by an ELF PT_NOTE prog // header. The caller has already opened filename, to get f, and read // at least 4 kB out, in data. func readELFGoBuildID(filename string, f *os.File, data []byte) (buildid string, err error) { // Assume the note content is in the data, already read. // Rewrite the ELF header to set shnum to 0, so that we can pass // the data to elf.NewFile and it will decode the Prog list but not // try to read the section headers and the string table from disk. // That's a waste of I/O when all we care about is the Prog list // and the one ELF note. switch elf.Class(data[elf.EI_CLASS]) { case elf.ELFCLASS32: data[48] = 0 data[49] = 0 case elf.ELFCLASS64: data[60] = 0 data[61] = 0 } const elfGoBuildIDTag = 4 ef, err := elf.NewFile(bytes.NewReader(data)) if err != nil { return "", &os.PathError{Path: filename, Op: "parse", Err: err} } for _, p := range ef.Progs { if p.Type != elf.PT_NOTE || p.Filesz < 16 { continue } var note []byte if p.Off+p.Filesz < uint64(len(data)) { note = data[p.Off : p.Off+p.Filesz] } else { // For some linkers, such as the Solaris linker, // the buildid may not be found in data (which // likely contains the first 16kB of the file) // or even the first few megabytes of the file // due to differences in note segment placement; // in that case, extract the note data manually. _, err = f.Seek(int64(p.Off), 0) if err != nil { return "", err } note = make([]byte, p.Filesz) _, err = io.ReadFull(f, note) if err != nil { return "", err } } nameSize := ef.ByteOrder.Uint32(note) valSize := ef.ByteOrder.Uint32(note[4:]) tag := ef.ByteOrder.Uint32(note[8:]) name := note[12:16] if nameSize != 4 || 16+valSize > uint32(len(note)) || tag != elfGoBuildIDTag || !bytes.Equal(name, elfGoNote) { continue } return string(note[16 : 16+valSize]), nil } // No note. Treat as successful but build ID empty. return "", nil }