mirror of https://github.com/golang/go.git
cmd/link, cmd/internal/dwarf: add DWARF5 line table support
This patch rolls out the necessary changes to migrate the DWARF line table support in the compiler and linker to DWARF version 5, gated by the "dwarf5" GOEXPERIMENT. DWARF version 5 includes a number of changes to the line table, notably a revamped prolog section and a change in the indexing system used to refer to files and directories within the line table program. Specifically, prior to DWARF 4 a compilation's directory table was considered to have an implicit zero entry containing the compilation directory of the translation unit (package), and the file table was considered to have an implicit zero entry storing the "primary source file" (stored in the compilation unit DIE name). DWARF 5 does away with these implicity entries meaning that files and dirs are now effectively a 0-based index. Updates #26379. Change-Id: I9b4f1be5415aacec1ba57366d60bd48819c56ea5 Reviewed-on: https://go-review.googlesource.com/c/go/+/633879 Reviewed-by: Alessandro Arzilli <alessandro.arzilli@gmail.com> Reviewed-by: David Chase <drchase@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com>
This commit is contained in:
parent
41a30dd192
commit
767e7680ec
|
|
@ -1207,6 +1207,12 @@ func PutAbstractFunc(ctxt Context, s *FnState) error {
|
|||
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
|
||||
|
|
@ -1240,7 +1246,7 @@ func putInlinedFunc(ctxt Context, s *FnState, callIdx int) error {
|
|||
}
|
||||
|
||||
// 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)
|
||||
|
||||
|
|
@ -1343,7 +1349,7 @@ func PutDefaultFunc(ctxt Context, s *FnState, isWrapper bool) error {
|
|||
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
|
||||
|
|
|
|||
|
|
@ -1212,18 +1212,70 @@ func expandFile(fname string) string {
|
|||
|
||||
// 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.
|
||||
|
|
@ -1241,7 +1293,7 @@ func (d *dwctxt) writeDirFileTables(unit *sym.CompilationUnit, lsu *loader.Symbo
|
|||
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)
|
||||
}
|
||||
|
|
@ -1260,22 +1312,61 @@ func (d *dwctxt) writeDirFileTables(unit *sym.CompilationUnit, lsu *loader.Symbo
|
|||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
// 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
|
||||
// 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])
|
||||
}
|
||||
|
||||
// ... 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
|
||||
|
|
@ -1304,13 +1395,23 @@ func (d *dwctxt) writelines(unit *sym.CompilationUnit, lineProlog loader.Sym) []
|
|||
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
|
||||
|
|
|
|||
Loading…
Reference in New Issue