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";
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 {
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));
package elf
import (
+ "bytes";
"debug/dwarf";
"encoding/binary";
"fmt";
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
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) {
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
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);
}
package elf
import (
+ "debug/dwarf";
"encoding/binary";
"reflect";
"testing";
}
}
}
+
+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;
+ }
+ }
+}
Uint16(b []byte) uint16;
Uint32(b []byte) uint32;
Uint64(b []byte) uint64;
+ PutUint16([]byte, uint16);
+ PutUint32([]byte, uint32);
+ PutUint64([]byte, uint64);
String() string;
}
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";
}
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";
}