Example #1
0
func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int {
	if symtab.sym != nil {
		return 0
	}

	strbuf := make([]byte, symtab.strsize)
	if obj.Bseek(m.f, m.base+int64(symtab.stroff), 0) < 0 || obj.Bread(m.f, strbuf) != len(strbuf) {
		return -1
	}

	symsize := 12
	if m.is64 {
		symsize = 16
	}
	n := int(symtab.nsym * uint32(symsize))
	symbuf := make([]byte, n)
	if obj.Bseek(m.f, m.base+int64(symtab.symoff), 0) < 0 || obj.Bread(m.f, symbuf) != len(symbuf) {
		return -1
	}
	sym := make([]LdMachoSym, symtab.nsym)
	p := symbuf
	var s *LdMachoSym
	var v uint32
	for i := 0; uint32(i) < symtab.nsym; i++ {
		s = &sym[i]
		v = m.e.Uint32(p)
		if v >= symtab.strsize {
			return -1
		}
		s.name = cstring(strbuf[v:])
		s.type_ = uint8(p[4])
		s.sectnum = uint8(p[5])
		s.desc = m.e.Uint16(p[6:])
		if m.is64 {
			s.value = m.e.Uint64(p[8:])
		} else {
			s.value = uint64(m.e.Uint32(p[8:]))
		}
		p = p[symsize:]
	}

	symtab.str = strbuf
	symtab.sym = sym
	return 0
}
Example #2
0
func ldobjfile(ctxt *Link, f *obj.Biobuf, pkg string, length int64, pn string) {
	start := obj.Boffset(f)
	ctxt.Version++
	var buf [8]uint8
	obj.Bread(f, buf[:])
	if string(buf[:]) != startmagic {
		log.Fatalf("%s: invalid file start %x %x %x %x %x %x %x %x", pn, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7])
	}
	c := obj.Bgetc(f)
	if c != 1 {
		log.Fatalf("%s: invalid file version number %d", pn, c)
	}

	var lib string
	for {
		lib = rdstring(f)
		if lib == "" {
			break
		}
		addlib(ctxt, pkg, pn, lib)
	}

	for {
		c, err := f.Peek(1)
		if err != nil {
			log.Fatalf("%s: peeking: %v", pn, err)
		}
		if c[0] == 0xff {
			break
		}
		readsym(ctxt, f, pkg, pn)
	}

	buf = [8]uint8{}
	obj.Bread(f, buf[:])
	if string(buf[:]) != endmagic {
		log.Fatalf("%s: invalid file end", pn)
	}

	if obj.Boffset(f) != start+length {
		log.Fatalf("%s: unexpected end at %d, want %d", pn, int64(obj.Boffset(f)), int64(start+length))
	}
}
Example #3
0
File: ar.go Project: ckeyer/gosrc
// readArmap reads the archive symbol map.
func readArmap(filename string, f *obj.Biobuf, arhdr ArHdr) archiveMap {
	is64 := arhdr.name == "/SYM64/"
	wordSize := 4
	if is64 {
		wordSize = 8
	}

	l := atolwhex(arhdr.size)
	contents := make([]byte, l)
	if obj.Bread(f, contents) != int(l) {
		Exitf("short read from %s", filename)
	}

	var c uint64
	if is64 {
		c = binary.BigEndian.Uint64(contents)
	} else {
		c = uint64(binary.BigEndian.Uint32(contents))
	}
	contents = contents[wordSize:]

	ret := make(archiveMap)

	names := contents[c*uint64(wordSize):]
	for i := uint64(0); i < c; i++ {
		n := 0
		for names[n] != 0 {
			n++
		}
		name := string(names[:n])
		names = names[n+1:]

		// For Mach-O and PE/386 files we strip a leading
		// underscore from the symbol name.
		if goos == "darwin" || (goos == "windows" && goarch == "386") {
			if name[0] == '_' && len(name) > 1 {
				name = name[1:]
			}
		}

		var off uint64
		if is64 {
			off = binary.BigEndian.Uint64(contents)
		} else {
			off = uint64(binary.BigEndian.Uint32(contents))
		}
		contents = contents[wordSize:]

		ret[name] = off
	}

	return ret
}
Example #4
0
func macholoaddsym(m *LdMachoObj, d *LdMachoDysymtab) int {
	n := int(d.nindirectsyms)

	p := make([]byte, n*4)
	if obj.Bseek(m.f, m.base+int64(d.indirectsymoff), 0) < 0 || obj.Bread(m.f, p) != len(p) {
		return -1
	}

	d.indir = make([]uint32, n)
	for i := 0; i < n; i++ {
		d.indir[i] = m.e.Uint32(p[4*i:])
	}
	return 0
}
Example #5
0
File: ldpe.go Project: ckeyer/gosrc
func pemap(peobj *PeObj, sect *PeSect) int {
	if sect.base != nil {
		return 0
	}

	sect.base = make([]byte, sect.sh.SizeOfRawData)
	if sect.sh.PointerToRawData == 0 { // .bss doesn't have data in object file
		return 0
	}
	if obj.Bseek(peobj.f, int64(peobj.base)+int64(sect.sh.PointerToRawData), 0) < 0 || obj.Bread(peobj.f, sect.base) != len(sect.base) {
		return -1
	}

	return 0
}
Example #6
0
func macholoadrel(m *LdMachoObj, sect *LdMachoSect) int {
	if sect.rel != nil || sect.nreloc == 0 {
		return 0
	}
	rel := make([]LdMachoRel, sect.nreloc)
	n := int(sect.nreloc * 8)
	buf := make([]byte, n)
	if obj.Bseek(m.f, m.base+int64(sect.reloff), 0) < 0 || obj.Bread(m.f, buf) != n {
		return -1
	}
	var p []byte
	var r *LdMachoRel
	var v uint32
	for i := 0; uint32(i) < sect.nreloc; i++ {
		r = &rel[i]
		p = buf[i*8:]
		r.addr = m.e.Uint32(p)

		// TODO(rsc): Wrong interpretation for big-endian bitfields?
		if r.addr&0x80000000 != 0 {
			// scatterbrained relocation
			r.scattered = 1

			v = r.addr >> 24
			r.addr &= 0xFFFFFF
			r.type_ = uint8(v & 0xF)
			v >>= 4
			r.length = 1 << (v & 3)
			v >>= 2
			r.pcrel = uint8(v & 1)
			r.value = m.e.Uint32(p[4:])
		} else {
			v = m.e.Uint32(p[4:])
			r.symnum = v & 0xFFFFFF
			v >>= 24
			r.pcrel = uint8(v & 1)
			v >>= 1
			r.length = 1 << (v & 3)
			v >>= 2
			r.extrn = uint8(v & 1)
			v >>= 1
			r.type_ = uint8(v)
		}
	}

	sect.rel = rel
	return 0
}
Example #7
0
func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) {
	if sect.base != nil {
		return nil
	}

	if sect.off+sect.size > uint64(elfobj.length) {
		err = fmt.Errorf("elf section past end of file")
		return err
	}

	sect.base = make([]byte, sect.size)
	err = fmt.Errorf("short read")
	if obj.Bseek(elfobj.f, int64(uint64(elfobj.base)+sect.off), 0) < 0 || obj.Bread(elfobj.f, sect.base) != len(sect.base) {
		return err
	}

	return nil
}
Example #8
0
func rdsym(ctxt *Link, f *obj.Biobuf, pkg string) *LSym {
	n := rdint(f)
	if n == 0 {
		rdint64(f)
		return nil
	}

	if len(symbuf) < n {
		symbuf = make([]byte, n)
	}
	obj.Bread(f, symbuf[:n])
	p := string(symbuf[:n])
	v := rdint(f)
	if v != 0 {
		v = ctxt.Version
	}
	s := Linklookup(ctxt, expandpkg(p, pkg), v)

	if v == 0 && s.Name[0] == '$' && s.Type == 0 {
		if strings.HasPrefix(s.Name, "$f32.") {
			x, _ := strconv.ParseUint(s.Name[5:], 16, 32)
			i32 := int32(x)
			s.Type = obj.SRODATA
			s.Local = true
			Adduint32(ctxt, s, uint32(i32))
			s.Reachable = false
		} else if strings.HasPrefix(s.Name, "$f64.") || strings.HasPrefix(s.Name, "$i64.") {
			x, _ := strconv.ParseUint(s.Name[5:], 16, 64)
			i64 := int64(x)
			s.Type = obj.SRODATA
			s.Local = true
			Adduint64(ctxt, s, uint64(i64))
			s.Reachable = false
		}
	}
	if v == 0 && strings.HasPrefix(s.Name, "runtime.gcbits.") {
		s.Local = true
	}
	return s
}
Example #9
0
func rddata(f *obj.Biobuf) []byte {
	n := rdint64(f)
	p := make([]byte, n)
	obj.Bread(f, p)
	return p
}
Example #10
0
func rdstring(f *obj.Biobuf) string {
	n := rdint64(f)
	p := make([]byte, n)
	obj.Bread(f, p)
	return string(p)
}
Example #11
0
File: ldpe.go Project: ckeyer/gosrc
func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) {
	if Debug['v'] != 0 {
		fmt.Fprintf(&Bso, "%5.2f ldpe %s\n", obj.Cputime(), pn)
	}

	var sect *PeSect
	Ctxt.Version++
	base := int32(obj.Boffset(f))

	peobj := new(PeObj)
	peobj.f = f
	peobj.base = uint32(base)
	peobj.name = pn

	// read header
	var err error
	var j int
	var l uint32
	var name string
	var numaux int
	var r []Reloc
	var rp *Reloc
	var rsect *PeSect
	var s *LSym
	var sym *PeSym
	var symbuf [18]uint8
	if err = binary.Read(f, binary.LittleEndian, &peobj.fh); err != nil {
		goto bad
	}

	// load section list
	peobj.sect = make([]PeSect, peobj.fh.NumberOfSections)

	peobj.nsect = uint(peobj.fh.NumberOfSections)
	for i := 0; i < int(peobj.fh.NumberOfSections); i++ {
		if err = binary.Read(f, binary.LittleEndian, &peobj.sect[i].sh); err != nil {
			goto bad
		}
		peobj.sect[i].size = uint64(peobj.sect[i].sh.SizeOfRawData)
		peobj.sect[i].name = cstring(peobj.sect[i].sh.Name[:])
	}

	// TODO return error if found .cormeta

	// load string table
	obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0)

	if obj.Bread(f, symbuf[:4]) != 4 {
		goto bad
	}
	l = Le32(symbuf[:])
	peobj.snames = make([]byte, l)
	obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0)
	if obj.Bread(f, peobj.snames) != len(peobj.snames) {
		goto bad
	}

	// rewrite section names if they start with /
	for i := 0; i < int(peobj.fh.NumberOfSections); i++ {
		if peobj.sect[i].name == "" {
			continue
		}
		if peobj.sect[i].name[0] != '/' {
			continue
		}
		n, _ := strconv.Atoi(peobj.sect[i].name[1:])
		peobj.sect[i].name = cstring(peobj.snames[n:])
	}

	// read symbols
	peobj.pesym = make([]PeSym, peobj.fh.NumberOfSymbols)

	peobj.npesym = uint(peobj.fh.NumberOfSymbols)
	obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable), 0)
	for i := 0; uint32(i) < peobj.fh.NumberOfSymbols; i += numaux + 1 {
		obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(i), 0)
		if obj.Bread(f, symbuf[:]) != len(symbuf) {
			goto bad
		}

		if (symbuf[0] == 0) && (symbuf[1] == 0) && (symbuf[2] == 0) && (symbuf[3] == 0) {
			l = Le32(symbuf[4:])
			peobj.pesym[i].name = cstring(peobj.snames[l:]) // sym name length <= 8
		} else {
			peobj.pesym[i].name = cstring(symbuf[:8])
		}

		peobj.pesym[i].value = Le32(symbuf[8:])
		peobj.pesym[i].sectnum = Le16(symbuf[12:])
		peobj.pesym[i].sclass = symbuf[16]
		peobj.pesym[i].aux = symbuf[17]
		peobj.pesym[i].type_ = Le16(symbuf[14:])
		numaux = int(peobj.pesym[i].aux)
		if numaux < 0 {
			numaux = 0
		}
	}

	// create symbols for mapped sections
	for i := 0; uint(i) < peobj.nsect; i++ {
		sect = &peobj.sect[i]
		if sect.sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
			continue
		}

		if sect.sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
			// This has been seen for .idata sections, which we
			// want to ignore.  See issues 5106 and 5273.
			continue
		}

		if pemap(peobj, sect) < 0 {
			goto bad
		}

		name = fmt.Sprintf("%s(%s)", pkg, sect.name)
		s = Linklookup(Ctxt, name, Ctxt.Version)

		switch sect.sh.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) {
		case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata
			s.Type = obj.SRODATA

		case IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.bss
			s.Type = obj.SNOPTRBSS

		case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.data
			s.Type = obj.SNOPTRDATA

		case IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: //.text
			s.Type = obj.STEXT

		default:
			err = fmt.Errorf("unexpected flags %#06x for PE section %s", sect.sh.Characteristics, sect.name)
			goto bad
		}

		s.P = sect.base
		s.P = s.P[:sect.size]
		s.Size = int64(sect.size)
		sect.sym = s
		if sect.name == ".rsrc" {
			setpersrc(sect.sym)
		}
	}

	// load relocations
	for i := 0; uint(i) < peobj.nsect; i++ {
		rsect = &peobj.sect[i]
		if rsect.sym == nil || rsect.sh.NumberOfRelocations == 0 {
			continue
		}
		if rsect.sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
			continue
		}
		if sect.sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
			// This has been seen for .idata sections, which we
			// want to ignore.  See issues 5106 and 5273.
			continue
		}

		r = make([]Reloc, rsect.sh.NumberOfRelocations)
		obj.Bseek(f, int64(peobj.base)+int64(rsect.sh.PointerToRelocations), 0)
		for j = 0; j < int(rsect.sh.NumberOfRelocations); j++ {
			rp = &r[j]
			if obj.Bread(f, symbuf[:10]) != 10 {
				goto bad
			}
			rva := Le32(symbuf[0:])
			symindex := Le32(symbuf[4:])
			type_ := Le16(symbuf[8:])
			if err = readpesym(peobj, int(symindex), &sym); err != nil {
				goto bad
			}
			if sym.sym == nil {
				err = fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", sym.name, symindex, sym.type_)
				goto bad
			}

			rp.Sym = sym.sym
			rp.Siz = 4
			rp.Off = int32(rva)
			switch type_ {
			default:
				Diag("%s: unknown relocation type %d;", pn, type_)
				fallthrough

			case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32,
				IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32
				IMAGE_REL_AMD64_ADDR32NB:
				rp.Type = obj.R_PCREL

				rp.Add = int64(int32(Le32(rsect.base[rp.Off:])))

			case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32:
				rp.Type = obj.R_ADDR

				// load addend from image
				rp.Add = int64(int32(Le32(rsect.base[rp.Off:])))

			case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64
				rp.Siz = 8

				rp.Type = obj.R_ADDR

				// load addend from image
				rp.Add = int64(Le64(rsect.base[rp.Off:]))
			}

			// ld -r could generate multiple section symbols for the
			// same section but with different values, we have to take
			// that into account
			if issect(&peobj.pesym[symindex]) {
				rp.Add += int64(peobj.pesym[symindex].value)
			}
		}

		sort.Sort(rbyoff(r[:rsect.sh.NumberOfRelocations]))

		s = rsect.sym
		s.R = r
		s.R = s.R[:rsect.sh.NumberOfRelocations]
	}

	// enter sub-symbols into symbol table.
	for i := 0; uint(i) < peobj.npesym; i++ {
		if peobj.pesym[i].name == "" {
			continue
		}
		if issect(&peobj.pesym[i]) {
			continue
		}
		if uint(peobj.pesym[i].sectnum) > peobj.nsect {
			continue
		}
		if peobj.pesym[i].sectnum > 0 {
			sect = &peobj.sect[peobj.pesym[i].sectnum-1]
			if sect.sym == nil {
				continue
			}
		}

		if err = readpesym(peobj, i, &sym); err != nil {
			goto bad
		}

		s = sym.sym
		if sym.sectnum == 0 { // extern
			if s.Type == obj.SDYNIMPORT {
				s.Plt = -2 // flag for dynimport in PE object files.
			}
			if s.Type == obj.SXREF && sym.value > 0 { // global data
				s.Type = obj.SNOPTRDATA
				s.Size = int64(sym.value)
			}

			continue
		} else if sym.sectnum > 0 && uint(sym.sectnum) <= peobj.nsect {
			sect = &peobj.sect[sym.sectnum-1]
			if sect.sym == nil {
				Diag("%s: %s sym == 0!", pn, s.Name)
			}
		} else {
			Diag("%s: %s sectnum < 0!", pn, s.Name)
		}

		if sect == nil {
			return
		}

		if s.Outer != nil {
			if s.Dupok != 0 {
				continue
			}
			Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name)
		}

		s.Sub = sect.sym.Sub
		sect.sym.Sub = s
		s.Type = sect.sym.Type | obj.SSUB
		s.Value = int64(sym.value)
		s.Size = 4
		s.Outer = sect.sym
		if sect.sym.Type == obj.STEXT {
			if s.External != 0 && s.Dupok == 0 {
				Diag("%s: duplicate definition of %s", pn, s.Name)
			}
			s.External = 1
		}
	}

	// Sort outer lists by address, adding to textp.
	// This keeps textp in increasing address order.
	for i := 0; uint(i) < peobj.nsect; i++ {
		s = peobj.sect[i].sym
		if s == nil {
			continue
		}
		if s.Sub != nil {
			s.Sub = listsort(s.Sub, valuecmp, listsubp)
		}
		if s.Type == obj.STEXT {
			if s.Onlist != 0 {
				log.Fatalf("symbol %s listed multiple times", s.Name)
			}
			s.Onlist = 1
			if Ctxt.Etextp != nil {
				Ctxt.Etextp.Next = s
			} else {
				Ctxt.Textp = s
			}
			Ctxt.Etextp = s
			for s = s.Sub; s != nil; s = s.Sub {
				if s.Onlist != 0 {
					log.Fatalf("symbol %s listed multiple times", s.Name)
				}
				s.Onlist = 1
				Ctxt.Etextp.Next = s
				Ctxt.Etextp = s
			}
		}
	}

	return

