"bufio"
        "bytes"
        "debug/elf"
+       "encoding/binary"
        "errors"
        "flag"
        "fmt"
        "go/build"
+       "io"
        "io/ioutil"
        "log"
        "math/rand"
        }
 }
 
+// Is a given offset into the file contained in a loaded segment?
+func isOffsetLoaded(f *elf.File, offset uint64) bool {
+       for _, prog := range f.Progs {
+               if prog.Type == elf.PT_LOAD {
+                       if prog.Off <= offset && offset < prog.Off+prog.Filesz {
+                               return true
+                       }
+               }
+       }
+       return false
+}
+
+func rnd(v int32, r int32) int32 {
+       if r <= 0 {
+               return v
+       }
+       v += r - 1
+       c := v % r
+       if c < 0 {
+               c += r
+       }
+       v -= c
+       return v
+}
+
+func readwithpad(r io.Reader, sz int32) ([]byte, error) {
+       data := make([]byte, rnd(sz, 4))
+       _, err := io.ReadFull(r, data)
+       if err != nil {
+               return nil, err
+       }
+       data = data[:sz]
+       return data, nil
+}
+
+type note struct {
+       name    string
+       tag     int32
+       desc    string
+       section *elf.Section
+}
+
+// Read all notes from f. As ELF section names are not supposed to be special, one
+// looks for a particular note by scanning all SHT_NOTE sections looking for a note
+// with a particular "name" and "tag".
+func readNotes(f *elf.File) ([]*note, error) {
+       var notes []*note
+       for _, sect := range f.Sections {
+               if sect.Type != elf.SHT_NOTE {
+                       continue
+               }
+               r := sect.Open()
+               for {
+                       var namesize, descsize, tag int32
+                       err := binary.Read(r, f.ByteOrder, &namesize)
+                       if err != nil {
+                               if err == io.EOF {
+                                       break
+                               }
+                               return nil, fmt.Errorf("read namesize failed:", err)
+                       }
+                       err = binary.Read(r, f.ByteOrder, &descsize)
+                       if err != nil {
+                               return nil, fmt.Errorf("read descsize failed:", err)
+                       }
+                       err = binary.Read(r, f.ByteOrder, &tag)
+                       if err != nil {
+                               return nil, fmt.Errorf("read type failed:", err)
+                       }
+                       name, err := readwithpad(r, namesize)
+                       if err != nil {
+                               return nil, fmt.Errorf("read name failed:", err)
+                       }
+                       desc, err := readwithpad(r, descsize)
+                       if err != nil {
+                               return nil, fmt.Errorf("read desc failed:", err)
+                       }
+                       notes = append(notes, ¬e{name: string(name), tag: tag, desc: string(desc), section: sect})
+               }
+       }
+       return notes, nil
+}
+
 func dynStrings(path string, flag elf.DynTag) []string {
        f, err := elf.Open(path)
        defer f.Close()
        run(t, "executable linked to GOPATH library", "./bin/exe")
 }
 
