diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index f573b98cb5c6f4..b9354cdd6a2168 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -78,9 +78,9 @@ func (p *Prog) loadDebugInfo() { switch { default: continue; - case strings.Index(line, "warning: useless type name in empty declaration") >= 0: + case strings.Index(line, ": useless type name in empty declaration") >= 0: what = "type"; - case strings.Index(line, "warning: statement with no effect") >= 0: + case strings.Index(line, ": statement with no effect") >= 0: what = "value"; case strings.Index(line, "undeclared") >= 0: what = "error"; @@ -114,7 +114,7 @@ func (p *Prog) loadDebugInfo() { fatal("gcc failed:\n%s\non input:\n%s", stderr, b.Bytes()); } - // Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i. + // Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i. types := make([]dwarf.Type, len(names)); r := d.Reader(); for { @@ -198,10 +198,10 @@ func (p *Prog) gccDebug(stdin []byte) (*dwarf.Data, string) { machine, "-Wall", // many warnings "-Werror", // warnings are errors - "-o"+tmp, // write object to tmp - "-gdwarf-2", // generate DWARF v2 debugging symbols + "-o"+tmp, // write object to tmp + "-gdwarf-2", // generate DWARF v2 debugging symbols "-c", // do not link - "-xc", // input language is C + "-xc", // input language is C "-", // read input from standard input }; _, stderr, ok := run(stdin, concat(base, p.GccOptions)); diff --git a/src/pkg/debug/elf/file.go b/src/pkg/debug/elf/file.go index 7b1d784548bed7..0945eb506fdaef 100644 --- a/src/pkg/debug/elf/file.go +++ b/src/pkg/debug/elf/file.go @@ -6,6 +6,7 @@ package elf import ( + "bytes"; "debug/dwarf"; "encoding/binary"; "fmt"; @@ -109,6 +110,13 @@ func (p *Prog) Open() io.ReadSeeker { return io.NewSectionReader(p.sr, 0, 1<<63 - 1); } +// A Symbol represents an entry in an ELF symbol table section. +type Symbol struct { + Name uint32; + Info, Other byte; + Section uint32; + Value, Size uint64; +} /* * ELF reader @@ -305,6 +313,60 @@ func NewFile(r io.ReaderAt) (*File, os.Error) { return f, nil; } +func (f *File) getSymbols() ([]Symbol, os.Error) { + switch f.Class { + case ELFCLASS64: + return f.getSymbols64(); + } + + return nil, os.ErrorString("not implemented"); +} + +// GetSymbols returns a slice of Symbols from parsing the symbol table. +func (f *File) getSymbols64() ([]Symbol, os.Error) { + var symtabSection *Section; + for _, section := range f.Sections { + if section.Type == SHT_SYMTAB { + symtabSection = section; + break; + } + } + + if symtabSection == nil { + return nil, os.ErrorString("no symbol section"); + } + + data, err := symtabSection.Data(); + if err != nil { + return nil, os.ErrorString("cannot load symbol section"); + } + symtab := bytes.NewBuffer(data); + if symtab.Len() % Sym64Size != 0 { + return nil, os.ErrorString("length of symbol section is not a multiple of Sym64Size"); + } + + // The first entry is all zeros. + var skip [Sym64Size]byte; + symtab.Read(skip[0:len(skip)]); + + symbols := make([]Symbol, symtab.Len() / Sym64Size); + + i := 0; + var sym Sym64; + for symtab.Len() > 0 { + binary.Read(symtab, f.ByteOrder, &sym); + symbols[i].Name = sym.Name; + symbols[i].Info = sym.Info; + symbols[i].Other = sym.Other; + symbols[i].Section = uint32(sym.Shndx); + symbols[i].Value = sym.Value; + symbols[i].Size = sym.Size; + i++; + } + + return symbols, nil; +} + // getString extracts a string from an ELF string table. func getString(section []byte, start int) (string, bool) { if start < 0 || start >= len(section) { @@ -330,6 +392,60 @@ func (f *File) Section(name string) *Section { return nil; } +// applyRelocations applies relocations to dst. rels is a relocations section +// in RELA format. +func (f *File) applyRelocations(dst []byte, rels []byte) os.Error { + if f.Class == ELFCLASS64 && f.Machine == EM_X86_64 { + return f.applyRelocationsAMD64(dst, rels); + } + + return os.ErrorString("not implemented"); +} + +func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) os.Error { + if len(rels) % Sym64Size != 0 { + return os.ErrorString("length of relocation section is not a multiple of Sym64Size"); + } + + symbols, err := f.getSymbols(); + if err != nil { + return err; + } + + b := bytes.NewBuffer(rels); + var rela Rela64; + + for b.Len() > 0 { + binary.Read(b, f.ByteOrder, &rela); + symNo := rela.Info >> 32; + t := R_X86_64(rela.Info & 0xffff); + + if symNo >= uint64(len(symbols)) { + continue; + } + sym := &symbols[symNo]; + if SymType(sym.Info & 0xf) != STT_SECTION { + // We don't handle non-section relocations for now. + continue; + } + + switch t { + case R_X86_64_64: + if rela.Off + 8 >= uint64(len(dst)) || rela.Addend < 0 { + continue; + } + f.ByteOrder.PutUint64(dst[rela.Off : rela.Off + 8], uint64(rela.Addend)); + case R_X86_64_32: + if rela.Off + 4 >= uint64(len(dst)) || rela.Addend < 0 { + continue; + } + f.ByteOrder.PutUint32(dst[rela.Off : rela.Off + 4], uint32(rela.Addend)); + } + } + + return nil; +} + func (f *File) DWARF() (*dwarf.Data, os.Error) { // There are many other DWARF sections, but these // are the required ones, and the debug/dwarf package @@ -349,6 +465,20 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) { dat[i] = b; } + // If there's a relocation table for .debug_info, we have to process it + // now otherwise the data in .debug_info is invalid for x86-64 objects. + rela := f.Section(".rela.debug_info"); + if rela != nil && rela.Type == SHT_RELA && f.Machine == EM_X86_64 { + data, err := rela.Data(); + if err != nil { + return nil, err; + } + err = f.applyRelocations(dat[1], data); + if err != nil { + return nil, err; + } + } + abbrev, info, str := dat[0], dat[1], dat[2]; return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str); } diff --git a/src/pkg/debug/elf/file_test.go b/src/pkg/debug/elf/file_test.go index 9b756aea122c0f..04c924d12406ea 100644 --- a/src/pkg/debug/elf/file_test.go +++ b/src/pkg/debug/elf/file_test.go @@ -5,6 +5,7 @@ package elf import ( + "debug/dwarf"; "encoding/binary"; "reflect"; "testing"; @@ -127,3 +128,53 @@ func TestOpen(t *testing.T) { } } } + +type relocationTest struct { + file string; + firstEntry *dwarf.Entry; +} + +var relocationTests = []relocationTest{ + relocationTest{ + "testdata/go-relocation-test-gcc441-x86-64.o", + &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(1)}, dwarf.Field{Attr: dwarf.AttrName, Val: "go-relocation-test.c"}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp"}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0)}}}, + }, + relocationTest{ + "testdata/go-relocation-test-gcc441-x86.o", + &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(1)}, dwarf.Field{Attr: dwarf.AttrName, Val: "t.c"}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp"}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: uint64(0x5)}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0)}}}, + }, + relocationTest{ + "testdata/go-relocation-test-gcc424-x86-64.o", + &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(1)}, dwarf.Field{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp"}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0)}}}, + }, +} + +func TestDWARFRelocations(t *testing.T) { + for i, test := range relocationTests { + f, err := Open(test.file); + if err != nil { + t.Error(err); + continue; + } + dwarf, err := f.DWARF(); + if err != nil { + t.Error(err); + continue; + } + reader := dwarf.Reader(); + // Checking only the first entry is sufficient since it has + // many different strings. If the relocation had failed, all + // the string offsets would be zero and all the strings would + // end up being the same. + firstEntry, err := reader.Next(); + if err != nil { + t.Error(err); + continue; + } + + if !reflect.DeepEqual(test.firstEntry, firstEntry) { + t.Errorf("#%d: mismatch: got:%#v want:%#v", i, firstEntry, test.firstEntry); + continue; + } + } +} diff --git a/src/pkg/debug/elf/testdata/go-relocation-test-gcc424-x86-64.o b/src/pkg/debug/elf/testdata/go-relocation-test-gcc424-x86-64.o new file mode 100644 index 00000000000000..a7c6d6e562c1c7 Binary files /dev/null and b/src/pkg/debug/elf/testdata/go-relocation-test-gcc424-x86-64.o differ diff --git a/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86-64.o b/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86-64.o new file mode 100644 index 00000000000000..2d37ab6e6e12cf Binary files /dev/null and b/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86-64.o differ diff --git a/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86.o b/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86.o new file mode 100644 index 00000000000000..0d59fe303b6dad Binary files /dev/null and b/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86.o differ diff --git a/src/pkg/encoding/binary/binary.go b/src/pkg/encoding/binary/binary.go index 836a43df0939d1..c49879c664b85e 100644 --- a/src/pkg/encoding/binary/binary.go +++ b/src/pkg/encoding/binary/binary.go @@ -19,6 +19,9 @@ type ByteOrder interface { Uint16(b []byte) uint16; Uint32(b []byte) uint32; Uint64(b []byte) uint64; + PutUint16([]byte, uint16); + PutUint32([]byte, uint32); + PutUint64([]byte, uint64); String() string; } @@ -35,15 +38,38 @@ func (littleEndian) Uint16(b []byte) uint16 { return uint16(b[0]) | uint16(b[1])<<8; } +func (littleEndian) PutUint16(b []byte, v uint16) { + b[0] = byte(v); + b[1] = byte(v>>8); +} + func (littleEndian) Uint32(b []byte) uint32 { return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24; } +func (littleEndian) PutUint32(b []byte, v uint32) { + b[0] = byte(v); + b[1] = byte(v>>8); + b[2] = byte(v>>16); + b[3] = byte(v>>24); +} + func (littleEndian) Uint64(b []byte) uint64 { return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56; } +func (littleEndian) PutUint64(b []byte, v uint64) { + b[0] = byte(v); + b[1] = byte(v>>8); + b[2] = byte(v>>16); + b[3] = byte(v>>24); + b[4] = byte(v>>32); + b[5] = byte(v>>40); + b[6] = byte(v>>48); + b[7] = byte(v>>56); +} + func (littleEndian) String() string { return "LittleEndian"; } @@ -58,15 +84,38 @@ func (bigEndian) Uint16(b []byte) uint16 { return uint16(b[1]) | uint16(b[0])<<8; } +func (bigEndian) PutUint16(b []byte, v uint16) { + b[0] = byte(v>>8); + b[1] = byte(v); +} + func (bigEndian) Uint32(b []byte) uint32 { return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24; } +func (bigEndian) PutUint32(b []byte, v uint32) { + b[0] = byte(v>>24); + b[1] = byte(v>>16); + b[2] = byte(v>>8); + b[3] = byte(v); +} + func (bigEndian) Uint64(b []byte) uint64 { return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56; } +func (bigEndian) PutUint64(b []byte, v uint64) { + b[0] = byte(v>>56); + b[1] = byte(v>>48); + b[2] = byte(v>>40); + b[3] = byte(v>>32); + b[4] = byte(v>>24); + b[5] = byte(v>>16); + b[6] = byte(v>>8); + b[7] = byte(v); +} + func (bigEndian) String() string { return "BigEndian"; }