bad:
	Diag("%s: malformed pe file: %v", pn, err)
}
Example #12
0
File: go.go Project: ckeyer/gosrc
func ldpkg(f *obj.Biobuf, pkg string, length int64, filename string, whence int) {
	var p0, p1 int

	if Debug['g'] != 0 {
		return
	}

	if int64(int(length)) != length {
		fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename)
		if Debug['u'] != 0 {
			errorexit()
		}
		return
	}

	// In a __.PKGDEF, we only care about the package name.
	// Don't read all the export data.
	if length > 1000 && whence == Pkgdef {
		length = 1000
	}

	bdata := make([]byte, length)
	if int64(obj.Bread(f, bdata)) != length {
		fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename)
		if Debug['u'] != 0 {
			errorexit()
		}
		return
	}
	data := string(bdata)

	// first \n$$ marks beginning of exports - skip rest of line
	p0 = strings.Index(data, "\n$$")
	if p0 < 0 {
		if Debug['u'] != 0 && whence != ArchiveObj {
			Exitf("cannot find export data in %s", filename)
		}
		return
	}

	// \n$$B marks the beginning of binary export data - don't skip over the B
	p0 += 3
	for p0 < len(data) && data[p0] != '\n' && data[p0] != 'B' {
		p0++
	}

	// second marks end of exports / beginning of local data
	p1 = strings.Index(data[p0:], "\n$$\n")
	if p1 < 0 && whence == Pkgdef {
		p1 = len(data) - p0
	}
	if p1 < 0 {
		fmt.Fprintf(os.Stderr, "%s: cannot find end of exports in %s\n", os.Args[0], filename)
		if Debug['u'] != 0 {
			errorexit()
		}
		return
	}
	p1 += p0

	for p0 < p1 && data[p0] != 'B' && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') {
		p0++
	}
	// don't check this section if we have binary (B) export data
	// TODO fix this eventually
	if p0 < p1 && data[p0] != 'B' {
		if !strings.HasPrefix(data[p0:], "package ") {
			fmt.Fprintf(os.Stderr, "%s: bad package section in %s - %.20s\n", os.Args[0], filename, data[p0:])
			if Debug['u'] != 0 {
				errorexit()
			}
			return
		}

		p0 += 8
		for p0 < p1 && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') {
			p0++
		}
		pname := p0
		for p0 < p1 && data[p0] != ' ' && data[p0] != '\t' && data[p0] != '\n' {
			p0++
		}
		if Debug['u'] != 0 && whence != ArchiveObj && (p0+6 > p1 || !strings.HasPrefix(data[p0:], " safe\n")) {
			Exitf("load of unsafe package %s", filename)
		}

		name := data[pname:p0]
		for p0 < p1 && data[p0] != '\n' {
			p0++
		}
		if p0 < p1 {
			p0++
		}

		if pkg == "main" && name != "main" {
			Exitf("%s: not package main (package %s)", filename, name)
		}
	}

	// __.PKGDEF has no cgo section - those are in the C compiler-generated object files.
	if whence == Pkgdef {
		return
	}

	// look for cgo section
	p0 = strings.Index(data[p1:], "\n$$  // cgo")
	if p0 >= 0 {
		p0 += p1
		i := strings.IndexByte(data[p0+1:], '\n')
		if i < 0 {
			fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename)
			if Debug['u'] != 0 {
				errorexit()
			}
			return
		}
		p0 += 1 + i

		p1 = strings.Index(data[p0:], "\n$$")
		if p1 < 0 {
			p1 = strings.Index(data[p0:], "\n!\n")
		}
		if p1 < 0 {
			fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename)
			if Debug['u'] != 0 {
				errorexit()
			}
			return
		}
		p1 += p0

		loadcgo(filename, pkg, data[p0:p1])
	}
}
Example #13
0
File: ar.go Project: ckeyer/gosrc
// hostArchive reads an archive file holding host objects and links in
// required objects.  The general format is the same as a Go archive
// file, but it has an armap listing symbols and the objects that
// define them.  This is used for the compiler support library
// libgcc.a.
func hostArchive(name string) {
	f, err := obj.Bopenr(name)
	if err != nil {
		if os.IsNotExist(err) {
			// It's OK if we don't have a libgcc file at all.
			if Debug['v'] != 0 {
				fmt.Fprintf(&Bso, "skipping libgcc file: %v\n", err)
			}
			return
		}
		Exitf("cannot open file %s: %v", name, err)
	}
	defer obj.Bterm(f)

	magbuf := make([]byte, len(ARMAG))
	if obj.Bread(f, magbuf) != len(magbuf) {
		Exitf("file %s too short", name)
	}

	var arhdr ArHdr
	l := nextar(f, obj.Boffset(f), &arhdr)
	if l <= 0 {
		Exitf("%s missing armap", name)
	}

	var armap archiveMap
	if arhdr.name == "/" || arhdr.name == "/SYM64/" {
		armap = readArmap(name, f, arhdr)
	} else {
		Exitf("%s missing armap", name)
	}

	loaded := make(map[uint64]bool)
	any := true
	for any {
		var load []uint64
		for s := Ctxt.Allsym; s != nil; s = s.Allsym {
			for _, r := range s.R {
				if r.Sym != nil && r.Sym.Type&obj.SMASK == obj.SXREF {
					if off := armap[r.Sym.Name]; off != 0 && !loaded[off] {
						load = append(load, off)
						loaded[off] = true
					}
				}
			}
		}

		for _, off := range load {
			l := nextar(f, int64(off), &arhdr)
			if l <= 0 {
				Exitf("%s missing archive entry at offset %d", name, off)
			}
			pname := fmt.Sprintf("%s(%s)", name, arhdr.name)
			l = atolwhex(arhdr.size)

			h := ldobj(f, "libgcc", l, pname, name, ArchiveObj)
			obj.Bseek(f, h.off, 0)
			h.ld(f, h.pkg, h.length, h.pn)
		}

		any = len(load) > 0
	}
}
Example #14
0
func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) {
	var err error
	var j int
	var is64 bool
	var secaddr uint64
	var hdr [7 * 4]uint8
	var cmdp []byte
	var dat []byte
	var ncmd uint32
	var cmdsz uint32
	var ty uint32
	var sz uint32
	var off uint32
	var m *LdMachoObj
	var e binary.ByteOrder
	var sect *LdMachoSect
	var rel *LdMachoRel
	var rpi int
	var s *LSym
	var s1 *LSym
	var outer *LSym
	var c *LdMachoCmd
	var symtab *LdMachoSymtab
	var dsymtab *LdMachoDysymtab
	var sym *LdMachoSym
	var r []Reloc
	var rp *Reloc
	var name string

	Ctxt.Version++
	base := obj.Boffset(f)
	if obj.Bread(f, hdr[:]) != len(hdr) {
		goto bad
	}

	if binary.BigEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE {
		e = binary.BigEndian
	} else if binary.LittleEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE {
		e = binary.LittleEndian
	} else {
		err = fmt.Errorf("bad magic - not mach-o file")
		goto bad
	}

	is64 = e.Uint32(hdr[:]) == 0xFEEDFACF
	ncmd = e.Uint32([]byte(hdr[4*4:]))
	cmdsz = e.Uint32([]byte(hdr[5*4:]))
	if ncmd > 0x10000 || cmdsz >= 0x01000000 {
		err = fmt.Errorf("implausible mach-o header ncmd=%d cmdsz=%d", ncmd, cmdsz)
		goto bad
	}

	if is64 {
		var tmp [4]uint8
		obj.Bread(f, tmp[:4]) // skip reserved word in header
	}

	m = new(LdMachoObj)

	m.f = f
	m.e = e
	m.cputype = uint(e.Uint32([]byte(hdr[1*4:])))
	m.subcputype = uint(e.Uint32([]byte(hdr[2*4:])))
	m.filetype = e.Uint32([]byte(hdr[3*4:]))
	m.ncmd = uint(ncmd)
	m.flags = e.Uint32([]byte(hdr[6*4:]))
	m.is64 = is64
	m.base = base
	m.length = length
	m.name = pn

	switch Thearch.Thechar {
	default:
		Diag("%s: mach-o %s unimplemented", pn, Thestring)
		return

	case '6':
		if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 {
			Diag("%s: mach-o object but not amd64", pn)
			return
		}

	case '8':
		if e != binary.LittleEndian || m.cputype != LdMachoCpu386 {
			Diag("%s: mach-o object but not 386", pn)
			return
		}
	}

	m.cmd = make([]LdMachoCmd, ncmd)
	off = uint32(len(hdr))
	cmdp = make([]byte, cmdsz)
	if obj.Bread(f, cmdp) != len(cmdp) {
		err = fmt.Errorf("reading cmds: %v", err)
		goto bad
	}

	// read and parse load commands
	c = nil

	symtab = nil
	dsymtab = nil

	for i := 0; uint32(i) < ncmd; i++ {
		ty = e.Uint32(cmdp)
		sz = e.Uint32(cmdp[4:])
		m.cmd[i].off = off
		unpackcmd(cmdp, m, &m.cmd[i], uint(ty), uint(sz))
		cmdp = cmdp[sz:]
		off += sz
		if ty == LdMachoCmdSymtab {
			if symtab != nil {
				err = fmt.Errorf("multiple symbol tables")
				goto bad
			}

			symtab = &m.cmd[i].sym
			macholoadsym(m, symtab)
		}

		if ty == LdMachoCmdDysymtab {
			dsymtab = &m.cmd[i].dsym
			macholoaddsym(m, dsymtab)
		}

		if (is64 && ty == LdMachoCmdSegment64) || (!is64 && ty == LdMachoCmdSegment) {
			if c != nil {
				err = fmt.Errorf("multiple load commands")
				goto bad
			}

			c = &m.cmd[i]
		}
	}

	// load text and data segments into memory.
	// they are not as small as the load commands, but we'll need
	// the memory anyway for the symbol images, so we might
	// as well use one large chunk.
	if c == nil {
		err = fmt.Errorf("no load command")
		goto bad
	}

	if symtab == nil {
		// our work is done here - no symbols means nothing can refer to this file
		return
	}

	if int64(c.seg.fileoff+c.seg.filesz) >= length {
		err = fmt.Errorf("load segment out of range")
		goto bad
	}

	dat = make([]byte, c.seg.filesz)
	if obj.Bseek(f, m.base+int64(c.seg.fileoff), 0) < 0 || obj.Bread(f, dat) != len(dat) {
		err = fmt.Errorf("cannot load object data: %v", err)
		goto bad
	}

	for i := 0; uint32(i) < c.seg.nsect; i++ {
		sect = &c.seg.sect[i]
		if sect.segname != "__TEXT" && sect.segname != "__DATA" {
			continue
		}
		if sect.name == "__eh_frame" {
			continue
		}
		name = fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name)
		s = Linklookup(Ctxt, name, Ctxt.Version)
		if s.Type != 0 {
			err = fmt.Errorf("duplicate %s/%s", sect.segname, sect.name)
			goto bad
		}

		if sect.flags&0xff == 1 { // S_ZEROFILL
			s.P = make([]byte, sect.size)
		} else {
			s.P = dat[sect.addr-c.seg.vmaddr:][:sect.size]
		}
		s.Size = int64(len(s.P))

		if sect.segname == "__TEXT" {
			if sect.name == "__text" {
				s.Type = obj.STEXT
			} else {
				s.Type = obj.SRODATA
			}
		} else {
			if sect.name == "__bss" {
				s.Type = obj.SNOPTRBSS
				s.P = s.P[:0]
			} else {
				s.Type = obj.SNOPTRDATA
			}
		}

		sect.sym = s
	}

	// enter sub-symbols into symbol table.
	// have to guess sizes from next symbol.
	for i := 0; uint32(i) < symtab.nsym; i++ {
		sym = &symtab.sym[i]
		if sym.type_&N_STAB != 0 {
			continue
		}

		// TODO: check sym->type against outer->type.
		name = sym.name

		if name[0] == '_' && name[1] != '\x00' {
			name = name[1:]
		}
		v := 0
		if sym.type_&N_EXT == 0 {
			v = Ctxt.Version
		}
		s = Linklookup(Ctxt, name, v)
		if sym.type_&N_EXT == 0 {
			s.Dupok = 1
		}
		sym.sym = s
		if sym.sectnum == 0 { // undefined
			continue
		}
		if uint32(sym.sectnum) > c.seg.nsect {
			err = fmt.Errorf("reference to invalid section %d", sym.sectnum)
			goto bad
		}

		sect = &c.seg.sect[sym.sectnum-1]
		outer = sect.sym
		if outer == nil {
			err = fmt.Errorf("reference to invalid section %s/%s", sect.segname, sect.name)
			continue
		}

		if s.Outer != nil {
			if s.Dupok != 0 {
				continue
			}
			Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name)
		}

		s.Type = outer.Type | obj.SSUB
		s.Sub = outer.Sub
		outer.Sub = s
		s.Outer = outer
		s.Value = int64(sym.value - sect.addr)
		if s.Cgoexport&CgoExportDynamic == 0 {
			s.Dynimplib = "" // satisfy dynimport
		}
		if outer.Type == obj.STEXT {
			if s.External != 0 && s.Dupok == 0 {
				Diag("%s: duplicate definition of %s", pn, s.Name)
			}
			s.External = 1
		}

		sym.sym = s
	}

	// Sort outer lists by address, adding to textp.
	// This keeps textp in increasing address order.
	for i := 0; uint32(i) < c.seg.nsect; i++ {
		sect = &c.seg.sect[i]
		s = sect.sym
		if s == nil {
			continue
		}
		if s.Sub != nil {
			s.Sub = listsort(s.Sub, valuecmp, listsubp)

			// assign sizes, now that we know symbols in sorted order.
			for s1 = s.Sub; s1 != nil; s1 = s1.Sub {
				if s1.Sub != nil {
					s1.Size = s1.Sub.Value - s1.Value
				} else {
					s1.Size = s.Value + s.Size - s1.Value
				}
			}
		}

		if s.Type == obj.STEXT {
			if s.Onlist != 0 {
				log.Fatalf("symbol %s listed multiple times", s.Name)
			}
			s.Onlist = 1
			if Ctxt.Etextp != nil {
				Ctxt.Etextp.Next = s
			} else {
				Ctxt.Textp = s
			}
			Ctxt.Etextp = s
			for s1 = s.Sub; s1 != nil; s1 = s1.Sub {
				if s1.Onlist != 0 {
					log.Fatalf("symbol %s listed multiple times", s1.Name)
				}
				s1.Onlist = 1
				Ctxt.Etextp.Next = s1
				Ctxt.Etextp = s1
			}
		}
	}

	// load relocations
	for i := 0; uint32(i) < c.seg.nsect; i++ {
		sect = &c.seg.sect[i]
		s = sect.sym
		if s == nil {
			continue
		}
		macholoadrel(m, sect)
		if sect.rel == nil {
			continue
		}
		r = make([]Reloc, sect.nreloc)
		rpi = 0
	Reloc:
		for j = 0; uint32(j) < sect.nreloc; j++ {
			rp = &r[rpi]
			rel = &sect.rel[j]
			if rel.scattered != 0 {
				if Thearch.Thechar != '8' {
					// mach-o only uses scattered relocation on 32-bit platforms
					Diag("unexpected scattered relocation")

					continue
				}

				// on 386, rewrite scattered 4/1 relocation and some
				// scattered 2/1 relocation into the pseudo-pc-relative
				// reference that it is.
				// assume that the second in the pair is in this section
				// and use that as the pc-relative base.
				if uint32(j+1) >= sect.nreloc {
					err = fmt.Errorf("unsupported scattered relocation %d", int(rel.type_))
					goto bad
				}

				if sect.rel[j+1].scattered == 0 || sect.rel[j+1].type_ != 1 || (rel.type_ != 4 && rel.type_ != 2) || uint64(sect.rel[j+1].value) < sect.addr || uint64(sect.rel[j+1].value) >= sect.addr+sect.size {
					err = fmt.Errorf("unsupported scattered relocation %d/%d", int(rel.type_), int(sect.rel[j+1].type_))
					goto bad
				}

				rp.Siz = rel.length
				rp.Off = int32(rel.addr)

				// NOTE(rsc): I haven't worked out why (really when)
				// we should ignore the addend on a
				// scattered relocation, but it seems that the
				// common case is we ignore it.
				// It's likely that this is not strictly correct
				// and that the math should look something
				// like the non-scattered case below.
				rp.Add = 0

				// want to make it pc-relative aka relative to rp->off+4
				// but the scatter asks for relative to off = sect->rel[j+1].value - sect->addr.
				// adjust rp->add accordingly.
				rp.Type = obj.R_PCREL

				rp.Add += int64(uint64(int64(rp.Off)+4) - (uint64(sect.rel[j+1].value) - sect.addr))

				// now consider the desired symbol.
				// find the section where it lives.
				var ks *LdMachoSect
				for k := 0; uint32(k) < c.seg.nsect; k++ {
					ks = &c.seg.sect[k]
					if ks.addr <= uint64(rel.value) && uint64(rel.value) < ks.addr+ks.size {
						if ks.sym != nil {
							rp.Sym = ks.sym
							rp.Add += int64(uint64(rel.value) - ks.addr)
						} else if ks.segname == "__IMPORT" && ks.name == "__pointers" {
							// handle reference to __IMPORT/__pointers.
							// how much worse can this get?
							// why are we supporting 386 on the mac anyway?
							rp.Type = 512 + MACHO_FAKE_GOTPCREL

							// figure out which pointer this is a reference to.
							k = int(uint64(ks.res1) + (uint64(rel.value)-ks.addr)/4)

							// load indirect table for __pointers
							// fetch symbol number
							if dsymtab == nil || k < 0 || uint32(k) >= dsymtab.nindirectsyms || dsymtab.indir == nil {
								err = fmt.Errorf("invalid scattered relocation: indirect symbol reference out of range")
								goto bad
							}

							k = int(dsymtab.indir[k])
							if k < 0 || uint32(k) >= symtab.nsym {
								err = fmt.Errorf("invalid scattered relocation: symbol reference out of range")
								goto bad
							}

							rp.Sym = symtab.sym[k].sym
						} else {
							err = fmt.Errorf("unsupported scattered relocation: reference to %s/%s", ks.segname, ks.name)
							goto bad
						}

						rpi++

						// skip #1 of 2 rel; continue skips #2 of 2.
						j++

						continue Reloc
					}
				}

				err = fmt.Errorf("unsupported scattered relocation: invalid address %#x", rel.addr)
				goto bad

			}

			rp.Siz = rel.length
			rp.Type = 512 + (int32(rel.type_) << 1) + int32(rel.pcrel)
			rp.Off = int32(rel.addr)

			// Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0).
			if Thearch.Thechar == '6' && rel.extrn == 0 && rel.type_ == 1 {
				// Calculate the addend as the offset into the section.
				//
				// The rip-relative offset stored in the object file is encoded
				// as follows:
				//
				//    movsd	0x00000360(%rip),%xmm0
				//
				// To get the absolute address of the value this rip-relative address is pointing
				// to, we must add the address of the next instruction to it. This is done by
				// taking the address of the relocation and adding 4 to it (since the rip-relative
				// offset can at most be 32 bits long).  To calculate the offset into the section the
				// relocation is referencing, we subtract the vaddr of the start of the referenced
				// section found in the original object file.
				//
				// [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h]
				secaddr = c.seg.sect[rel.symnum-1].addr

				rp.Add = int64(uint64(int64(int32(e.Uint32(s.P[rp.Off:])))+int64(rp.Off)+4) - secaddr)
			} else {
				rp.Add = int64(int32(e.Uint32(s.P[rp.Off:])))
			}

			// For i386 Mach-O PC-relative, the addend is written such that
			// it *is* the PC being subtracted.  Use that to make
			// it match our version of PC-relative.
			if rel.pcrel != 0 && Thearch.Thechar == '8' {
				rp.Add += int64(rp.Off) + int64(rp.Siz)
			}
			if rel.extrn == 0 {
				if rel.symnum < 1 || rel.symnum > c.seg.nsect {
					err = fmt.Errorf("invalid relocation: section reference out of range %d vs %d", rel.symnum, c.seg.nsect)
					goto bad
				}

				rp.Sym = c.seg.sect[rel.symnum-1].sym
				if rp.Sym == nil {
					err = fmt.Errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name)
					goto bad
				}

				// References to symbols in other sections
				// include that information in the addend.
				// We only care about the delta from the
				// section base.
				if Thearch.Thechar == '8' {
					rp.Add -= int64(c.seg.sect[rel.symnum-1].addr)
				}
			} else {
				if rel.symnum >= symtab.nsym {
					err = fmt.Errorf("invalid relocation: symbol reference out of range")
					goto bad
				}

				rp.Sym = symtab.sym[rel.symnum].sym
			}

			rpi++
		}

		sort.Sort(rbyoff(r[:rpi]))
		s.R = r
		s.R = s.R[:rpi]
	}

	return