+// The shared library contains a note listing the packages it contains in a section
+// that is not mapped into memory.
+func testPkgListNote(t *testing.T, f *elf.File, note *note) {
+       if note.section.Flags != 0 {
+               t.Errorf("package list section has flags %v", note.section.Flags)
+       }
+       if isOffsetLoaded(f, note.section.Offset) {
+               t.Errorf("package list section contained in PT_LOAD segment")
+       }
+       if note.desc != "dep\n" {
+               t.Errorf("incorrect package list %q", note.desc)
+       }
+}
+
+// The shared library contains a note containing the ABI hash that is mapped into
+// memory and there is a local symbol called go.link.abihashbytes that points 16
+// bytes into it.
+func testABIHashNote(t *testing.T, f *elf.File, note *note) {
+       if note.section.Flags != elf.SHF_ALLOC {
+               t.Errorf("abi hash section has flags %v", note.section.Flags)
+       }
+       if !isOffsetLoaded(f, note.section.Offset) {
+               t.Errorf("abihash section not contained in PT_LOAD segment")
+       }
+       var hashbytes elf.Symbol
+       symbols, err := f.Symbols()
+       if err != nil {
+               t.Errorf("error reading symbols %v", err)
+               return
+       }
+       for _, sym := range symbols {
+               if sym.Name == "go.link.abihashbytes" {
+                       hashbytes = sym
+               }
+       }
+       if hashbytes.Name == "" {
+               t.Errorf("no symbol called go.link.abihashbytes")
+               return
+       }
+       if elf.ST_BIND(hashbytes.Info) != elf.STB_LOCAL {
+               t.Errorf("%s has incorrect binding %v", hashbytes.Name, elf.ST_BIND(hashbytes.Info))
+       }
+       if f.Sections[hashbytes.Section] != note.section {
+               t.Errorf("%s has incorrect section %v", hashbytes.Name, f.Sections[hashbytes.Section].Name)
+       }
+       if hashbytes.Value-note.section.Addr != 16 {
+               t.Errorf("%s has incorrect offset into section %d", hashbytes.Name, hashbytes.Value-note.section.Addr)
+       }
+}
+
+// The shared library contains notes with defined contents; see above.
+func TestNotes(t *testing.T) {
+       goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep")
+       f, err := elf.Open(filepath.Join(gopathInstallDir, "libdep.so"))
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer f.Close()
+       notes, err := readNotes(f)
+       if err != nil {
+               t.Fatal(err)
+       }
+       pkgListNoteFound := false
+       abiHashNoteFound := false
+       for _, note := range notes {
+               if note.name != "GO\x00\x00" {
+                       continue
+               }
+               switch note.tag {
+               case 1: // ELF_NOTE_GOPKGLIST_TAG
+                       if pkgListNoteFound {
+                               t.Error("multiple package list notes")
+                       }
+                       testPkgListNote(t, f, note)
+                       pkgListNoteFound = true
+               case 2: // ELF_NOTE_GOABIHASH_TAG
+                       if abiHashNoteFound {
+                               t.Error("multiple abi hash notes")
+                       }
+                       testABIHashNote(t, f, note)
+                       abiHashNoteFound = true
+               }
+       }
+       if !pkgListNoteFound {
+               t.Error("package list note not found")
+       }
+       if !abiHashNoteFound {
+               t.Error("abi hash note not found")
+       }
+}
+
 // Testing rebuilding of shared libraries when they are stale is a bit more
 // complicated that it seems like it should be. First, we make everything "old": but
 // only a few seconds old, or it might be older than 6g (or the runtime source) and
 
                ld.Elfinit()
 
                ld.HEADR = ld.ELFRESERVE
-               if ld.Buildmode == ld.BuildmodeShared {
-                       // When building a shared library we write a package list
-                       // note that can get quite large. The external linker will
-                       // re-layout all the sections anyway, so making this larger
-                       // just wastes a little space in the intermediate object
-                       // file, not the final shared library.
-                       ld.HEADR *= 3
-               }
                if ld.INITTEXT == -1 {
                        ld.INITTEXT = (1 << 22) + int64(ld.HEADR)
                }
 
                }
        }
 
