Example #1
0
File: note.go Project: pjump/gcc
// 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
}
Example #2
0
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
}
Example #3
0
File: note.go Project: arnold8/go
// 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
}