bad:
	Diag("%s: malformed mach-o file: %v", pn, err)
}
Example #15
0
func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) {
	if Debug['v'] != 0 {
		fmt.Fprintf(&Bso, "%5.2f ldelf %s\n", obj.Cputime(), pn)
	}

	Ctxt.Version++
	base := int32(obj.Boffset(f))

	var add uint64
	var e binary.ByteOrder
	var elfobj *ElfObj
	var err error
	var flag int
	var hdr *ElfHdrBytes
	var hdrbuf [64]uint8
	var info uint64
	var is64 int
	var j int
	var n int
	var name string
	var p []byte
	var r []Reloc
	var rela int
	var rp *Reloc
	var rsect *ElfSect
	var s *LSym
	var sect *ElfSect
	var sym ElfSym
	var symbols []*LSym
	if obj.Bread(f, hdrbuf[:]) != len(hdrbuf) {
		goto bad
	}
	hdr = new(ElfHdrBytes)
	binary.Read(bytes.NewReader(hdrbuf[:]), binary.BigEndian, hdr) // only byte arrays; byte order doesn't matter
	if string(hdr.Ident[:4]) != "\x7FELF" {
		goto bad
	}
	switch hdr.Ident[5] {
	case ElfDataLsb:
		e = binary.LittleEndian

	case ElfDataMsb:
		e = binary.BigEndian

	default:
		goto bad
	}

	// read header
	elfobj = new(ElfObj)

	elfobj.e = e
	elfobj.f = f
	elfobj.base = int64(base)
	elfobj.length = length
	elfobj.name = pn

	is64 = 0
	if hdr.Ident[4] == ElfClass64 {
		is64 = 1
		hdr := new(ElfHdrBytes64)
		binary.Read(bytes.NewReader(hdrbuf[:]), binary.BigEndian, hdr) // only byte arrays; byte order doesn't matter
		elfobj.type_ = uint32(e.Uint16(hdr.Type[:]))
		elfobj.machine = uint32(e.Uint16(hdr.Machine[:]))
		elfobj.version = e.Uint32(hdr.Version[:])
		elfobj.phoff = e.Uint64(hdr.Phoff[:])
		elfobj.shoff = e.Uint64(hdr.Shoff[:])
		elfobj.flags = e.Uint32(hdr.Flags[:])
		elfobj.ehsize = uint32(e.Uint16(hdr.Ehsize[:]))
		elfobj.phentsize = uint32(e.Uint16(hdr.Phentsize[:]))
		elfobj.phnum = uint32(e.Uint16(hdr.Phnum[:]))
		elfobj.shentsize = uint32(e.Uint16(hdr.Shentsize[:]))
		elfobj.shnum = uint32(e.Uint16(hdr.Shnum[:]))
		elfobj.shstrndx = uint32(e.Uint16(hdr.Shstrndx[:]))
	} else {
		elfobj.type_ = uint32(e.Uint16(hdr.Type[:]))
		elfobj.machine = uint32(e.Uint16(hdr.Machine[:]))
		elfobj.version = e.Uint32(hdr.Version[:])
		elfobj.entry = uint64(e.Uint32(hdr.Entry[:]))
		elfobj.phoff = uint64(e.Uint32(hdr.Phoff[:]))
		elfobj.shoff = uint64(e.Uint32(hdr.Shoff[:]))
		elfobj.flags = e.Uint32(hdr.Flags[:])
		elfobj.ehsize = uint32(e.Uint16(hdr.Ehsize[:]))
		elfobj.phentsize = uint32(e.Uint16(hdr.Phentsize[:]))
		elfobj.phnum = uint32(e.Uint16(hdr.Phnum[:]))
		elfobj.shentsize = uint32(e.Uint16(hdr.Shentsize[:]))
		elfobj.shnum = uint32(e.Uint16(hdr.Shnum[:]))
		elfobj.shstrndx = uint32(e.Uint16(hdr.Shstrndx[:]))
	}

	elfobj.is64 = is64

	if uint32(hdr.Ident[6]) != elfobj.version {
		goto bad
	}

	if e.Uint16(hdr.Type[:]) != ElfTypeRelocatable {
		Diag("%s: elf but not elf relocatable object", pn)
		return
	}

	switch Thearch.Thechar {
	default:
		Diag("%s: elf %s unimplemented", pn, Thestring)
		return

	case '0':
		if elfobj.machine != ElfMachMips || hdr.Ident[4] != ElfClass64 {
			Diag("%s: elf object but not mips64", pn)
			return
		}

	case '5':
		if e != binary.LittleEndian || elfobj.machine != ElfMachArm || hdr.Ident[4] != ElfClass32 {
			Diag("%s: elf object but not arm", pn)
			return
		}

	case '6':
		if e != binary.LittleEndian || elfobj.machine != ElfMachAmd64 || hdr.Ident[4] != ElfClass64 {
			Diag("%s: elf object but not amd64", pn)
			return
		}

	case '7':
		if e != binary.LittleEndian || elfobj.machine != ElfMachArm64 || hdr.Ident[4] != ElfClass64 {
			Diag("%s: elf object but not arm64", pn)
			return
		}

	case '8':
		if e != binary.LittleEndian || elfobj.machine != ElfMach386 || hdr.Ident[4] != ElfClass32 {
			Diag("%s: elf object but not 386", pn)
			return
		}

	case '9':
		if elfobj.machine != ElfMachPower64 || hdr.Ident[4] != ElfClass64 {
			Diag("%s: elf object but not ppc64", pn)
			return
		}
	}

	// load section list into memory.
	elfobj.sect = make([]ElfSect, elfobj.shnum)

	elfobj.nsect = uint(elfobj.shnum)
	for i := 0; uint(i) < elfobj.nsect; i++ {
		if obj.Bseek(f, int64(uint64(base)+elfobj.shoff+uint64(int64(i)*int64(elfobj.shentsize))), 0) < 0 {
			goto bad
		}
		sect = &elfobj.sect[i]
		if is64 != 0 {
			var b ElfSectBytes64

			if err = binary.Read(f, e, &b); err != nil {
				goto bad
			}

			sect.nameoff = uint32(e.Uint32(b.Name[:]))
			sect.type_ = e.Uint32(b.Type[:])
			sect.flags = e.Uint64(b.Flags[:])
			sect.addr = e.Uint64(b.Addr[:])
			sect.off = e.Uint64(b.Off[:])
			sect.size = e.Uint64(b.Size[:])
			sect.link = e.Uint32(b.Link[:])
			sect.info = e.Uint32(b.Info[:])
			sect.align = e.Uint64(b.Align[:])
			sect.entsize = e.Uint64(b.Entsize[:])
		} else {
			var b ElfSectBytes

			if err = binary.Read(f, e, &b); err != nil {
				goto bad
			}

			sect.nameoff = uint32(e.Uint32(b.Name[:]))
			sect.type_ = e.Uint32(b.Type[:])
			sect.flags = uint64(e.Uint32(b.Flags[:]))
			sect.addr = uint64(e.Uint32(b.Addr[:]))
			sect.off = uint64(e.Uint32(b.Off[:]))
			sect.size = uint64(e.Uint32(b.Size[:]))
			sect.link = e.Uint32(b.Link[:])
			sect.info = e.Uint32(b.Info[:])
			sect.align = uint64(e.Uint32(b.Align[:]))
			sect.entsize = uint64(e.Uint32(b.Entsize[:]))
		}
	}

	// read section string table and translate names
	if elfobj.shstrndx >= uint32(elfobj.nsect) {
		err = fmt.Errorf("shstrndx out of range %d >= %d", elfobj.shstrndx, elfobj.nsect)
		goto bad
	}

	sect = &elfobj.sect[elfobj.shstrndx]
	if err = elfmap(elfobj, sect); err != nil {
		goto bad
	}
	for i := 0; uint(i) < elfobj.nsect; i++ {
		if elfobj.sect[i].nameoff != 0 {
			elfobj.sect[i].name = cstring(sect.base[elfobj.sect[i].nameoff:])
		}
	}

	// load string table for symbols into memory.
	elfobj.symtab = section(elfobj, ".symtab")

	if elfobj.symtab == nil {
		// our work is done here - no symbols means nothing can refer to this file
		return
	}

	if elfobj.symtab.link <= 0 || elfobj.symtab.link >= uint32(elfobj.nsect) {
		Diag("%s: elf object has symbol table with invalid string table link", pn)
		return
	}

	elfobj.symstr = &elfobj.sect[elfobj.symtab.link]
	if is64 != 0 {
		elfobj.nsymtab = int(elfobj.symtab.size / ELF64SYMSIZE)
	} else {
		elfobj.nsymtab = int(elfobj.symtab.size / ELF32SYMSIZE)
	}

	if err = elfmap(elfobj, elfobj.symtab); err != nil {
		goto bad
	}
	if err = elfmap(elfobj, elfobj.symstr); err != nil {
		goto bad
	}

	// load text and data segments into memory.
	// they are not as small as the section lists, but we'll need
	// the memory anyway for the symbol images, so we might
	// as well use one large chunk.

	// create symbols for elfmapped sections
	for i := 0; uint(i) < elfobj.nsect; i++ {
		sect = &elfobj.sect[i]
		if sect.type_ == SHT_ARM_ATTRIBUTES && sect.name == ".ARM.attributes" {
			if err = elfmap(elfobj, sect); err != nil {
				goto bad
			}
			parseArmAttributes(e, sect.base[:sect.size])
		}
		if (sect.type_ != ElfSectProgbits && sect.type_ != ElfSectNobits) || sect.flags&ElfSectFlagAlloc == 0 {
			continue
		}
		if sect.type_ != ElfSectNobits {
			if err = elfmap(elfobj, sect); err != nil {
				goto bad
			}
		}

		name = fmt.Sprintf("%s(%s)", pkg, sect.name)
		s = Linklookup(Ctxt, name, Ctxt.Version)

		switch int(sect.flags) & (ElfSectFlagAlloc | ElfSectFlagWrite | ElfSectFlagExec) {
		default:
			err = fmt.Errorf("unexpected flags for ELF section %s", sect.name)
			goto bad

		case ElfSectFlagAlloc:
			s.Type = obj.SRODATA

		case ElfSectFlagAlloc + ElfSectFlagWrite:
			if sect.type_ == ElfSectNobits {
				s.Type = obj.SNOPTRBSS
			} else {
				s.Type = obj.SNOPTRDATA
			}

		case ElfSectFlagAlloc + ElfSectFlagExec:
			s.Type = obj.STEXT
		}

		if sect.name == ".got" || sect.name == ".toc" {
			s.Type = obj.SELFGOT
		}
		if sect.type_ == ElfSectProgbits {
			s.P = sect.base
			s.P = s.P[:sect.size]
		}

		s.Size = int64(sect.size)
		s.Align = int32(sect.align)
		sect.sym = s
	}

	// enter sub-symbols into symbol table.
	// symbol 0 is the null symbol.
	symbols = make([]*LSym, elfobj.nsymtab)

	for i := 1; i < elfobj.nsymtab; i++ {
		if err = readelfsym(elfobj, i, &sym, 1); err != nil {
			goto bad
		}
		symbols[i] = sym.sym
		if sym.type_ != ElfSymTypeFunc && sym.type_ != ElfSymTypeObject && sym.type_ != ElfSymTypeNone {
			continue
		}
		if sym.shndx == ElfSymShnCommon {
			s = sym.sym
			if uint64(s.Size) < sym.size {
				s.Size = int64(sym.size)
			}
			if s.Type == 0 || s.Type == obj.SXREF {
				s.Type = obj.SNOPTRBSS
			}
			continue
		}

		if uint(sym.shndx) >= elfobj.nsect || sym.shndx == 0 {
			continue
		}

		// even when we pass needSym == 1 to readelfsym, it might still return nil to skip some unwanted symbols
		if sym.sym == nil {
			continue
		}
		sect = &elfobj.sect[sym.shndx:][0]
		if sect.sym == nil {
			if strings.HasPrefix(sym.name, ".Linfo_string") { // clang does this
				continue
			}

			if sym.name == "" && sym.type_ == 0 && sect.name == ".debug_str" {
				// This reportedly happens with clang 3.7 on ARM.
				// See issue 13139.
				continue
			}

			Diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type_)
			continue
		}

		s = sym.sym
		if s.Outer != nil {
			if s.Dupok != 0 {
				continue
			}
			Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name)
		}

		s.Sub = sect.sym.Sub
		sect.sym.Sub = s
		s.Type = sect.sym.Type | s.Type&^obj.SMASK | obj.SSUB
		if s.Cgoexport&CgoExportDynamic == 0 {
			s.Dynimplib = "" // satisfy dynimport
		}
		s.Value = int64(sym.value)
		s.Size = int64(sym.size)
		s.Outer = sect.sym
		if sect.sym.Type == obj.STEXT {
			if s.External != 0 && s.Dupok == 0 {
				Diag("%s: duplicate definition of %s", pn, s.Name)
			}
			s.External = 1
		}

		if elfobj.machine == ElfMachPower64 {
			flag = int(sym.other) >> 5
			if 2 <= flag && flag <= 6 {
				s.Localentry = 1 << uint(flag-2)
			} else if flag == 7 {
				Diag("%s: invalid sym.other 0x%x for %s", pn, sym.other, s.Name)
			}
		}
	}

	// Sort outer lists by address, adding to textp.
	// This keeps textp in increasing address order.
	for i := 0; uint(i) < elfobj.nsect; i++ {
		s = elfobj.sect[i].sym
		if s == nil {
			continue
		}
		if s.Sub != nil {
			s.Sub = listsort(s.Sub, valuecmp, listsubp)
		}
		if s.Type == obj.STEXT {
			if s.Onlist != 0 {
				log.Fatalf("symbol %s listed multiple times", s.Name)
			}
			s.Onlist = 1
			if Ctxt.Etextp != nil {
				Ctxt.Etextp.Next = s
			} else {
				Ctxt.Textp = s
			}
			Ctxt.Etextp = s
			for s = s.Sub; s != nil; s = s.Sub {
				if s.Onlist != 0 {
					log.Fatalf("symbol %s listed multiple times", s.Name)
				}
				s.Onlist = 1
				Ctxt.Etextp.Next = s
				Ctxt.Etextp = s
			}
		}
	}

	// load relocations
	for i := 0; uint(i) < elfobj.nsect; i++ {
		rsect = &elfobj.sect[i]
		if rsect.type_ != ElfSectRela && rsect.type_ != ElfSectRel {
			continue
		}
		if rsect.info >= uint32(elfobj.nsect) || elfobj.sect[rsect.info].base == nil {
			continue
		}
		sect = &elfobj.sect[rsect.info]
		if err = elfmap(elfobj, rsect); err != nil {
			goto bad
		}
		rela = 0
		if rsect.type_ == ElfSectRela {
			rela = 1
		}
		n = int(rsect.size / uint64(4+4*is64) / uint64(2+rela))
		r = make([]Reloc, n)
		p = rsect.base
		for j = 0; j < n; j++ {
			add = 0
			rp = &r[j]
			if is64 != 0 {
				// 64-bit rel/rela
				rp.Off = int32(e.Uint64(p))

				p = p[8:]
				info = e.Uint64(p)
				p = p[8:]
				if rela != 0 {
					add = e.Uint64(p)
					p = p[8:]
				}
			} else {
				// 32-bit rel/rela
				rp.Off = int32(e.Uint32(p))

				p = p[4:]
				info = uint64(e.Uint32(p))
				info = info>>8<<32 | info&0xff // convert to 64-bit info
				p = p[4:]
				if rela != 0 {
					add = uint64(e.Uint32(p))
					p = p[4:]
				}
			}

			if info&0xffffffff == 0 { // skip R_*_NONE relocation
				j--
				n--
				continue
			}

			if info>>32 == 0 { // absolute relocation, don't bother reading the null symbol
				rp.Sym = nil
			} else {
				if err = readelfsym(elfobj, int(info>>32), &sym, 0); err != nil {
					goto bad
				}
				sym.sym = symbols[info>>32]
				if sym.sym == nil {
					err = fmt.Errorf("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", sect.sym.Name, j, int(info>>32), sym.name, sym.shndx, sym.type_)
					goto bad
				}

				rp.Sym = sym.sym
			}

			rp.Type = int32(reltype(pn, int(uint32(info)), &rp.Siz))
			if rela != 0 {
				rp.Add = int64(add)
			} else {
				// load addend from image
				if rp.Siz == 4 {
					rp.Add = int64(e.Uint32(sect.base[rp.Off:]))
				} else if rp.Siz == 8 {
					rp.Add = int64(e.Uint64(sect.base[rp.Off:]))
				} else {
					Diag("invalid rela size %d", rp.Siz)
				}
			}

			if rp.Siz == 2 {
				rp.Add = int64(int16(rp.Add))
			}
			if rp.Siz == 4 {
				rp.Add = int64(int32(rp.Add))
			}
		}

		//print("rel %s %d %d %s %#llx\n", sect->sym->name, rp->type, rp->siz, rp->sym->name, rp->add);
		sort.Sort(rbyoff(r[:n]))
		// just in case

		s = sect.sym
		s.R = r
		s.R = s.R[:n]
	}

	return

bad:
	Diag("%s: malformed elf file: %v", pn, err)
}