+       if Buildmode == BuildmodeShared {
+               s := Linklookup(Ctxt, "go.link.abihashbytes", 0)
+               sectSym := Linklookup(Ctxt, ".note.go.abihash", 0)
+               s.Sect = sectSym.Sect
+               s.Value = int64(sectSym.Sect.Vaddr + 16)
+       }
+
        xdefine("runtime.text", obj.STEXT, int64(text.Vaddr))
        xdefine("runtime.etext", obj.STEXT, int64(text.Vaddr+text.Length))
        xdefine("runtime.rodata", obj.SRODATA, int64(rodata.Vaddr))
 
 
 import (
        "cmd/internal/obj"
+       "crypto/sha1"
        "encoding/binary"
        "fmt"
+       "sort"
 )
 
 /*
        return int(sh.size)
 }
 
-// Go package list note
+// Go specific notes
 const (
        ELF_NOTE_GOPKGLIST_TAG = 1
+       ELF_NOTE_GOABIHASH_TAG = 2
 )
 
 var ELF_NOTE_GO_NAME = []byte("GO\x00\x00")
 
-func elfgopkgnote(sh *ElfShdr, startva uint64, resoff uint64) int {
-       n := len(ELF_NOTE_GO_NAME) + int(Rnd(int64(len(pkglistfornote)), 4))
-       return elfnote(sh, startva, resoff, n, false)
-}
-
-func elfwritegopkgnote() int {
-       sh := elfwritenotehdr(".note.go.pkg-list", uint32(len(ELF_NOTE_GO_NAME)), uint32(len(pkglistfornote)), ELF_NOTE_GOPKGLIST_TAG)
-       if sh == nil {
-               return 0
-       }
-
-       Cwrite(ELF_NOTE_GO_NAME)
-       Cwrite(pkglistfornote)
-       var zero = make([]byte, 4)
-       Cwrite(zero[:int(Rnd(int64(len(pkglistfornote)), 4)-int64(len(pkglistfornote)))])
-
-       return int(sh.size)
-}
-
 var elfverneed int
 
 type Elfaux struct {
 
 func elfshbits(sect *Section) *ElfShdr {
        sh := elfshalloc(sect)
+       // If this section has already been set up as a note, we assume type_ and
+       // flags are already correct, but the other fields still need filling in.
+       if sh.type_ == SHT_NOTE {
+               if Linkmode != LinkExternal {
+                       // TODO(mwhudson): the approach here will work OK when
+                       // linking internally for notes that we want to be included
+                       // in a loadable segment (e.g. the abihash note) but not for
+                       // notes that we do not want to be mapped (e.g. the package
+                       // list note). The real fix is probably to define new values
+                       // for LSym.Type corresponding to mapped and unmapped notes
+                       // and handle them in dodata().
+                       Diag("sh.type_ == SHT_NOTE in elfshbits when linking internally")
+               }
+               sh.addralign = uint64(sect.Align)
+               sh.size = sect.Length
+               sh.off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr
+               return sh
+       }
        if sh.type_ > 0 {
                return sh
        }
 
 func elfshreloc(sect *Section) *ElfShdr {
        // If main section is SHT_NOBITS, nothing to relocate.
-       // Also nothing to relocate in .shstrtab.
+       // Also nothing to relocate in .shstrtab or notes.
        if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
                return nil
        }
        if sect.Name == ".shstrtab" || sect.Name == ".tbss" {
                return nil
        }
+       if sect.Elfsect.type_ == SHT_NOTE {
+               return nil
+       }
 
        var prefix string
        var typ int
        }
 }
 
+func addgonote(sectionName string, tag uint32, desc []byte) {
+       s := Linklookup(Ctxt, sectionName, 0)
+       s.Reachable = true
+       s.Type = obj.SELFROSECT
+       // namesz
+       Adduint32(Ctxt, s, uint32(len(ELF_NOTE_GO_NAME)))
+       // descsz
+       Adduint32(Ctxt, s, uint32(len(desc)))
+       // tag
+       Adduint32(Ctxt, s, tag)
+       // name + padding
+       s.P = append(s.P, ELF_NOTE_GO_NAME...)
+       for len(s.P)%4 != 0 {
+               s.P = append(s.P, 0)
+       }
+       // desc + padding
+       s.P = append(s.P, desc...)
+       for len(s.P)%4 != 0 {
+               s.P = append(s.P, 0)
+       }
+       s.Size = int64(len(s.P))
+}
+
 func doelf() {
        if !Iself {
                return
        if len(buildinfo) > 0 {
                Addstring(shstrtab, ".note.gnu.build-id")
        }
-       if Buildmode == BuildmodeShared {
-               Addstring(shstrtab, ".note.go.pkg-list")
-       }
        Addstring(shstrtab, ".elfdata")
        Addstring(shstrtab, ".rodata")
        Addstring(shstrtab, ".typelink")
 
                // add a .note.GNU-stack section to mark the stack as non-executable
                Addstring(shstrtab, ".note.GNU-stack")
+
+               if Buildmode == BuildmodeShared {
+                       Addstring(shstrtab, ".note.go.abihash")
+                       Addstring(shstrtab, ".note.go.pkg-list")
+               }
        }
 
        hasinitarr := Linkshared
                // size of .rel(a).plt section.
                Elfwritedynent(s, DT_DEBUG, 0)
        }
+
+       if Buildmode == BuildmodeShared {
+               // The go.link.abihashbytes symbol will be pointed at the appropriate
+               // part of the .note.go.abihash section in data.go:func address().
+               s := Linklookup(Ctxt, "go.link.abihashbytes", 0)
+               s.Local = true
+               s.Type = obj.SRODATA
+               s.Special = 1
+               s.Reachable = true
+               s.Size = int64(sha1.Size)
+
+               sort.Sort(byPkg(Ctxt.Library))
+               h := sha1.New()
+               for _, l := range Ctxt.Library {
+                       h.Write(l.hash)
+               }
+               addgonote(".note.go.abihash", ELF_NOTE_GOABIHASH_TAG, h.Sum([]byte{}))
+               addgonote(".note.go.pkg-list", ELF_NOTE_GOPKGLIST_TAG, []byte(pkglistfornote))
+       }
 }
 
 // Do not write DT_NULL.  elfdynhash will finish it.
                eh.phentsize = 0
 
                if Buildmode == BuildmodeShared {
-                       // The package list note we make space for here can get quite
-                       // large. The external linker will re-layout all the sections
-                       // anyway, so making this larger just wastes a little space
-                       // in the intermediate object file, not the final shared
-                       // library.
-                       elfreserve *= 3
-                       resoff = elfreserve
                        sh := elfshname(".note.go.pkg-list")
-                       resoff -= int64(elfgopkgnote(sh, uint64(startva), uint64(resoff)))
+                       sh.type_ = SHT_NOTE
+                       sh = elfshname(".note.go.abihash")
+                       sh.type_ = SHT_NOTE
+                       sh.flags = SHF_ALLOC
                }
                goto elfobj
        }
                        a += int64(elfwritebuildinfo())
                }
        }
-       if Buildmode == BuildmodeShared {
-               a += int64(elfwritegopkgnote())
-       }
 
        if a > elfreserve {
                Diag("ELFRESERVE too small: %d > %d", a, elfreserve)
 
        "cmd/internal/obj"
        "crypto/sha1"
        "debug/elf"
+       "encoding/binary"
        "fmt"
        "io"
        "io/ioutil"
 func readelfsymboldata(f *elf.File, sym *elf.Symbol) []byte {
        data := make([]byte, sym.Size)
        sect := f.Sections[sym.Section]
-       if sect.Type != elf.SHT_PROGBITS {
-               Diag("reading %s from non-PROGBITS section", sym.Name)
+       if sect.Type != elf.SHT_PROGBITS && sect.Type != elf.SHT_NOTE {
+               Diag("reading %s from non-data section", sym.Name)
        }
        n, err := sect.ReadAt(data, int64(sym.Value-sect.Offset))
        if uint64(n) != sym.Size {
        return data
 }
 
+func readwithpad(r io.Reader, sz int32) ([]byte, error) {
+       data := make([]byte, Rnd(int64(sz), 4))
+       _, err := io.ReadFull(r, data)
+       if err != nil {
+               return nil, err
+       }
+       data = data[:sz]
+       return data, nil
+}
+
+func readnote(f *elf.File, name []byte, typ int32) ([]byte, error) {
+       for _, sect := range f.Sections {
+               if sect.Type != elf.SHT_NOTE {
+                       continue
+               }
+               r := sect.Open()
+               for {
+                       var namesize, descsize, noteType int32
+                       err := binary.Read(r, f.ByteOrder, &namesize)
+                       if err != nil {
+                               if err == io.EOF {
+                                       break
+                               }
+                               return nil, fmt.Errorf("read namesize failed:", err)
+                       }
+                       err = binary.Read(r, f.ByteOrder, &descsize)
+                       if err != nil {
+                               return nil, fmt.Errorf("read descsize failed:", err)
+                       }
+                       err = binary.Read(r, f.ByteOrder, ¬eType)
+                       if err != nil {
+                               return nil, fmt.Errorf("read type failed:", err)
+                       }
+                       noteName, err := readwithpad(r, namesize)
+                       if err != nil {
+                               return nil, fmt.Errorf("read name failed:", err)
+                       }
+                       desc, err := readwithpad(r, descsize)
+                       if err != nil {
+                               return nil, fmt.Errorf("read desc failed:", err)
+                       }
+                       if string(name) == string(noteName) && typ == noteType {
+                               return desc, nil
+                       }
+               }
+       }
+       return nil, nil
+}
+
 func ldshlibsyms(shlib string) {
        found := false
        libpath := ""
                return
        }
        defer f.Close()
+
+       hash, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GOABIHASH_TAG)
+       if err != nil {
+               Diag("cannot read ABI hash from shared library %s: %v", libpath, err)
+               return
+       }
+
        syms, err := f.Symbols()
        if err != nil {
                Diag("cannot read symbols from shared library: %s", libpath)
        // table removed.
        gcmasks := make(map[uint64][]byte)
        types := []*LSym{}
-       var hash []byte
        for _, s := range syms {
                if elf.ST_TYPE(s.Info) == elf.STT_NOTYPE || elf.ST_TYPE(s.Info) == elf.STT_SECTION {
                        continue
                if strings.HasPrefix(s.Name, "runtime.gcbits.") {
                        gcmasks[s.Value] = readelfsymboldata(f, &s)
                }
-               if s.Name == "go.link.abihashbytes" {
-                       hash = readelfsymboldata(f, &s)
-               }
                if elf.ST_BIND(s.Info) != elf.STB_GLOBAL {
                        continue
                }
 
 
 import (
        "cmd/internal/obj"
-       "crypto/sha1"
        "fmt"
        "path/filepath"
-       "sort"
        "strings"
 )
 
        }
 
        if Buildmode == BuildmodeShared {
-               sort.Sort(byPkg(Ctxt.Library))
-               h := sha1.New()
-               for _, l := range Ctxt.Library {
-                       h.Write(l.hash)
-               }
                abihashgostr := Linklookup(Ctxt, "go.link.abihash."+filepath.Base(outfile), 0)
                abihashgostr.Reachable = true
                abihashgostr.Type = obj.SRODATA
-               var hashbytes []byte
-               addgostring(abihashgostr, "go.link.abihashbytes", string(h.Sum(hashbytes)))
+               hashsym := Linklookup(Ctxt, "go.link.abihashbytes", 0)
+               Addaddr(Ctxt, abihashgostr, hashsym)
+               adduint(Ctxt, abihashgostr, uint64(hashsym.Size))
        }
 
        // Information about the layout of the executable image for the