return nil
}
+// dwarfFileIndex returns the DWARF file index value for the file associated
+// with pos.
+func dwarfFileIndex(pos src.Pos) int64 {
+ return int64(1 + pos.FileIndex())
+}
+
// Emit DWARF attributes and child DIEs for an inlined subroutine. The
// first attribute of an inlined subroutine DIE is a reference back to
// its corresponding 'abstract' DIE (containing location-independent
}
// Emit call file, line attrs.
- putattr(ctxt, s.Info, abbrev, DW_FORM_data4, DW_CLS_CONSTANT, int64(1+ic.CallPos.FileIndex()), nil) // 1-based file table
+ putattr(ctxt, s.Info, abbrev, DW_FORM_data4, DW_CLS_CONSTANT, dwarfFileIndex(ic.CallPos), nil)
form := int(expandPseudoForm(DW_FORM_udata_pseudo))
putattr(ctxt, s.Info, abbrev, form, DW_CLS_CONSTANT, int64(ic.CallPos.RelLine()), nil)
if isWrapper {
putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, int64(1), 0)
} else {
- putattr(ctxt, s.Info, abbrev, DW_FORM_data4, DW_CLS_CONSTANT, int64(1+s.StartPos.FileIndex()), nil) // 1-based file index
+ putattr(ctxt, s.Info, abbrev, DW_FORM_data4, DW_CLS_CONSTANT, dwarfFileIndex(s.StartPos), nil)
putattr(ctxt, s.Info, abbrev, DW_FORM_udata, DW_CLS_CONSTANT, int64(s.StartPos.RelLine()), nil)
var ev int64
// writeDirFileTables emits the portion of the DWARF line table
// prologue containing the include directories and file names,
-// described in section 6.2.4 of the DWARF 4 standard. It walks the
+// described in section 6.2.4 of the DWARF standard. It walks the
// filepaths for the unit to discover any common directories, which
// are emitted to the directory table first, then the file table is
// emitted after that.
+//
+// Note that there are some differences betwen DWARF versions 4 and 5
+// regarding how files and directories are handled; the chief item is
+// that in version 4 we have an implicit directory index 0 that holds
+// the compilation dir, and an implicit file index 0 that holds the
+// "primary" source file being compiled (with the assumption that
+// we'll get these two pieces of info from attributes on the
+// compilation unit DIE). DWARF version 5 does away with these
+// implicit entries; if you want to refer to a file or dir, you have
+// to mention it explicitly.
+//
+// So, let's say we have a Go package with import path
+// "github.com/fruit/bowl" with two source files, "apple.go" and
+// "orange.go". The directory and file table in version 4 might look
+// like:
+//
+// Dir Table:
+// 1 github.com/fruit/bowl
+//
+// File Table:
+// Entry Dir Time Size Name
+// 1 0 0 0 <autogenerated>
+// 2 1 0 0 apple.go
+// 3 1 0 0 orange.go
+//
+// With DWARF version 5, the file and directory tables might look like
+//
+// Dir Table:
+// 0 .
+// 1 github.com/fruit/bowl
+//
+// File Table:
+// Entry Dir Name
+// 0 0 ? /* see remark below about this entry */
+// 1 0 <autogenerated>
+// 2 1 apple.go
+// 3 1 orange.go
+//
+// Also worth noting that the line table prolog allows you to control
+// what items or fields appear in file and die entries (as opposed
+// to the fixed dir/time/size/name format for files in DWARF 4).
func (d *dwctxt) writeDirFileTables(unit *sym.CompilationUnit, lsu *loader.SymbolBuilder) {
type fileDir struct {
base string
dir int
}
dirNums := make(map[string]int)
- dirs := []string{""}
+ dirs := []string{"."}
+ dirNums["."] = 0
files := []fileDir{}
+ if buildcfg.Experiment.Dwarf5 {
+ // Add a dummy zero entry (not used for anything) so as to
+ // ensure that the first useful file index is 1, since that's
+ // the default value of the line table file register). Note
+ // the "?"; this was originally an empty string, but doing
+ // this triggers crashes in some versions of GDB, see
+ // https://sourceware.org/bugzilla/show_bug.cgi?id=30357 for
+ // the details.
+ files = append(files, fileDir{base: "?", dir: 0})
+ }
// Preprocess files to collect directories. This assumes that the
// file table is already de-duped.
dir := path.Dir(name)
dirIdx, ok := dirNums[dir]
if !ok && dir != "." {
- dirIdx = len(dirNums) + 1
+ dirIdx = len(dirNums)
dirNums[dir] = dirIdx
dirs = append(dirs, dir)
}
}
}
- // Emit directory section. This is a series of nul terminated
- // strings, followed by a single zero byte.
lsDwsym := dwSym(lsu.Sym())
- for k := 1; k < len(dirs); k++ {
- d.AddString(lsDwsym, dirs[k])
- }
- lsu.AddUint8(0) // terminator
+ if buildcfg.Experiment.Dwarf5 {
+ // Emit DWARF5 directory and file sections. The key added
+ // element here for version 5 is that we emit a small prolog
+ // describing the format of each file/dir entry, then a count
+ // of the entries, then the entries themselves.
+
+ // Dir table first...
+ lsu.AddUint8(1) // directory_entry_format_count
+ // each directory entry is just a single path string.
+ dwarf.Uleb128put(d, lsDwsym, dwarf.DW_LNCT_path)
+ dwarf.Uleb128put(d, lsDwsym, dwarf.DW_FORM_string)
+ // emit count, then dirs
+ dwarf.Uleb128put(d, lsDwsym, int64(len(dirs)))
+ for k := 0; k < len(dirs); k++ {
+ d.AddString(lsDwsym, dirs[k])
+ }
- // Emit file section.
- for k := 0; k < len(files); k++ {
- d.AddString(lsDwsym, files[k].base)
- dwarf.Uleb128put(d, lsDwsym, int64(files[k].dir))
- lsu.AddUint8(0) // mtime
- lsu.AddUint8(0) // length
+ // ... now file table. With DWARF version 5 we put out a
+ // prolog that describes the format of each file entry; our
+ // chosen format is a pair <F,D> where F is the file and D is
+ // the dir index (zero-based).
+ lsu.AddUint8(2) // file_entry_format_count
+ // each file entry is just a file path followed by dir index.
+ dwarf.Uleb128put(d, lsDwsym, dwarf.DW_LNCT_path)
+ dwarf.Uleb128put(d, lsDwsym, dwarf.DW_FORM_string)
+ dwarf.Uleb128put(d, lsDwsym, dwarf.DW_LNCT_directory_index)
+ dwarf.Uleb128put(d, lsDwsym, dwarf.DW_FORM_udata)
+ // emit count, then files
+ dwarf.Uleb128put(d, lsDwsym, int64(len(files)))
+ for k := 0; k < len(files); k++ {
+ d.AddString(lsDwsym, files[k].base)
+ dwarf.Uleb128put(d, lsDwsym, int64(files[k].dir))
+ }
+ } else {
+ // Emit DWARF4 directory and file sections.
+
+ // Dir table first. This just a series of nul terminated
+ // strings, followed by a single zero byte.
+ for k := 1; k < len(dirs); k++ {
+ d.AddString(lsDwsym, dirs[k])
+ }
+ lsu.AddUint8(0) // terminator needed in V4 case.
+
+ // File table next. This is a series of
+ // file/dirindex/mtime/length tuples (where dirindex is
+ // 1-based) followed by a single zero byte.
+ for k := 0; k < len(files); k++ {
+ d.AddString(lsDwsym, files[k].base)
+ dwarf.Uleb128put(d, lsDwsym, int64(files[k].dir))
+ lsu.AddUint8(0) // mtime
+ lsu.AddUint8(0) // length
+ }
+ lsu.AddUint8(0) // terminator
}
- lsu.AddUint8(0) // terminator
}
// writelines collects up and chains together the symbols needed to
unitLengthOffset := lsu.Size()
d.createUnitLength(lsu, 0) // unit_length (*), filled in at end
unitstart = lsu.Size()
- lsu.AddUint16(d.arch, 2) // dwarf version (appendix F) -- version 3 is incompatible w/ XCode 9.0's dsymutil, latest supported on OSX 10.12 as of 2018-05
+ if buildcfg.Experiment.Dwarf5 {
+ lsu.AddUint16(d.arch, 5)
+ // DWARF5 requires address_size and segment selector sizes
+ // here, both ubyte format.
+ lsu.AddUint8(uint8(d.arch.PtrSize))
+ lsu.AddUint8(0)
+ } else {
+ lsu.AddUint16(d.arch, 2) // dwarf version (appendix F) -- version 3 is incompatible w/ XCode 9.0's dsymutil, latest supported on OSX 10.12 as of 2018-05
+ }
headerLengthOffset := lsu.Size()
d.addDwarfAddrField(lsu, 0) // header_length (*), filled in at end
headerstart = lsu.Size()
- // cpos == unitstart + 4 + 2 + 4
- lsu.AddUint8(1) // minimum_instruction_length
+ lsu.AddUint8(1) // minimum_instruction_length
+ if buildcfg.Experiment.Dwarf5 {
+ lsu.AddUint8(1) // maximum_operations_per_instruction
+ }
lsu.AddUint8(is_stmt) // default_is_stmt
lsu.AddUint8(LINE_BASE & 0xFF) // line_base
lsu.AddUint8(LINE_RANGE) // line_range