Commit 420350df authored by Zhou Yaochen's avatar Zhou Yaochen
Browse files

update ignore

parent fc1f6363

Too many changes to show.

To preserve performance only 723 of 723+ files are displayed.
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package arch contains architecture-specific definitions.
package arch
import (
"encoding/binary"
"math"
)
const MaxBreakpointSize = 4 // TODO
// Architecture defines the architecture-specific details for a given machine.
type Architecture struct {
// BreakpointSize is the size of a breakpoint instruction, in bytes.
BreakpointSize int
// IntSize is the size of the int type, in bytes.
IntSize int
// PointerSize is the size of a pointer, in bytes.
PointerSize int
// ByteOrder is the byte order for ints and pointers.
ByteOrder binary.ByteOrder
// FloatByteOrder is the byte order for floats.
FloatByteOrder binary.ByteOrder
BreakpointInstr [MaxBreakpointSize]byte
}
func (a *Architecture) Int(buf []byte) int64 {
return int64(a.Uint(buf))
}
func (a *Architecture) Uint(buf []byte) uint64 {
if len(buf) != a.IntSize {
panic("bad IntSize")
}
switch a.IntSize {
case 4:
return uint64(a.ByteOrder.Uint32(buf[:4]))
case 8:
return a.ByteOrder.Uint64(buf[:8])
}
panic("no IntSize")
}
func (a *Architecture) Int16(buf []byte) int16 {
return int16(a.Uint16(buf))
}
func (a *Architecture) Int32(buf []byte) int32 {
return int32(a.Uint32(buf))
}
func (a *Architecture) Int64(buf []byte) int64 {
return int64(a.Uint64(buf))
}
func (a *Architecture) Uint16(buf []byte) uint16 {
return a.ByteOrder.Uint16(buf)
}
func (a *Architecture) Uint32(buf []byte) uint32 {
return a.ByteOrder.Uint32(buf)
}
func (a *Architecture) Uint64(buf []byte) uint64 {
return a.ByteOrder.Uint64(buf)
}
func (a *Architecture) IntN(buf []byte) int64 {
if len(buf) == 0 {
return 0
}
x := int64(0)
if a.ByteOrder == binary.LittleEndian {
i := len(buf) - 1
x = int64(int8(buf[i])) // sign-extended
for i--; i >= 0; i-- {
x <<= 8
x |= int64(buf[i]) // not sign-extended
}
} else {
x = int64(int8(buf[0])) // sign-extended
for i := 1; i < len(buf); i++ {
x <<= 8
x |= int64(buf[i]) // not sign-extended
}
}
return x
}
func (a *Architecture) UintN(buf []byte) uint64 {
u := uint64(0)
if a.ByteOrder == binary.LittleEndian {
shift := uint(0)
for _, c := range buf {
u |= uint64(c) << shift
shift += 8
}
} else {
for _, c := range buf {
u <<= 8
u |= uint64(c)
}
}
return u
}
func (a *Architecture) Uintptr(buf []byte) uint64 {
if len(buf) != a.PointerSize {
panic("bad PointerSize")
}
switch a.PointerSize {
case 4:
return uint64(a.ByteOrder.Uint32(buf[:4]))
case 8:
return a.ByteOrder.Uint64(buf[:8])
}
panic("no PointerSize")
}
func (a *Architecture) Float32(buf []byte) float32 {
if len(buf) != 4 {
panic("bad float32 size")
}
return math.Float32frombits(a.FloatByteOrder.Uint32(buf))
}
func (a *Architecture) Float64(buf []byte) float64 {
if len(buf) != 8 {
panic("bad float64 size")
}
return math.Float64frombits(a.FloatByteOrder.Uint64(buf))
}
func (a *Architecture) Complex64(buf []byte) complex64 {
if len(buf) != 8 {
panic("bad complex64 size")
}
return complex(a.Float32(buf[0:4]), a.Float32(buf[4:8]))
}
func (a *Architecture) Complex128(buf []byte) complex128 {
if len(buf) != 16 {
panic("bad complex128 size")
}
return complex(a.Float64(buf[0:8]), a.Float64(buf[8:16]))
}
var AMD64 = Architecture{
BreakpointSize: 1,
IntSize: 8,
PointerSize: 8,
ByteOrder: binary.LittleEndian,
FloatByteOrder: binary.LittleEndian,
BreakpointInstr: [MaxBreakpointSize]byte{0xCC}, // INT 3
}
var X86 = Architecture{
BreakpointSize: 1,
IntSize: 4,
PointerSize: 4,
ByteOrder: binary.LittleEndian,
FloatByteOrder: binary.LittleEndian,
BreakpointInstr: [MaxBreakpointSize]byte{0xCC}, // INT 3
}
var ARM = Architecture{
BreakpointSize: 4, // TODO
IntSize: 4,
PointerSize: 4,
ByteOrder: binary.LittleEndian,
FloatByteOrder: binary.LittleEndian, // TODO
BreakpointInstr: [MaxBreakpointSize]byte{0x00, 0x00, 0x00, 0x00}, // TODO
}
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// debugproxy connects to the target binary, and serves an RPC interface using
// the types in server/protocol to access and control it.
// +build linux
package main
import (
"flag"
"fmt"
"log"
"net/rpc"
"os"
"cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server"
)
var (
textFlag = flag.String("text", "", "file name of binary being debugged")
)
func main() {
log.SetFlags(0)
log.SetPrefix("debugproxy: ")
flag.Parse()
if *textFlag == "" {
flag.Usage()
os.Exit(2)
}
s, err := server.New(*textFlag)
if err != nil {
fmt.Printf("server.New: %v\n", err)
os.Exit(2)
}
err = rpc.Register(s)
if err != nil {
fmt.Printf("rpc.Register: %v\n", err)
os.Exit(2)
}
fmt.Println("OK")
log.Print("starting server")
rpc.ServeConn(&rwc{
os.Stdin,
os.Stdout,
})
log.Print("server finished")
}
// rwc creates a single io.ReadWriteCloser from a read side and a write side.
// It allows us to do RPC using standard in and standard out.
type rwc struct {
r *os.File
w *os.File
}
func (rwc *rwc) Read(p []byte) (int, error) {
return rwc.r.Read(p)
}
func (rwc *rwc) Write(p []byte) (int, error) {
return rwc.w.Write(p)
}
func (rwc *rwc) Close() error {
rerr := rwc.r.Close()
werr := rwc.w.Close()
if rerr != nil {
return rerr
}
return werr
}
ptrace and NTPL, the missing manpage
== Signals ==
A signal sent to a ptrace'd process or thread causes only the thread
that receives it to stop and report to the attached process.
Use tgkill to target a signal (for example, SIGSTOP) at a particular
thread. If you use kill, the signal could be delivered to another
thread in the same process.
Note that SIGSTOP differs from its usual behavior when a process is
being traced. Usually, a SIGSTOP sent to any thread in a thread group
will stop all threads in the thread group. When a thread is traced,
however, a SIGSTOP affects only the receiving thread (and any other
threads in the thread group that are not traced).
SIGKILL behaves like it does for non-traced processes. It affects all
threads in the process and terminates them without the WSTOPSIG event
generated by other signals. However, if PTRACE_O_TRACEEXIT is set,
the attached process will still receive PTRACE_EVENT_EXIT events
before receiving WIFSIGNALED events.
See "Following thread death" for a caveat regarding signal delivery to
zombie threads.
== Waiting on threads ==
Cloned threads in ptrace'd processes are treated similarly to cloned
threads in your own process. Thus, you must use the __WALL option in
order to receive notifications from threads created by the child
process. Similarly, the __WCLONE option will wait only on
notifications from threads created by the child process and *not* on
notifications from the initial child thread.
Even when waiting on a specific thread's PID using waitpid or similar,
__WALL or __WCLONE is necessary or waitpid will return ECHILD.
== Attaching to existing threads ==
libthread_db (which gdb uses), attaches to existing threads by pulling
the pthread data structures out of the traced process. The much
easier way is to traverse the /proc/PID/task directory, though it's
unclear how the semantics of these two approaches differ.
Unfortunately, if the main thread has exited (but the overall process
has not), it sticks around as a zombie process. This zombie will
appear in the /proc/PID/task directory, but trying to attach to it
will yield EPERM. In this case, the third field of the
/proc/PID/task/PID/stat file will be "Z". Attempting to open the stat
file is also a convenient way to detect races between listing the task
directory and the thread exiting. Coincidentally, gdb will simply
fail to attach to a process whose main thread is a zombie.
Because new threads may be created while the debugger is in the
process of attaching to existing threads, the debugger must repeatedly
re-list the task directory until it has attached to (and thus stopped)
every thread listed.
In order to follow new threads created by existing threads,
PTRACE_O_TRACECLONE must be set on each thread attached to.
== Following new threads ==
With the child process stopped, use PTRACE_SETOPTIONS to set the
PTRACE_O_TRACECLONE option. This option is per-thread, and thus must
be set on each existing thread individually. When an existing thread
with PTRACE_O_TRACECLONE set spawns a new thread, the existing thread
will stop with (SIGTRAP | PTRACE_EVENT_CLONE << 8) and the PID of the
new thread can be retrieved with PTRACE_GETEVENTMSG on the creating
thread. At this time, the new thread will exist, but will initially
be stopped with a SIGSTOP. The new thread will automatically be
traced and will inherit the PTRACE_O_TRACECLONE option from its
parent. The attached process should wait on the new thread to receive
the SIGSTOP notification.
When using waitpid(-1, ...), don't rely on the parent thread reporting
a SIGTRAP before receiving the SIGSTOP from the new child thread.
Without PTRACE_O_TRACECLONE, newly cloned threads will not be
ptrace'd. As a result, signals received by new threads will be
handled in the usual way, which may affect the parent and in turn
appear to the attached process, but attributed to the parent (possibly
in unexpected ways).
== Following thread death ==
If any thread with the PTRACE_O_TRACEEXIT option set exits (either by
returning or pthread_exit'ing), the tracing process will receive an
immediate PTRACE_EVENT_EXIT. At this point, the thread will still
exist. The exit status, encoded as for wait, can be queried using
PTRACE_GETEVENTMSG on the exiting thread's PID. The thread should be
continued so it can actually exit, after which its wait behavior is
the same as for a thread without the PTRACE_O_TRACEEXIT option.
If a non-main thread exits (either by returning or pthread_exit'ing),
its corresponding process will also exit, producing a WIFEXITED event
(after the process is continued from a possible PTRACE_EVENT_EXIT
event). It is *not* necessary for another thread to ptrace_join for
this to happen.
If the main thread exits by returning, then all threads will exit,
first generating a PTRACE_EVENT_EXIT event for each thread if
appropriate, then producing a WIFEXITED event for each thread.
If the main thread exits using pthread_exit, then it enters a
non-waitable zombie state. It will still produce an immediate
PTRACE_O_TRACEEXIT event, but the WIFEXITED event will be delayed
until the entire process exits. This state exists so that shells
don't think the process is done until all of the threads have exited.
Unfortunately, signals cannot be delivered to non-waitable zombies.
Most notably, SIGSTOP cannot be delivered; as a result, when you
broadcast SIGSTOP to all of the threads, you must not wait for
non-waitable zombies to stop. Furthermore, any ptrace command on a
non-waitable zombie, including PTRACE_DETACH, will return ESRCH.
== Multi-threaded debuggers ==
If the debugger itself is multi-threaded, ptrace calls must come from
the same thread that originally attached to the remote thread. The
kernel simply compares the PID of the caller of ptrace against the
tracer PID of the process passed to ptrace. Because each debugger
thread has a different PID, calling ptrace from a different thread
might as well be calling it from a different process and the kernel
will return ESRCH.
wait, on the other hand, does not have this restriction. Any debugger
thread can wait on any thread in the attached process.
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Buffered reading and decoding of DWARF data streams.
package dwarf
import (
"encoding/binary"
"fmt"
"strconv"
)
// Data buffer being decoded.
type buf struct {
dwarf *Data
order binary.ByteOrder
format dataFormat
name string
off Offset
data []byte
err error
}
// Data format, other than byte order. This affects the handling of
// certain field formats.
type dataFormat interface {
// DWARF version number. Zero means unknown.
version() int
// 64-bit DWARF format?
dwarf64() (dwarf64 bool, isKnown bool)
// Size of an address, in bytes. Zero means unknown.
addrsize() int
}
// Some parts of DWARF have no data format, e.g., abbrevs.
type unknownFormat struct{}
func (u unknownFormat) version() int {
return 0
}
func (u unknownFormat) dwarf64() (bool, bool) {
return false, false
}
func (u unknownFormat) addrsize() int {
return 0
}
func makeBuf(d *Data, format dataFormat, name string, off Offset, data []byte) buf {
return buf{d, d.order, format, name, off, data, nil}
}
func (b *buf) slice(length int) buf {
n := *b
data := b.data
b.skip(length) // Will validate length.
n.data = data[:length]
return n
}
func (b *buf) uint8() uint8 {
if len(b.data) < 1 {
b.error("underflow")
return 0
}
val := b.data[0]
b.data = b.data[1:]
b.off++
return val
}
func (b *buf) bytes(n int) []byte {
if len(b.data) < n {
b.error("underflow")
return nil
}
data := b.data[0:n]
b.data = b.data[n:]
b.off += Offset(n)
return data
}
func (b *buf) skip(n int) { b.bytes(n) }
// string returns the NUL-terminated (C-like) string at the start of the buffer.
// The terminal NUL is discarded.
func (b *buf) string() string {
for i := 0; i < len(b.data); i++ {
if b.data[i] == 0 {
s := string(b.data[0:i])
b.data = b.data[i+1:]
b.off += Offset(i + 1)
return s
}
}
b.error("underflow")
return ""
}
func (b *buf) uint16() uint16 {
a := b.bytes(2)
if a == nil {
return 0
}
return b.order.Uint16(a)
}
func (b *buf) uint32() uint32 {
a := b.bytes(4)
if a == nil {
return 0
}
return b.order.Uint32(a)
}
func (b *buf) uint64() uint64 {
a := b.bytes(8)
if a == nil {
return 0
}
return b.order.Uint64(a)
}
// Read a varint, which is 7 bits per byte, little endian.
// the 0x80 bit means read another byte.
func (b *buf) varint() (c uint64, bits uint) {
for i := 0; i < len(b.data); i++ {
byte := b.data[i]
c |= uint64(byte&0x7F) << bits
bits += 7
if byte&0x80 == 0 {
b.off += Offset(i + 1)
b.data = b.data[i+1:]
return c, bits
}
}
return 0, 0
}
// Unsigned int is just a varint.
func (b *buf) uint() uint64 {
x, _ := b.varint()
return x
}
// Signed int is a sign-extended varint.
func (b *buf) int() int64 {
ux, bits := b.varint()
x := int64(ux)
if x&(1<<(bits-1)) != 0 {
x |= -1 << bits
}
return x
}
// Address-sized uint.
func (b *buf) addr() uint64 {
switch b.format.addrsize() {
case 1:
return uint64(b.uint8())
case 2:
return uint64(b.uint16())
case 4:
return uint64(b.uint32())
case 8:
return uint64(b.uint64())
}
b.error("unknown address size")
return 0
}
// assertEmpty checks that everything has been read from b.
func (b *buf) assertEmpty() {
if len(b.data) == 0 {
return
}
if len(b.data) > 5 {
b.error(fmt.Sprintf("unexpected extra data: %x...", b.data[0:5]))
}
b.error(fmt.Sprintf("unexpected extra data: %x", b.data))
}
func (b *buf) error(s string) {
if b.err == nil {
b.data = nil
b.err = DecodeError{b.name, b.off, s}
}
}
type DecodeError struct {
Name string
Offset Offset
Err string
}
func (e DecodeError) Error() string {
return "decoding dwarf section " + e.Name + " at offset 0x" + strconv.FormatInt(int64(e.Offset), 16) + ": " + e.Err
}
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dwarf
import (
"fmt"
"sort"
)
// pcToFuncEntries maps PC ranges to function entries.
//
// Each element contains a *Entry for a function and its corresponding start PC.
// If we know the address one past the last instruction of a function, and it is
// not equal to the start address of the next function, we mark that with
// another element containing that address and a nil entry. The elements are
// sorted by PC. Among elements with the same PC, those with non-nil *Entry
// are put earlier.
type pcToFuncEntries []pcToFuncEntry
type pcToFuncEntry struct {
pc uint64
entry *Entry
}
func (p pcToFuncEntries) Len() int { return len(p) }
func (p pcToFuncEntries) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p pcToFuncEntries) Less(i, j int) bool {
if p[i].pc != p[j].pc {
return p[i].pc < p[j].pc
}
return p[i].entry != nil && p[j].entry == nil
}
// nameCache maps each symbol name to a linked list of the entries with that name.
type nameCache map[string]*nameCacheEntry
type nameCacheEntry struct {
entry *Entry
link *nameCacheEntry
}
// pcToLineEntries maps PCs to line numbers.
//
// It is a slice of (PC, line, file number) triples, sorted by PC. The file
// number is an index into the source files slice.
// If (PC1, line1, file1) and (PC2, line2, file2) are two consecutive elements,
// then the span of addresses [PC1, PC2) belongs to (line1, file1). If an
// element's file number is zero, it only marks the end of a span.
//
// TODO: could save memory by changing pcToLineEntries and lineToPCEntries to use
// interval trees containing references into .debug_line.
type pcToLineEntries []pcToLineEntry
type pcToLineEntry struct {
pc uint64
line uint64
file uint64
}
func (p pcToLineEntries) Len() int { return len(p) }
func (p pcToLineEntries) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p pcToLineEntries) Less(i, j int) bool {
if p[i].pc != p[j].pc {
return p[i].pc < p[j].pc
}
return p[i].file > p[j].file
}
// byFileLine is used temporarily while building lineToPCEntries.
type byFileLine []pcToLineEntry
func (b byFileLine) Len() int { return len(b) }
func (b byFileLine) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b byFileLine) Less(i, j int) bool {
if b[i].file != b[j].file {
return b[i].file < b[j].file
}
return b[i].line < b[j].line
}
// lineToPCEntries maps line numbers to breakpoint addresses.
//
// The slice contains, for each source file in Data, a slice of (line, PC)
// pairs, sorted by line. Note that there may be more than one PC for a line.
type lineToPCEntries [][]lineToPCEntry
type lineToPCEntry struct {
line uint64
pc uint64
}
func (d *Data) buildLineToPCCache(pclfs pcToLineEntries) {
// TODO: only include lines where is_stmt is true
sort.Sort(byFileLine(pclfs))
// Make a slice of (line, PC) pairs for each (non-zero) file.
var (
c = make(lineToPCEntries, len(d.sourceFiles))
curSlice []lineToPCEntry
)
for i, pclf := range pclfs {
if pclf.file == 0 {
// This entry indicated the end of an instruction sequence, not a breakpoint.
continue
}
curSlice = append(curSlice, lineToPCEntry{line: pclf.line, pc: pclf.pc})
if i+1 == len(pclfs) || pclf.file != pclfs[i+1].file {
// curSlice now contains all of the entries for pclf.file.
if pclf.file > 0 && pclf.file < uint64(len(c)) {
c[pclf.file] = curSlice
}
curSlice = nil
}
}
d.lineToPCEntries = c
}
func (d *Data) buildPCToLineCache(cache pcToLineEntries) {
// Sort cache by PC (in increasing order), then by file number (in decreasing order).
sort.Sort(cache)
// Build a copy without redundant entries.
var out pcToLineEntries
for i, pclf := range cache {
if i > 0 && pclf.pc == cache[i-1].pc {
// This entry is for the same PC as the previous entry.
continue
}
if i > 0 && pclf.file == cache[i-1].file && pclf.line == cache[i-1].line {
// This entry is for the same file and line as the previous entry.
continue
}
out = append(out, pclf)
}
d.pcToLineEntries = out
}
// buildLineCaches constructs d.sourceFiles, d.lineToPCEntries, d.pcToLineEntries.
func (d *Data) buildLineCaches() error {
var cache pcToLineEntries
r := d.Reader()
for {
entry, err := r.Next()
if err != nil {
return err
}
if entry == nil {
break
}
if entry.Tag != TagCompileUnit {
r.SkipChildren()
continue
}
// Get the offset of this unit's line number program in .debug_line.
offset, ok := entry.Val(AttrStmtList).(int64)
if !ok {
return fmt.Errorf("AttrStmtList not present or not int64 for unit %d", r.unit)
}
buf := makeBuf(d, &d.unit[r.unit], "line", Offset(offset), d.line[offset:])
var m lineMachine
if err := m.parseHeader(&buf); err != nil {
return err
}
fileNumOffset := uint64(len(d.sourceFiles))
for _, f := range m.header.file {
d.sourceFiles = append(d.sourceFiles, f.name)
}
fn := func(m *lineMachine) bool {
if m.endSequence {
cache = append(cache, pcToLineEntry{
pc: m.address,
line: 0,
file: 0,
})
} else {
// m.file is a 1-based index into the files of this line number program's
// header. Translate it to a 0-based index into d.sourceFiles.
fnum := fileNumOffset + m.file - 1
cache = append(cache, pcToLineEntry{
pc: m.address,
line: m.line,
file: fnum,
})
}
return true
}
if err := m.evalCompilationUnit(&buf, fn); err != nil {
return err
}
}
d.buildLineToPCCache(cache)
d.buildPCToLineCache(cache)
return nil
}
// buildInfoCaches initializes nameCache and pcToFuncEntries by walking the
// top-level entries under each compile unit. It swallows any errors in parsing.
func (d *Data) buildInfoCaches() {
// TODO: record errors somewhere?
d.nameCache = make(map[string]*nameCacheEntry)
var pcToFuncEntries pcToFuncEntries
r := d.Reader()
loop:
for {
entry, err := r.Next()
if entry == nil || err != nil {
break loop
}
if entry.Tag != TagCompileUnit /* DW_TAG_compile_unit */ {
r.SkipChildren()
continue
}
for {
entry, err := r.Next()
if entry == nil || err != nil {
break loop
}
if entry.Tag == 0 {
// End of children of current compile unit.
break
}
r.SkipChildren()
// Update name-to-entry cache.
if name, ok := entry.Val(AttrName).(string); ok {
d.nameCache[name] = &nameCacheEntry{entry: entry, link: d.nameCache[name]}
}
// If this entry is a function, update PC-to-containing-function cache.
if entry.Tag != TagSubprogram /* DW_TAG_subprogram */ {
continue
}
// DW_AT_low_pc, if present, is the address of the first instruction of
// the function.
lowpc, ok := entry.Val(AttrLowpc).(uint64)
if !ok {
continue
}
pcToFuncEntries = append(pcToFuncEntries, pcToFuncEntry{lowpc, entry})
// DW_AT_high_pc, if present (TODO: and of class address) is the address
// one past the last instruction of the function.
highpc, ok := entry.Val(AttrHighpc).(uint64)
if !ok {
continue
}
pcToFuncEntries = append(pcToFuncEntries, pcToFuncEntry{highpc, nil})
}
}
// Sort elements by PC. If there are multiple elements with the same PC,
// those with non-nil *Entry are placed earlier.
sort.Sort(pcToFuncEntries)
// Copy only the first element for each PC to out.
n := 0
for i, ce := range pcToFuncEntries {
if i == 0 || ce.pc != pcToFuncEntries[i-1].pc {
n++
}
}
out := make([]pcToFuncEntry, 0, n)
for i, ce := range pcToFuncEntries {
if i == 0 || ce.pc != pcToFuncEntries[i-1].pc {
out = append(out, ce)
}
}
d.pcToFuncEntries = out
}
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Constants
package dwarf
import "strconv"
// An Attr identifies the attribute type in a DWARF Entry's Field.
type Attr uint32
const (
AttrSibling Attr = 0x01
AttrLocation Attr = 0x02
AttrName Attr = 0x03
AttrOrdering Attr = 0x09
AttrByteSize Attr = 0x0B
AttrBitOffset Attr = 0x0C
AttrBitSize Attr = 0x0D
AttrStmtList Attr = 0x10
AttrLowpc Attr = 0x11
AttrHighpc Attr = 0x12
AttrLanguage Attr = 0x13
AttrDiscr Attr = 0x15
AttrDiscrValue Attr = 0x16
AttrVisibility Attr = 0x17
AttrImport Attr = 0x18
AttrStringLength Attr = 0x19
AttrCommonRef Attr = 0x1A
AttrCompDir Attr = 0x1B
AttrConstValue Attr = 0x1C
AttrContainingType Attr = 0x1D
AttrDefaultValue Attr = 0x1E
AttrInline Attr = 0x20
AttrIsOptional Attr = 0x21
AttrLowerBound Attr = 0x22
AttrProducer Attr = 0x25
AttrPrototyped Attr = 0x27
AttrReturnAddr Attr = 0x2A
AttrStartScope Attr = 0x2C
AttrStrideSize Attr = 0x2E
AttrUpperBound Attr = 0x2F
AttrAbstractOrigin Attr = 0x31
AttrAccessibility Attr = 0x32
AttrAddrClass Attr = 0x33
AttrArtificial Attr = 0x34
AttrBaseTypes Attr = 0x35
AttrCalling Attr = 0x36
AttrCount Attr = 0x37
AttrDataMemberLoc Attr = 0x38
AttrDeclColumn Attr = 0x39
AttrDeclFile Attr = 0x3A
AttrDeclLine Attr = 0x3B
AttrDeclaration Attr = 0x3C
AttrDiscrList Attr = 0x3D
AttrEncoding Attr = 0x3E
AttrExternal Attr = 0x3F
AttrFrameBase Attr = 0x40
AttrFriend Attr = 0x41
AttrIdentifierCase Attr = 0x42
AttrMacroInfo Attr = 0x43
AttrNamelistItem Attr = 0x44
AttrPriority Attr = 0x45
AttrSegment Attr = 0x46
AttrSpecification Attr = 0x47
AttrStaticLink Attr = 0x48
AttrType Attr = 0x49
AttrUseLocation Attr = 0x4A
AttrVarParam Attr = 0x4B
AttrVirtuality Attr = 0x4C
AttrVtableElemLoc Attr = 0x4D
AttrAllocated Attr = 0x4E
AttrAssociated Attr = 0x4F
AttrDataLocation Attr = 0x50
AttrStride Attr = 0x51
AttrEntrypc Attr = 0x52
AttrUseUTF8 Attr = 0x53
AttrExtension Attr = 0x54
AttrRanges Attr = 0x55
AttrTrampoline Attr = 0x56
AttrCallColumn Attr = 0x57
AttrCallFile Attr = 0x58
AttrCallLine Attr = 0x59
AttrDescription Attr = 0x5A
// Go-specific attributes.
AttrGoKind Attr = 0x2900
AttrGoKey Attr = 0x2901
AttrGoElem Attr = 0x2902
AttrGoEmbeddedField Attr = 0x2903
)
var attrNames = [...]string{
AttrSibling: "Sibling",
AttrLocation: "Location",
AttrName: "Name",
AttrOrdering: "Ordering",
AttrByteSize: "ByteSize",
AttrBitOffset: "BitOffset",
AttrBitSize: "BitSize",
AttrStmtList: "StmtList",
AttrLowpc: "Lowpc",
AttrHighpc: "Highpc",
AttrLanguage: "Language",
AttrDiscr: "Discr",
AttrDiscrValue: "DiscrValue",
AttrVisibility: "Visibility",
AttrImport: "Import",
AttrStringLength: "StringLength",
AttrCommonRef: "CommonRef",
AttrCompDir: "CompDir",
AttrConstValue: "ConstValue",
AttrContainingType: "ContainingType",
AttrDefaultValue: "DefaultValue",
AttrInline: "Inline",
AttrIsOptional: "IsOptional",
AttrLowerBound: "LowerBound",
AttrProducer: "Producer",
AttrPrototyped: "Prototyped",
AttrReturnAddr: "ReturnAddr",
AttrStartScope: "StartScope",
AttrStrideSize: "StrideSize",
AttrUpperBound: "UpperBound",
AttrAbstractOrigin: "AbstractOrigin",
AttrAccessibility: "Accessibility",
AttrAddrClass: "AddrClass",
AttrArtificial: "Artificial",
AttrBaseTypes: "BaseTypes",
AttrCalling: "Calling",
AttrCount: "Count",
AttrDataMemberLoc: "DataMemberLoc",
AttrDeclColumn: "DeclColumn",
AttrDeclFile: "DeclFile",
AttrDeclLine: "DeclLine",
AttrDeclaration: "Declaration",
AttrDiscrList: "DiscrList",
AttrEncoding: "Encoding",
AttrExternal: "External",
AttrFrameBase: "FrameBase",
AttrFriend: "Friend",
AttrIdentifierCase: "IdentifierCase",
AttrMacroInfo: "MacroInfo",
AttrNamelistItem: "NamelistItem",
AttrPriority: "Priority",
AttrSegment: "Segment",
AttrSpecification: "Specification",
AttrStaticLink: "StaticLink",
AttrType: "Type",
AttrUseLocation: "UseLocation",
AttrVarParam: "VarParam",
AttrVirtuality: "Virtuality",
AttrVtableElemLoc: "VtableElemLoc",
AttrAllocated: "Allocated",
AttrAssociated: "Associated",
AttrDataLocation: "DataLocation",
AttrStride: "Stride",
AttrEntrypc: "Entrypc",
AttrUseUTF8: "UseUTF8",
AttrExtension: "Extension",
AttrRanges: "Ranges",
AttrTrampoline: "Trampoline",
AttrCallColumn: "CallColumn",
AttrCallFile: "CallFile",
AttrCallLine: "CallLine",
AttrDescription: "Description",
}
func (a Attr) String() string {
if int(a) < len(attrNames) {
s := attrNames[a]
if s != "" {
return s
}
}
switch a {
case AttrGoKind:
return "GoKind"
case AttrGoKey:
return "GoKey"
case AttrGoElem:
return "GoElem"
case AttrGoEmbeddedField:
return "GoEmbeddedField"
}
return strconv.Itoa(int(a))
}
func (a Attr) GoString() string {
if int(a) < len(attrNames) {
s := attrNames[a]
if s != "" {
return "dwarf.Attr" + s
}
}
return "dwarf.Attr(" + strconv.FormatInt(int64(a), 10) + ")"
}
// A format is a DWARF data encoding format.
type format uint32
const (
// value formats
formAddr format = 0x01
formDwarfBlock2 format = 0x03
formDwarfBlock4 format = 0x04
formData2 format = 0x05
formData4 format = 0x06
formData8 format = 0x07
formString format = 0x08
formDwarfBlock format = 0x09
formDwarfBlock1 format = 0x0A
formData1 format = 0x0B
formFlag format = 0x0C
formSdata format = 0x0D
formStrp format = 0x0E
formUdata format = 0x0F
formRefAddr format = 0x10
formRef1 format = 0x11
formRef2 format = 0x12
formRef4 format = 0x13
formRef8 format = 0x14
formRefUdata format = 0x15
formIndirect format = 0x16
// The following are new in DWARF 4.
formSecOffset format = 0x17
formExprloc format = 0x18
formFlagPresent format = 0x19
formRefSig8 format = 0x20
// Extensions for multi-file compression (.dwz)
// http://www.dwarfstd.org/ShowIssue.php?issue=120604.1
formGnuRefAlt format = 0x1f20
formGnuStrpAlt format = 0x1f21
)
// A Tag is the classification (the type) of an Entry.
type Tag uint32
const (
TagArrayType Tag = 0x01
TagClassType Tag = 0x02
TagEntryPoint Tag = 0x03
TagEnumerationType Tag = 0x04
TagFormalParameter Tag = 0x05
TagImportedDeclaration Tag = 0x08
TagLabel Tag = 0x0A
TagLexDwarfBlock Tag = 0x0B
TagMember Tag = 0x0D
TagPointerType Tag = 0x0F
TagReferenceType Tag = 0x10
TagCompileUnit Tag = 0x11
TagStringType Tag = 0x12
TagStructType Tag = 0x13
TagSubroutineType Tag = 0x15
TagTypedef Tag = 0x16
TagUnionType Tag = 0x17
TagUnspecifiedParameters Tag = 0x18
TagVariant Tag = 0x19
TagCommonDwarfBlock Tag = 0x1A
TagCommonInclusion Tag = 0x1B
TagInheritance Tag = 0x1C
TagInlinedSubroutine Tag = 0x1D
TagModule Tag = 0x1E
TagPtrToMemberType Tag = 0x1F
TagSetType Tag = 0x20
TagSubrangeType Tag = 0x21
TagWithStmt Tag = 0x22
TagAccessDeclaration Tag = 0x23
TagBaseType Tag = 0x24
TagCatchDwarfBlock Tag = 0x25
TagConstType Tag = 0x26
TagConstant Tag = 0x27
TagEnumerator Tag = 0x28
TagFileType Tag = 0x29
TagFriend Tag = 0x2A
TagNamelist Tag = 0x2B
TagNamelistItem Tag = 0x2C
TagPackedType Tag = 0x2D
TagSubprogram Tag = 0x2E
TagTemplateTypeParameter Tag = 0x2F
TagTemplateValueParameter Tag = 0x30
TagThrownType Tag = 0x31
TagTryDwarfBlock Tag = 0x32
TagVariantPart Tag = 0x33
TagVariable Tag = 0x34
TagVolatileType Tag = 0x35
// The following are new in DWARF 3.
TagDwarfProcedure Tag = 0x36
TagRestrictType Tag = 0x37
TagInterfaceType Tag = 0x38
TagNamespace Tag = 0x39
TagImportedModule Tag = 0x3A
TagUnspecifiedType Tag = 0x3B
TagPartialUnit Tag = 0x3C
TagImportedUnit Tag = 0x3D
TagMutableType Tag = 0x3E // Later removed from DWARF.
TagCondition Tag = 0x3F
TagSharedType Tag = 0x40
// The following are new in DWARF 4.
TagTypeUnit Tag = 0x41
TagRvalueReferenceType Tag = 0x42
TagTemplateAlias Tag = 0x43
)
var tagNames = [...]string{
TagArrayType: "ArrayType",
TagClassType: "ClassType",
TagEntryPoint: "EntryPoint",
TagEnumerationType: "EnumerationType",
TagFormalParameter: "FormalParameter",
TagImportedDeclaration: "ImportedDeclaration",
TagLabel: "Label",
TagLexDwarfBlock: "LexDwarfBlock",
TagMember: "Member",
TagPointerType: "PointerType",
TagReferenceType: "ReferenceType",
TagCompileUnit: "CompileUnit",
TagStringType: "StringType",
TagStructType: "StructType",
TagSubroutineType: "SubroutineType",
TagTypedef: "Typedef",
TagUnionType: "UnionType",
TagUnspecifiedParameters: "UnspecifiedParameters",
TagVariant: "Variant",
TagCommonDwarfBlock: "CommonDwarfBlock",
TagCommonInclusion: "CommonInclusion",
TagInheritance: "Inheritance",
TagInlinedSubroutine: "InlinedSubroutine",
TagModule: "Module",
TagPtrToMemberType: "PtrToMemberType",
TagSetType: "SetType",
TagSubrangeType: "SubrangeType",
TagWithStmt: "WithStmt",
TagAccessDeclaration: "AccessDeclaration",
TagBaseType: "BaseType",
TagCatchDwarfBlock: "CatchDwarfBlock",
TagConstType: "ConstType",
TagConstant: "Constant",
TagEnumerator: "Enumerator",
TagFileType: "FileType",
TagFriend: "Friend",
TagNamelist: "Namelist",
TagNamelistItem: "NamelistItem",
TagPackedType: "PackedType",
TagSubprogram: "Subprogram",
TagTemplateTypeParameter: "TemplateTypeParameter",
TagTemplateValueParameter: "TemplateValueParameter",
TagThrownType: "ThrownType",
TagTryDwarfBlock: "TryDwarfBlock",
TagVariantPart: "VariantPart",
TagVariable: "Variable",
TagVolatileType: "VolatileType",
TagDwarfProcedure: "DwarfProcedure",
TagRestrictType: "RestrictType",
TagInterfaceType: "InterfaceType",
TagNamespace: "Namespace",
TagImportedModule: "ImportedModule",
TagUnspecifiedType: "UnspecifiedType",
TagPartialUnit: "PartialUnit",
TagImportedUnit: "ImportedUnit",
TagMutableType: "MutableType",
TagCondition: "Condition",
TagSharedType: "SharedType",
TagTypeUnit: "TypeUnit",
TagRvalueReferenceType: "RvalueReferenceType",
TagTemplateAlias: "TemplateAlias",
}
func (t Tag) String() string {
if int(t) < len(tagNames) {
s := tagNames[t]
if s != "" {
return s
}
}
return strconv.Itoa(int(t))
}
func (t Tag) GoString() string {
if int(t) < len(tagNames) {
s := tagNames[t]
if s != "" {
return "dwarf.Tag" + s
}
}
return "dwarf.Tag(" + strconv.FormatInt(int64(t), 10) + ")"
}
// Location expression operators.
// The debug info encodes value locations like 8(R3)
// as a sequence of these op codes.
// This package does not implement full expressions;
// the opPlusUconst operator is expected by the type parser.
const (
opAddr = 0x03 /* 1 op, const addr */
opDeref = 0x06
opConst1u = 0x08 /* 1 op, 1 byte const */
opConst1s = 0x09 /* " signed */
opConst2u = 0x0A /* 1 op, 2 byte const */
opConst2s = 0x0B /* " signed */
opConst4u = 0x0C /* 1 op, 4 byte const */
opConst4s = 0x0D /* " signed */
opConst8u = 0x0E /* 1 op, 8 byte const */
opConst8s = 0x0F /* " signed */
opConstu = 0x10 /* 1 op, LEB128 const */
opConsts = 0x11 /* " signed */
opDup = 0x12
opDrop = 0x13
opOver = 0x14
opPick = 0x15 /* 1 op, 1 byte stack index */
opSwap = 0x16
opRot = 0x17
opXderef = 0x18
opAbs = 0x19
opAnd = 0x1A
opDiv = 0x1B
opMinus = 0x1C
opMod = 0x1D
opMul = 0x1E
opNeg = 0x1F
opNot = 0x20
opOr = 0x21
opPlus = 0x22
opPlusUconst = 0x23 /* 1 op, ULEB128 addend */
opShl = 0x24
opShr = 0x25
opShra = 0x26
opXor = 0x27
opSkip = 0x2F /* 1 op, signed 2-byte constant */
opBra = 0x28 /* 1 op, signed 2-byte constant */
opEq = 0x29
opGe = 0x2A
opGt = 0x2B
opLe = 0x2C
opLt = 0x2D
opNe = 0x2E
opLit0 = 0x30
/* OpLitN = OpLit0 + N for N = 0..31 */
opReg0 = 0x50
/* OpRegN = OpReg0 + N for N = 0..31 */
opBreg0 = 0x70 /* 1 op, signed LEB128 constant */
/* OpBregN = OpBreg0 + N for N = 0..31 */
opRegx = 0x90 /* 1 op, ULEB128 register */
opFbreg = 0x91 /* 1 op, SLEB128 offset */
opBregx = 0x92 /* 2 op, ULEB128 reg; SLEB128 off */
opPiece = 0x93 /* 1 op, ULEB128 size of piece */
opDerefSize = 0x94 /* 1-byte size of data retrieved */
opXderefSize = 0x95 /* 1-byte size of data retrieved */
opNop = 0x96
/* next four new in Dwarf v3 */
opPushObjAddr = 0x97
opCall2 = 0x98 /* 2-byte offset of DIE */
opCall4 = 0x99 /* 4-byte offset of DIE */
opCallRef = 0x9A /* 4- or 8- byte offset of DIE */
/* 0xE0-0xFF reserved for user-specific */
)
// Basic type encodings -- the value for AttrEncoding in a TagBaseType Entry.
const (
encAddress = 0x01
encBoolean = 0x02
encComplexFloat = 0x03
encFloat = 0x04
encSigned = 0x05
encSignedChar = 0x06
encUnsigned = 0x07
encUnsignedChar = 0x08
encImaginaryFloat = 0x09
)
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// DWARF debug information entry parser.
// An entry is a sequence of data items of a given format.
// The first word in the entry is an index into what DWARF
// calls the ``abbreviation table.'' An abbreviation is really
// just a type descriptor: it's an array of attribute tag/value format pairs.
package dwarf
import (
"errors"
"strconv"
)
// a single entry's description: a sequence of attributes
type abbrev struct {
tag Tag
children bool
field []afield
}
type afield struct {
attr Attr
fmt format
}
// a map from entry format ids to their descriptions
type abbrevTable map[uint32]abbrev
// ParseAbbrev returns the abbreviation table that starts at byte off
// in the .debug_abbrev section.
func (d *Data) parseAbbrev(off uint32) (abbrevTable, error) {
if m, ok := d.abbrevCache[off]; ok {
return m, nil
}
data := d.abbrev
if off > uint32(len(data)) {
data = nil
} else {
data = data[off:]
}
b := makeBuf(d, unknownFormat{}, "abbrev", 0, data)
// Error handling is simplified by the buf getters
// returning an endless stream of 0s after an error.
m := make(abbrevTable)
for {
// Table ends with id == 0.
id := uint32(b.uint())
if id == 0 {
break
}
// Walk over attributes, counting.
n := 0
b1 := b // Read from copy of b.
b1.uint()
b1.uint8()
for {
tag := b1.uint()
fmt := b1.uint()
if tag == 0 && fmt == 0 {
break
}
n++
}
if b1.err != nil {
return nil, b1.err
}
// Walk over attributes again, this time writing them down.
var a abbrev
a.tag = Tag(b.uint())
a.children = b.uint8() != 0
a.field = make([]afield, n)
for i := range a.field {
a.field[i].attr = Attr(b.uint())
a.field[i].fmt = format(b.uint())
}
b.uint()
b.uint()
m[id] = a
}
if b.err != nil {
return nil, b.err
}
d.abbrevCache[off] = m
return m, nil
}
// An entry is a sequence of attribute/value pairs.
type Entry struct {
Offset Offset // offset of Entry in DWARF info
Tag Tag // tag (kind of Entry)
Children bool // whether Entry is followed by children
Field []Field
}
// A Field is a single attribute/value pair in an Entry.
type Field struct {
Attr Attr
Val interface{}
}
// Val returns the value associated with attribute Attr in Entry,
// or nil if there is no such attribute.
//
// A common idiom is to merge the check for nil return with
// the check that the value has the expected dynamic type, as in:
// v, ok := e.Val(AttrSibling).(int64);
//
func (e *Entry) Val(a Attr) interface{} {
for _, f := range e.Field {
if f.Attr == a {
return f.Val
}
}
return nil
}
// An Offset represents the location of an Entry within the DWARF info.
// (See Reader.Seek.)
type Offset uint32
// Entry reads a single entry from buf, decoding
// according to the given abbreviation table.
func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry {
off := b.off
id := uint32(b.uint())
if id == 0 {
return &Entry{}
}
a, ok := atab[id]
if !ok {
b.error("unknown abbreviation table index")
return nil
}
e := &Entry{
Offset: off,
Tag: a.tag,
Children: a.children,
Field: make([]Field, len(a.field)),
}
for i := range e.Field {
e.Field[i].Attr = a.field[i].attr
fmt := a.field[i].fmt
if fmt == formIndirect {
fmt = format(b.uint())
}
var val interface{}
switch fmt {
default:
b.error("unknown entry attr format 0x" + strconv.FormatInt(int64(fmt), 16))
// address
case formAddr:
val = b.addr()
// block
case formDwarfBlock1:
val = b.bytes(int(b.uint8()))
case formDwarfBlock2:
val = b.bytes(int(b.uint16()))
case formDwarfBlock4:
val = b.bytes(int(b.uint32()))
case formDwarfBlock:
val = b.bytes(int(b.uint()))
// constant
case formData1:
val = int64(b.uint8())
case formData2:
val = int64(b.uint16())
case formData4:
val = int64(b.uint32())
case formData8:
val = int64(b.uint64())
case formSdata:
val = int64(b.int())
case formUdata:
val = int64(b.uint())
// flag
case formFlag:
val = b.uint8() == 1
// New in DWARF 4.
case formFlagPresent:
// The attribute is implicitly indicated as present, and no value is
// encoded in the debugging information entry itself.
val = true
// reference to other entry
case formRefAddr:
vers := b.format.version()
if vers == 0 {
b.error("unknown version for DW_FORM_ref_addr")
} else if vers == 2 {
val = Offset(b.addr())
} else {
is64, known := b.format.dwarf64()
if !known {
b.error("unknown size for DW_FORM_ref_addr")
} else if is64 {
val = Offset(b.uint64())
} else {
val = Offset(b.uint32())
}
}
case formRef1:
val = Offset(b.uint8()) + ubase
case formRef2:
val = Offset(b.uint16()) + ubase
case formRef4:
val = Offset(b.uint32()) + ubase
case formRef8:
val = Offset(b.uint64()) + ubase
case formRefUdata:
val = Offset(b.uint()) + ubase
// string
case formString:
val = b.string()
case formStrp:
off := b.uint32() // offset into .debug_str
if b.err != nil {
return nil
}
b1 := makeBuf(b.dwarf, unknownFormat{}, "str", 0, b.dwarf.str)
b1.skip(int(off))
val = b1.string()
if b1.err != nil {
b.err = b1.err
return nil
}
// lineptr, loclistptr, macptr, rangelistptr
// New in DWARF 4, but clang can generate them with -gdwarf-2.
// Section reference, replacing use of formData4 and formData8.
case formSecOffset, formGnuRefAlt, formGnuStrpAlt:
is64, known := b.format.dwarf64()
if !known {
b.error("unknown size for form 0x" + strconv.FormatInt(int64(fmt), 16))
} else if is64 {
val = int64(b.uint64())
} else {
val = int64(b.uint32())
}
// exprloc
// New in DWARF 4.
case formExprloc:
val = b.bytes(int(b.uint()))
// reference
// New in DWARF 4.
case formRefSig8:
// 64-bit type signature.
val = b.uint64()
}
e.Field[i].Val = val
}
if b.err != nil {
return nil
}
return e
}
// A Reader allows reading Entry structures from a DWARF ``info'' section.
// The Entry structures are arranged in a tree. The Reader's Next function
// return successive entries from a pre-order traversal of the tree.
// If an entry has children, its Children field will be true, and the children
// follow, terminated by an Entry with Tag 0.
type Reader struct {
b buf
d *Data
err error
unit int
lastChildren bool // .Children of last entry returned by Next
lastSibling Offset // .Val(AttrSibling) of last entry returned by Next
}
// Reader returns a new Reader for Data.
// The reader is positioned at byte offset 0 in the DWARF ``info'' section.
func (d *Data) Reader() *Reader {
r := &Reader{d: d}
r.Seek(0)
return r
}
// AddressSize returns the size in bytes of addresses in the current compilation
// unit.
func (r *Reader) AddressSize() int {
return r.d.unit[r.unit].asize
}
// Seek positions the Reader at offset off in the encoded entry stream.
// Offset 0 can be used to denote the first entry.
func (r *Reader) Seek(off Offset) {
d := r.d
r.err = nil
r.lastChildren = false
if off == 0 {
if len(d.unit) == 0 {
return
}
u := &d.unit[0]
r.unit = 0
r.b = makeBuf(r.d, u, "info", u.off, u.data)
return
}
// TODO(rsc): binary search (maybe a new package)
var i int
var u *unit
for i = range d.unit {
u = &d.unit[i]
if u.off <= off && off < u.off+Offset(len(u.data)) {
r.unit = i
r.b = makeBuf(r.d, u, "info", off, u.data[off-u.off:])
return
}
}
r.err = errors.New("offset out of range")
}
// maybeNextUnit advances to the next unit if this one is finished.
func (r *Reader) maybeNextUnit() {
for len(r.b.data) == 0 && r.unit+1 < len(r.d.unit) {
r.unit++
u := &r.d.unit[r.unit]
r.b = makeBuf(r.d, u, "info", u.off, u.data)
}
}
// Next reads the next entry from the encoded entry stream.
// It returns nil, nil when it reaches the end of the section.
// It returns an error if the current offset is invalid or the data at the
// offset cannot be decoded as a valid Entry.
func (r *Reader) Next() (*Entry, error) {
if r.err != nil {
return nil, r.err
}
r.maybeNextUnit()
if len(r.b.data) == 0 {
return nil, nil
}
u := &r.d.unit[r.unit]
e := r.b.entry(u.atable, u.base)
if r.b.err != nil {
r.err = r.b.err
return nil, r.err
}
if e != nil {
r.lastChildren = e.Children
if r.lastChildren {
r.lastSibling, _ = e.Val(AttrSibling).(Offset)
}
} else {
r.lastChildren = false
}
return e, nil
}
// SkipChildren skips over the child entries associated with
// the last Entry returned by Next. If that Entry did not have
// children or Next has not been called, SkipChildren is a no-op.
func (r *Reader) SkipChildren() {
if r.err != nil || !r.lastChildren {
return
}
// If the last entry had a sibling attribute,
// that attribute gives the offset of the next
// sibling, so we can avoid decoding the
// child subtrees.
if r.lastSibling >= r.b.off {
r.Seek(r.lastSibling)
return
}
for {
e, err := r.Next()
if err != nil || e == nil || e.Tag == 0 {
break
}
if e.Children {
r.SkipChildren()
}
}
}
// clone returns a copy of the reader. This is used by the typeReader
// interface.
func (r *Reader) clone() typeReader {
return r.d.Reader()
}
// offset returns the current buffer offset. This is used by the
// typeReader interface.
func (r *Reader) offset() Offset {
return r.b.off
}
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Mapping from PC to SP offset (called CFA - Canonical Frame Address - in DWARF).
// This value is the offset from the stack pointer to the virtual frame pointer
// (address of zeroth argument) at each PC value in the program.
package dwarf
import "fmt"
// http://www.dwarfstd.org/doc/DWARF4.pdf Section 6.4 page 126
// We implement only the CFA column of the table, not the location
// information about other registers. In other words, we implement
// only what we need to understand Go programs compiled by gc.
// PCToSPOffset returns the offset, at the specified PC, to add to the
// SP to reach the virtual frame pointer, which corresponds to the
// address of the zeroth argument of the function, the word on the
// stack immediately above the return PC.
func (d *Data) PCToSPOffset(pc uint64) (offset int64, err error) {
if len(d.frame) == 0 {
return 0, fmt.Errorf("PCToSPOffset: no frame table")
}
var m frameMachine
// Assume the first info unit is the same as us. Extremely likely. TODO?
if len(d.unit) == 0 {
return 0, fmt.Errorf("PCToSPOffset: no info section")
}
buf := makeBuf(d, &d.unit[0], "frame", 0, d.frame)
for len(buf.data) > 0 {
offset, err := m.evalCompilationUnit(&buf, pc)
if err != nil {
return 0, err
}
return offset, nil
}
return 0, fmt.Errorf("PCToSPOffset: no frame defined for PC %#x", pc)
}
// Call Frame instructions. Figure 40, page 181.
// Structure is high two bits plus low 6 bits specified by + in comment.
// Some take one or two operands.
const (
frameNop = 0<<6 + 0x00
frameAdvanceLoc = 1<<6 + 0x00 // + delta
frameOffset = 2<<6 + 0x00 // + register op: ULEB128 offset
frameRestore = 3<<6 + 0x00 // + register
frameSetLoc = 0<<6 + 0x01 // op: address
frameAdvanceLoc1 = 0<<6 + 0x02 // op: 1-byte delta
frameAdvanceLoc2 = 0<<6 + 0x03 // op: 2-byte delta
frameAdvanceLoc4 = 0<<6 + 0x04 // op: 4-byte delta
frameOffsetExtended = 0<<6 + 0x05 // ops: ULEB128 register ULEB128 offset
frameRestoreExtended = 0<<6 + 0x06 // op: ULEB128 register
frameUndefined = 0<<6 + 0x07 // op: ULEB128 register
frameSameValue = 0<<6 + 0x08 // op: ULEB128 register
frameRegister = 0<<6 + 0x09 // op: ULEB128 register ULEB128 register
frameRememberState = 0<<6 + 0x0a
frameRestoreState = 0<<6 + 0x0b
frameDefCFA = 0<<6 + 0x0c // op: ULEB128 register ULEB128 offset
frameDefCFARegister = 0<<6 + 0x0d // op: ULEB128 register
frameDefCFAOffset = 0<<6 + 0x0e // op: ULEB128 offset
frameDefCFAExpression = 0<<6 + 0x0f // op: BLOCK
frameExpression = 0<<6 + 0x10 // op: ULEB128 register BLOCK
frameOffsetExtendedSf = 0<<6 + 0x11 // op: ULEB128 register SLEB128 offset
frameDefCFASf = 0<<6 + 0x12 // op: ULEB128 register SLEB128 offset
frameDefCFAOffsetSf = 0<<6 + 0x13 // op: SLEB128 offset
frameValOffset = 0<<6 + 0x14 // op: ULEB128 ULEB128
frameValOffsetSf = 0<<6 + 0x15 // op: ULEB128 SLEB128
frameValExpression = 0<<6 + 0x16 // op: ULEB128 BLOCK
frameLoUser = 0<<6 + 0x1c
frameHiUser = 0<<6 + 0x3f
)
// frameMachine represents the PC/SP engine.
// Section 6.4, page 129.
type frameMachine struct {
// Initial values from CIE.
version uint8 // Version number, "independent of DWARF version"
augmentation string // Augmentation; treated as unexpected for now. TODO.
addressSize uint8 // In DWARF v4 and above. Size of a target address.
segmentSize uint8 // In DWARF v4 and above. Size of a segment selector.
codeAlignmentFactor uint64 // Unit of code size in advance instructions.
dataAlignmentFactor int64 // Unit of data size in certain offset instructions.
returnAddressRegister int // Pseudo-register (actually data column) representing return address.
returnRegisterOffset int64 // Offset to saved PC from CFA in bytes.
// CFA definition.
cfaRegister int // Which register represents the SP.
cfaOffset int64 // CFA offset value.
// Running machine.
location uint64
}
// evalCompilationUnit scans the frame data for one compilation unit to retrieve
// the offset information for the specified pc.
func (m *frameMachine) evalCompilationUnit(b *buf, pc uint64) (int64, error) {
err := m.parseCIE(b)
if err != nil {
return 0, err
}
for {
offset, found, err := m.scanFDE(b, pc)
if err != nil {
return 0, err
}
if found {
return offset, nil
}
}
}
// parseCIE assumes the incoming buffer starts with a CIE block and parses it
// to initialize a frameMachine.
func (m *frameMachine) parseCIE(allBuf *buf) error {
length := int(allBuf.uint32())
if len(allBuf.data) < length {
return fmt.Errorf("CIE parse error: too short")
}
// Create buffer for just this section.
b := allBuf.slice(length)
cie := b.uint32()
if cie != 0xFFFFFFFF {
return fmt.Errorf("CIE parse error: not CIE: %x", cie)
}
m.version = b.uint8()
if m.version != 3 && m.version != 4 {
return fmt.Errorf("CIE parse error: unsupported version %d", m.version)
}
m.augmentation = b.string()
if len(m.augmentation) > 0 {
return fmt.Errorf("CIE: can't handled augmentation string %q", m.augmentation)
}
if m.version >= 4 {
m.addressSize = b.uint8()
m.segmentSize = b.uint8()
} else {
// Unused. Gc generates version 3, so these values will not be
// set, but they are also not used so it's OK.
}
m.codeAlignmentFactor = b.uint()
m.dataAlignmentFactor = b.int()
m.returnAddressRegister = int(b.uint())
// Initial instructions. At least for Go, establishes SP register number
// and initial value of CFA offset at start of function.
_, err := m.run(&b, ^uint64(0))
if err != nil {
return err
}
// There's padding, but we can ignore it.
return nil
}
// scanFDE assumes the incoming buffer starts with a FDE block and parses it
// to run a frameMachine and, if the PC is represented in its range, return
// the CFA offset for that PC. The boolean returned reports whether the
// PC is in range for this FDE.
func (m *frameMachine) scanFDE(allBuf *buf, pc uint64) (int64, bool, error) {
length := int(allBuf.uint32())
if len(allBuf.data) < length {
return 0, false, fmt.Errorf("FDE parse error: too short")
}
if length <= 0 {
if length == 0 {
// EOF.
return 0, false, fmt.Errorf("PC %#x not found in PC/SP table", pc)
}
return 0, false, fmt.Errorf("bad FDE length %d", length)
}
// Create buffer for just this section.
b := allBuf.slice(length)
cieOffset := b.uint32() // TODO assumes 32 bits.
// Expect 0: first CIE in this segment. TODO.
if cieOffset != 0 {
return 0, false, fmt.Errorf("FDE parse error: bad CIE offset: %.2x", cieOffset)
}
// Initial location.
m.location = b.addr()
addressRange := b.addr()
// If the PC is not in this function, there's no point in executing the instructions.
if pc < m.location || m.location+addressRange <= pc {
return 0, false, nil
}
// The PC appears in this FDE. Scan to find the location.
offset, err := m.run(&b, pc)
if err != nil {
return 0, false, err
}
// There's padding, but we can ignore it.
return offset, true, nil
}
// run executes the instructions in the buffer, which has been sliced to contain
// only the data for this block. When we run out of data, we return.
// Since we are only called when we know the PC is in this block, reaching
// EOF is not an error, it just means the final CFA definition matches the
// tail of the block that holds the PC.
// The return value is the CFA at the end of the block or the PC, whichever
// comes first.
func (m *frameMachine) run(b *buf, pc uint64) (int64, error) {
// We run the machine at location == PC because if the PC is at the first
// instruction of a block, the definition of its offset arrives as an
// offset-defining operand after the PC is set to that location.
for m.location <= pc && len(b.data) > 0 {
op := b.uint8()
// Ops with embedded operands
switch op & 0xC0 {
case frameAdvanceLoc: // (6.4.2.1)
// delta in low bits
m.location += uint64(op & 0x3F)
continue
case frameOffset: // (6.4.2.3)
// Register in low bits; ULEB128 offset.
// For Go binaries we only see this in the CIE for the return address register.
if int(op&0x3F) != m.returnAddressRegister {
return 0, fmt.Errorf("invalid frameOffset register R%d should be R%d", op&0x3f, m.returnAddressRegister)
}
m.returnRegisterOffset = int64(b.uint()) * m.dataAlignmentFactor
continue
case frameRestore: // (6.4.2.3)
// register in low bits
return 0, fmt.Errorf("unimplemented frameRestore(R%d)\n", op&0x3F)
}
// The remaining ops do not have embedded operands.
switch op {
// Row creation instructions (6.4.2.1)
case frameNop:
case frameSetLoc: // op: address
return 0, fmt.Errorf("unimplemented setloc") // what size is operand?
case frameAdvanceLoc1: // op: 1-byte delta
m.location += uint64(b.uint8())
case frameAdvanceLoc2: // op: 2-byte delta
m.location += uint64(b.uint16())
case frameAdvanceLoc4: // op: 4-byte delta
m.location += uint64(b.uint32())
// CFA definition instructions (6.4.2.2)
case frameDefCFA: // op: ULEB128 register ULEB128 offset
m.cfaRegister = int(b.int())
m.cfaOffset = int64(b.uint())
case frameDefCFASf: // op: ULEB128 register SLEB128 offset
return 0, fmt.Errorf("unimplemented frameDefCFASf")
case frameDefCFARegister: // op: ULEB128 register
return 0, fmt.Errorf("unimplemented frameDefCFARegister")
case frameDefCFAOffset: // op: ULEB128 offset
return 0, fmt.Errorf("unimplemented frameDefCFAOffset")
case frameDefCFAOffsetSf: // op: SLEB128 offset
offset := b.int()
m.cfaOffset = offset * m.dataAlignmentFactor
// TODO: Verify we are using a factored offset.
case frameDefCFAExpression: // op: BLOCK
return 0, fmt.Errorf("unimplemented frameDefCFAExpression")
// Register Rule instructions (6.4.2.3)
case frameOffsetExtended: // ops: ULEB128 register ULEB128 offset
// The same as frameOffset, but with the register specified in an operand.
reg := b.uint()
// For Go binaries we only see this in the CIE for the return address register.
if reg != uint64(m.returnAddressRegister) {
return 0, fmt.Errorf("invalid frameOffsetExtended: register R%d should be R%d", reg, m.returnAddressRegister)
}
m.returnRegisterOffset = int64(b.uint()) * m.dataAlignmentFactor
case frameRestoreExtended: // op: ULEB128 register
return 0, fmt.Errorf("unimplemented frameRestoreExtended")
case frameUndefined: // op: ULEB128 register; unimplemented
return 0, fmt.Errorf("unimplemented frameUndefined")
case frameSameValue: // op: ULEB128 register
return 0, fmt.Errorf("unimplemented frameSameValue")
case frameRegister: // op: ULEB128 register ULEB128 register
return 0, fmt.Errorf("unimplemented frameRegister")
case frameRememberState:
return 0, fmt.Errorf("unimplemented frameRememberState")
case frameRestoreState:
return 0, fmt.Errorf("unimplemented frameRestoreState")
case frameExpression: // op: ULEB128 register BLOCK
return 0, fmt.Errorf("unimplemented frameExpression")
case frameOffsetExtendedSf: // op: ULEB128 register SLEB128 offset
return 0, fmt.Errorf("unimplemented frameOffsetExtended_sf")
case frameValOffset: // op: ULEB128 ULEB128
return 0, fmt.Errorf("unimplemented frameValOffset")
case frameValOffsetSf: // op: ULEB128 SLEB128
return 0, fmt.Errorf("unimplemented frameValOffsetSf")
case frameValExpression: // op: ULEB128 BLOCK
return 0, fmt.Errorf("unimplemented frameValExpression")
default:
if frameLoUser <= op && op <= frameHiUser {
return 0, fmt.Errorf("unknown user-defined frame op %#x", op)
}
return 0, fmt.Errorf("unknown frame op %#x", op)
}
}
return m.cfaOffset, nil
}
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dwarf_test
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
"cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf"
"cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf"
)
var (
pcspTempDir string
pcsptestBinary string
)
func doPCToSPTest(self bool) bool {
// For now, only works on amd64 platforms.
if runtime.GOARCH != "amd64" {
return false
}
// Self test reads test binary; only works on Linux or Mac.
if self {
if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
return false
}
}
// Command below expects "sh", so Unix.
if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
return false
}
if pcsptestBinary != "" {
return true
}
var err error
pcspTempDir, err = ioutil.TempDir("", "pcsptest")
if err != nil {
panic(err)
}
if strings.Contains(pcspTempDir, " ") {
panic("unexpected space in tempdir")
}
// This command builds pcsptest from testdata/pcsptest.go.
pcsptestBinary = filepath.Join(pcspTempDir, "pcsptest")
command := fmt.Sprintf("go tool compile -o %s.6 testdata/pcsptest.go && go tool link -H %s -o %s %s.6",
pcsptestBinary, runtime.GOOS, pcsptestBinary, pcsptestBinary)
cmd := exec.Command("sh", "-c", command)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
panic(err)
}
return true
}
func endPCToSPTest() {
if pcspTempDir != "" {
os.RemoveAll(pcspTempDir)
pcspTempDir = ""
pcsptestBinary = ""
}
}
func TestPCToSPOffset(t *testing.T) {
t.Skip("gets a stack layout it doesn't expect")
if !doPCToSPTest(false) {
return
}
defer endPCToSPTest()
data, err := getData(pcsptestBinary)
if err != nil {
t.Fatal(err)
}
entry, err := data.LookupFunction("main.test")
if err != nil {
t.Fatal("lookup startPC:", err)
}
startPC, ok := entry.Val(dwarf.AttrLowpc).(uint64)
if !ok {
t.Fatal(`DWARF data for function "main.test" has no low PC`)
}
endPC, ok := entry.Val(dwarf.AttrHighpc).(uint64)
if !ok {
t.Fatal(`DWARF data for function "main.test" has no high PC`)
}
const addrSize = 8 // TODO: Assumes amd64.
const argSize = 8 // Defined by int64 arguments in test binary.
// On 64-bit machines, the first offset must be one address size,
// for the return PC.
offset, err := data.PCToSPOffset(startPC)
if err != nil {
t.Fatal("startPC:", err)
}
if offset != addrSize {
t.Fatalf("expected %d at start of function; got %d", addrSize, offset)
}
// On 64-bit machines, expect some 8s and some 32s. (See the
// comments in testdata/pcsptest.go.
// TODO: The test could be stronger, but not much unless we
// disassemble the binary.
count := make(map[int64]int)
for pc := startPC; pc < endPC; pc++ {
offset, err := data.PCToSPOffset(pc)
if err != nil {
t.Fatal("scanning function:", err)
}
count[offset]++
}
if len(count) != 2 {
t.Errorf("expected 2 offset values, got %d; counts are: %v", len(count), count)
}
if count[addrSize] == 0 {
t.Errorf("expected some values at offset %d; got %v", addrSize, count)
}
if count[addrSize+3*argSize] == 0 {
t.Errorf("expected some values at offset %d; got %v", addrSize+3*argSize, count)
}
}
func getData(file string) (*dwarf.Data, error) {
switch runtime.GOOS {
case "linux":
f, err := elf.Open(file)
if err != nil {
return nil, err
}
dwarf, err := f.DWARF()
if err != nil {
return nil, err
}
f.Close()
return dwarf, nil
}
panic("unimplemented DWARF for GOOS=" + runtime.GOOS)
}
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dwarf
// This file implements the mapping from PC to lines.
// TODO: Find a way to test this properly.
// http://www.dwarfstd.org/doc/DWARF4.pdf Section 6.2 page 108
import (
"fmt"
"sort"
"strings"
)
// PCToLine returns the file and line number corresponding to the PC value.
// It returns an error if a correspondence cannot be found.
func (d *Data) PCToLine(pc uint64) (file string, line uint64, err error) {
c := d.pcToLineEntries
if len(c) == 0 {
return "", 0, fmt.Errorf("PCToLine: no line table")
}
i := sort.Search(len(c), func(i int) bool { return c[i].pc > pc }) - 1
// c[i] is now the entry in pcToLineEntries with the largest pc that is not
// larger than the query pc.
// The search has failed if:
// - All pcs in c were larger than the query pc (i == -1).
// - c[i] marked the end of a sequence of instructions (c[i].file == 0).
// - c[i] is the last element of c, and isn't the end of a sequence of
// instructions, and the search pc is much larger than c[i].pc. In this
// case, we don't know the range of the last instruction, but the search
// pc is probably past it.
if i == -1 || c[i].file == 0 || (i+1 == len(c) && pc-c[i].pc > 1024) {
return "", 0, fmt.Errorf("no source line defined for PC %#x", pc)
}
if c[i].file >= uint64(len(d.sourceFiles)) {
return "", 0, fmt.Errorf("invalid file number in DWARF data")
}
return d.sourceFiles[c[i].file], c[i].line, nil
}
// LineToBreakpointPCs returns the PCs that should be used as breakpoints
// corresponding to the given file and line number.
// It returns an empty slice if no PCs were found.
func (d *Data) LineToBreakpointPCs(file string, line uint64) ([]uint64, error) {
compDir := d.compilationDirectory()
// Find the closest match in the executable for the specified file.
// We choose the file with the largest number of path components matching
// at the end of the name. If there is a tie, we prefer files that are
// under the compilation directory. If there is still a tie, we choose
// the file with the shortest name.
// TODO: handle duplicate file names in the DWARF?
var bestFile struct {
fileNum uint64 // Index of the file in the DWARF data.
components int // Number of matching path components.
length int // Length of the filename.
underComp bool // File is under the compilation directory.
}
for filenum, filename := range d.sourceFiles {
c := matchingPathComponentSuffixSize(filename, file)
underComp := strings.HasPrefix(filename, compDir)
better := false
if c != bestFile.components {
better = c > bestFile.components
} else if underComp != bestFile.underComp {
better = underComp
} else {
better = len(filename) < bestFile.length
}
if better {
bestFile.fileNum = uint64(filenum)
bestFile.components = c
bestFile.length = len(filename)
bestFile.underComp = underComp
}
}
if bestFile.components == 0 {
return nil, fmt.Errorf("couldn't find file %q", file)
}
c := d.lineToPCEntries[bestFile.fileNum]
// c contains all (pc, line) pairs for the appropriate file.
start := sort.Search(len(c), func(i int) bool { return c[i].line >= line })
end := sort.Search(len(c), func(i int) bool { return c[i].line > line })
// c[i].line == line for all i in the range [start, end).
pcs := make([]uint64, 0, end-start)
for i := start; i < end; i++ {
pcs = append(pcs, c[i].pc)
}
return pcs, nil
}
// compilationDirectory finds the first compilation unit entry in d and returns
// the compilation directory contained in it.
// If it fails, it returns the empty string.
func (d *Data) compilationDirectory() string {
r := d.Reader()
for {
entry, err := r.Next()
if entry == nil || err != nil {
return ""
}
if entry.Tag == TagCompileUnit {
name, _ := entry.Val(AttrCompDir).(string)
return name
}
}
}
// matchingPathComponentSuffixSize returns the largest n such that the last n
// components of the paths p1 and p2 are equal.
// e.g. matchingPathComponentSuffixSize("a/b/x/y.go", "b/a/x/y.go") returns 2.
func matchingPathComponentSuffixSize(p1, p2 string) int {
// TODO: deal with other path separators.
c1 := strings.Split(p1, "/")
c2 := strings.Split(p2, "/")
min := len(c1)
if len(c2) < min {
min = len(c2)
}
var n int
for n = 0; n < min; n++ {
if c1[len(c1)-1-n] != c2[len(c2)-1-n] {
break
}
}
return n
}
// Standard opcodes. Figure 37, page 178.
// If an opcode >= lineMachine.prologue.opcodeBase, it is a special
// opcode rather than the opcode defined in this table.
const (
lineStdCopy = 0x01
lineStdAdvancePC = 0x02
lineStdAdvanceLine = 0x03
lineStdSetFile = 0x04
lineStdSetColumn = 0x05
lineStdNegateStmt = 0x06
lineStdSetBasicBlock = 0x07
lineStdConstAddPC = 0x08
lineStdFixedAdvancePC = 0x09
lineStdSetPrologueEnd = 0x0a
lineStdSetEpilogueBegin = 0x0b
lineStdSetISA = 0x0c
)
// Extended opcodes. Figure 38, page 179.
const (
lineStartExtendedOpcode = 0x00 // Not defined as a named constant in the spec.
lineExtEndSequence = 0x01
lineExtSetAddress = 0x02
lineExtDefineFile = 0x03
lineExtSetDiscriminator = 0x04 // New in version 4.
lineExtLoUser = 0x80
lineExtHiUser = 0xff
)
// lineHeader holds the information stored in the header of the line table for a
// single compilation unit.
// Section 6.2.4, page 112.
type lineHeader struct {
unitLength int
version int
headerLength int
minInstructionLength int
maxOpsPerInstruction int
defaultIsStmt bool
lineBase int
lineRange int
opcodeBase byte
stdOpcodeLengths []byte
include []string // entry 0 is empty; means current directory
file []lineFile // entry 0 is empty.
}
// lineFile represents a file name stored in the PC/line table, usually in the header.
type lineFile struct {
name string
index int // index into include directories
time int // implementation-defined time of last modification
length int // length in bytes, 0 if not available.
}
// lineMachine holds the registers evaluated during executing of the PC/line mapping engine.
// Section 6.2.2, page 109.
// A .debug_line section consists of multiple line number programs, one for each compilation unit.
type lineMachine struct {
// The program-counter value corresponding to a machine instruction generated by the compiler.
address uint64
// An unsigned integer representing the index of an operation within a VLIW
// instruction. The index of the first operation is 0. For non-VLIW
// architectures, this register will always be 0.
// The address and op_index registers, taken together, form an operation
// pointer that can reference any individual operation with the instruction
// stream.
opIndex uint64
// An unsigned integer indicating the identity of the source file corresponding to a machine instruction.
file uint64
// An unsigned integer indicating a source line number. Lines are numbered
// beginning at 1. The compiler may emit the value 0 in cases where an
// instruction cannot be attributed to any source line.
line uint64
// An unsigned integer indicating a column number within a source line.
// Columns are numbered beginning at 1. The value 0 is reserved to indicate
// that a statement begins at the “left edge” of the line.
column uint64
// A boolean indicating that the current instruction is a recommended
// breakpoint location. A recommended breakpoint location is intended to
// “represent” a line, a statement and/or a semantically distinct subpart of a
// statement.
isStmt bool
// A boolean indicating that the current instruction is the beginning of a basic
// block.
basicBlock bool
// A boolean indicating that the current address is that of the first byte after
// the end of a sequence of target machine instructions. end_sequence
// terminates a sequence of lines; therefore other information in the same
// row is not meaningful.
endSequence bool
// A boolean indicating that the current address is one (of possibly many)
// where execution should be suspended for an entry breakpoint of a
// function.
prologueEnd bool
// A boolean indicating that the current address is one (of possibly many)
// where execution should be suspended for an exit breakpoint of a function.
epilogueBegin bool
// An unsigned integer whose value encodes the applicable instruction set
// architecture for the current instruction.
// The encoding of instruction sets should be shared by all users of a given
// architecture. It is recommended that this encoding be defined by the ABI
// authoring committee for each architecture.
isa uint64
// An unsigned integer identifying the block to which the current instruction
// belongs. Discriminator values are assigned arbitrarily by the DWARF
// producer and serve to distinguish among multiple blocks that may all be
// associated with the same source file, line, and column. Where only one
// block exists for a given source position, the discriminator value should be
// zero.
discriminator uint64
// The header for the current compilation unit.
// Not an actual register, but stored here for cleanliness.
header lineHeader
// Offset in buf of the end of the line number program for the current unit.
unitEndOff Offset
}
// parseHeader parses the header describing the compilation unit in the line
// table starting at the specified offset.
func (m *lineMachine) parseHeader(b *buf) error {
m.header = lineHeader{}
m.header.unitLength = int(b.uint32()) // Note: We are assuming 32-bit DWARF format.
m.unitEndOff = b.off + Offset(m.header.unitLength)
if m.header.unitLength > len(b.data) {
return fmt.Errorf("DWARF: bad PC/line header length")
}
m.header.version = int(b.uint16())
m.header.headerLength = int(b.uint32())
m.header.minInstructionLength = int(b.uint8())
if m.header.version >= 4 {
m.header.maxOpsPerInstruction = int(b.uint8())
} else {
m.header.maxOpsPerInstruction = 1
}
m.header.defaultIsStmt = b.uint8() != 0
m.header.lineBase = int(int8(b.uint8()))
m.header.lineRange = int(b.uint8())
m.header.opcodeBase = b.uint8()
m.header.stdOpcodeLengths = make([]byte, m.header.opcodeBase-1)
copy(m.header.stdOpcodeLengths, b.bytes(int(m.header.opcodeBase-1)))
m.header.include = make([]string, 1) // First entry is empty; file index entries are 1-indexed.
// Includes
for {
name := b.string()
if name == "" {
break
}
m.header.include = append(m.header.include, name)
}
// Files
// Files are 1-indexed in line number program, but we'll deal with that in Data.buildLineCaches.
// Here, just collect the filenames.
for {
name := b.string()
if name == "" {
break
}
index := b.uint()
time := b.uint()
length := b.uint()
f := lineFile{
name: name,
index: int(index),
time: int(time),
length: int(length),
}
m.header.file = append(m.header.file, f)
}
return nil
}
// Special opcodes, page 117.
// There are seven steps to processing special opcodes. We break them up here
// because the caller needs to output a row between steps 2 and 4, and because
// we need to perform just step 2 for the opcode DW_LNS_const_add_pc.
func (m *lineMachine) specialOpcodeStep1(opcode byte) {
adjustedOpcode := int(opcode - m.header.opcodeBase)
lineAdvance := m.header.lineBase + (adjustedOpcode % m.header.lineRange)
m.line += uint64(lineAdvance)
}
func (m *lineMachine) specialOpcodeStep2(opcode byte) {
adjustedOpcode := int(opcode - m.header.opcodeBase)
advance := adjustedOpcode / m.header.lineRange
delta := (int(m.opIndex) + advance) / m.header.maxOpsPerInstruction
m.address += uint64(m.header.minInstructionLength * delta)
m.opIndex = (m.opIndex + uint64(advance)) % uint64(m.header.maxOpsPerInstruction)
}
func (m *lineMachine) specialOpcodeSteps4To7() {
m.basicBlock = false
m.prologueEnd = false
m.epilogueBegin = false
m.discriminator = 0
}
// evalCompilationUnit reads the next compilation unit and calls f at each output row.
// Line machine execution continues while f returns true.
func (m *lineMachine) evalCompilationUnit(b *buf, f func(m *lineMachine) (cont bool)) error {
m.reset()
for b.off < m.unitEndOff {
op := b.uint8()
if op >= m.header.opcodeBase {
m.specialOpcodeStep1(op)
m.specialOpcodeStep2(op)
// Step 3 is to output a row, so we call f here.
if !f(m) {
return nil
}
m.specialOpcodeSteps4To7()
continue
}
switch op {
case lineStartExtendedOpcode:
if len(b.data) == 0 {
return fmt.Errorf("DWARF: short extended opcode (1)")
}
size := b.uint()
if uint64(len(b.data)) < size {
return fmt.Errorf("DWARF: short extended opcode (2)")
}
op = b.uint8()
switch op {
case lineExtEndSequence:
m.endSequence = true
if !f(m) {
return nil
}
if len(b.data) == 0 {
return nil
}
m.reset()
case lineExtSetAddress:
m.address = b.addr()
m.opIndex = 0
case lineExtDefineFile:
return fmt.Errorf("DWARF: unimplemented define_file op")
case lineExtSetDiscriminator:
discriminator := b.uint()
m.discriminator = discriminator
default:
return fmt.Errorf("DWARF: unknown extended opcode %#x", op)
}
case lineStdCopy:
if !f(m) {
return nil
}
m.discriminator = 0
m.basicBlock = false
m.prologueEnd = false
m.epilogueBegin = false
case lineStdAdvancePC:
advance := b.uint()
delta := (int(m.opIndex) + int(advance)) / m.header.maxOpsPerInstruction
m.address += uint64(m.header.minInstructionLength * delta)
m.opIndex = (m.opIndex + uint64(advance)) % uint64(m.header.maxOpsPerInstruction)
m.basicBlock = false
m.prologueEnd = false
m.epilogueBegin = false
m.discriminator = 0
case lineStdAdvanceLine:
advance := b.int()
m.line = uint64(int64(m.line) + advance)
case lineStdSetFile:
index := b.uint()
m.file = index
case lineStdSetColumn:
column := b.uint()
m.column = column
case lineStdNegateStmt:
m.isStmt = !m.isStmt
case lineStdSetBasicBlock:
m.basicBlock = true
case lineStdFixedAdvancePC:
m.address += uint64(b.uint16())
m.opIndex = 0
case lineStdSetPrologueEnd:
m.prologueEnd = true
case lineStdSetEpilogueBegin:
m.epilogueBegin = true
case lineStdSetISA:
m.isa = b.uint()
case lineStdConstAddPC:
// Update the address and op_index registers.
m.specialOpcodeStep2(255)
default:
panic("not reached")
}
}
return nil
}
// reset sets the machine's registers to the initial state. Page 111.
func (m *lineMachine) reset() {
m.address = 0
m.opIndex = 0
m.file = 1
m.line = 1
m.column = 0
m.isStmt = m.header.defaultIsStmt
m.basicBlock = false
m.endSequence = false
m.prologueEnd = false
m.epilogueBegin = false
m.isa = 0
m.discriminator = 0
}
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package dwarf provides access to DWARF debugging information loaded from
// executable files, as defined in the DWARF 2.0 Standard at
// http://dwarfstd.org/doc/dwarf-2.0.0.pdf
package dwarf // import "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf"
import "encoding/binary"
// Data represents the DWARF debugging information
// loaded from an executable file (for example, an ELF or Mach-O executable).
type Data struct {
// raw data
abbrev []byte
aranges []byte
frame []byte
info []byte
line []byte
pubnames []byte
ranges []byte
str []byte
// parsed data
abbrevCache map[uint32]abbrevTable
order binary.ByteOrder
typeCache map[Offset]Type
typeSigs map[uint64]*typeUnit
unit []unit
sourceFiles []string // All the source files listed in .debug_line, from all the compilation units.
nameCache // map from name to top-level entries in .debug_info.
pcToFuncEntries // cache of .debug_info data for function bounds.
pcToLineEntries // cache of .debug_line data, used for efficient PC-to-line mapping.
lineToPCEntries // cache of .debug_line data, used for efficient line-to-[]PC mapping.
}
// New returns a new Data object initialized from the given parameters.
// Rather than calling this function directly, clients should typically use
// the DWARF method of the File type of the appropriate package debug/elf,
// debug/macho, or debug/pe.
//
// The []byte arguments are the data from the corresponding debug section
// in the object file; for example, for an ELF object, abbrev is the contents of
// the ".debug_abbrev" section.
func New(abbrev, aranges, frame, info, line, pubnames, ranges, str []byte) (*Data, error) {
d := &Data{
abbrev: abbrev,
aranges: aranges,
frame: frame,
info: info,
line: line,
pubnames: pubnames,
ranges: ranges,
str: str,
abbrevCache: make(map[uint32]abbrevTable),
typeCache: make(map[Offset]Type),
typeSigs: make(map[uint64]*typeUnit),
}
// Sniff .debug_info to figure out byte order.
// bytes 4:6 are the version, a tiny 16-bit number (1, 2, 3).
if len(d.info) < 6 {
return nil, DecodeError{"info", Offset(len(d.info)), "too short"}
}
x, y := d.info[4], d.info[5]
switch {
case x == 0 && y == 0:
return nil, DecodeError{"info", 4, "unsupported version 0"}
case x == 0:
d.order = binary.BigEndian
case y == 0:
d.order = binary.LittleEndian
default:
return nil, DecodeError{"info", 4, "cannot determine byte order"}
}
u, err := d.parseUnits()
if err != nil {
return nil, err
}
d.unit = u
d.buildInfoCaches()
if err := d.buildLineCaches(); err != nil {
return nil, err
}
return d, nil
}
// AddTypes will add one .debug_types section to the DWARF data. A
// typical object with DWARF version 4 debug info will have multiple
// .debug_types sections. The name is used for error reporting only,
// and serves to distinguish one .debug_types section from another.
func (d *Data) AddTypes(name string, types []byte) error {
return d.parseTypes(name, types)
}
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build go1.10
package dwarf_test
// Stripped-down, simplified version of ../../gosym/pclntab_test.go
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
. "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf"
)
var (
pclineTempDir string
pclinetestBinary string
)
func dotest(self bool) bool {
// For now, only works on amd64 platforms.
if runtime.GOARCH != "amd64" {
return false
}
// Self test reads test binary; only works on Linux or Mac.
if self {
if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
return false
}
}
// Command below expects "sh", so Unix.
if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
return false
}
if pclinetestBinary != "" {
return true
}
var err error
pclineTempDir, err = ioutil.TempDir("", "pclinetest")
if err != nil {
panic(err)
}
if strings.Contains(pclineTempDir, " ") {
panic("unexpected space in tempdir")
}
pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
command := fmt.Sprintf("go tool compile -o %s.6 testdata/pclinetest.go && go tool link -H %s -E main -o %s %s.6",
pclinetestBinary, runtime.GOOS, pclinetestBinary, pclinetestBinary)
cmd := exec.Command("sh", "-c", command)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
panic(err)
}
return true
}
func endtest() {
if pclineTempDir != "" {
os.RemoveAll(pclineTempDir)
pclineTempDir = ""
pclinetestBinary = ""
}
}
func TestPCAndLine(t *testing.T) {
// TODO(jba): go1.9: use subtests
if !dotest(false) {
return
}
defer endtest()
data, err := getData(pclinetestBinary)
if err != nil {
t.Fatal(err)
}
testLineToBreakpointPCs(t, data)
testPCToLine(t, data)
}
func testPCToLine(t *testing.T, data *Data) {
entry, err := data.LookupFunction("main.main")
if err != nil {
t.Fatal(err)
}
pc, ok := entry.Val(AttrLowpc).(uint64)
if !ok {
t.Fatal(`DWARF data for function "main" has no PC`)
}
for _, tt := range []struct {
offset, want uint64
}{
{0, 19},
{19, 19},
{33, 20},
{97, 22},
{165, 23},
} {
file, line, err := data.PCToLine(pc + tt.offset)
if err != nil {
t.Fatal(err)
}
if !strings.HasSuffix(file, "/pclinetest.go") {
t.Errorf("got %s; want %s", file, "/pclinetest.go")
}
if line != tt.want {
t.Errorf("line for offset %d: got %d; want %d", tt.offset, line, tt.want)
}
}
}
func testLineToBreakpointPCs(t *testing.T, data *Data) {
for _, tt := range []struct {
line uint64
want bool
}{
{18, false},
{19, true},
{20, true},
{21, false},
{22, true},
{23, true},
{24, false},
} {
pcs, err := data.LineToBreakpointPCs("pclinetest.go", uint64(tt.line))
if err != nil {
t.Fatal(err)
}
if got := len(pcs) > 0; got != tt.want {
t.Errorf("line %d: got pcs=%t, want pcs=%t", tt.line, got, tt.want)
}
}
}
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dwarf
// This file provides simple methods to access the symbol table by name and address.
import (
"fmt"
"regexp"
"sort"
)
// lookupEntry returns the first Entry for the name.
// If tag is non-zero, only entries with that tag are considered.
func (d *Data) lookupEntry(name string, tag Tag) (*Entry, error) {
x, ok := d.nameCache[name]
if !ok {
return nil, fmt.Errorf("DWARF entry for %q not found", name)
}
for ; x != nil; x = x.link {
if tag == 0 || x.entry.Tag == tag {
return x.entry, nil
}
}
return nil, fmt.Errorf("no DWARF entry for %q with tag %s", name, tag)
}
// LookupMatchingSymbols returns the names of all top-level entries matching
// the given regular expression.
func (d *Data) LookupMatchingSymbols(nameRE *regexp.Regexp) (result []string, err error) {
for name := range d.nameCache {
if nameRE.MatchString(name) {
result = append(result, name)
}
}
return result, nil
}
// LookupEntry returns the Entry for the named symbol.
func (d *Data) LookupEntry(name string) (*Entry, error) {
return d.lookupEntry(name, 0)
}
// LookupFunction returns the entry for a function.
func (d *Data) LookupFunction(name string) (*Entry, error) {
return d.lookupEntry(name, TagSubprogram)
}
// LookupVariable returns the entry for a (global) variable.
func (d *Data) LookupVariable(name string) (*Entry, error) {
return d.lookupEntry(name, TagVariable)
}
// EntryLocation returns the address of the object referred to by the given Entry.
func (d *Data) EntryLocation(e *Entry) (uint64, error) {
loc, _ := e.Val(AttrLocation).([]byte)
if len(loc) == 0 {
return 0, fmt.Errorf("DWARF entry has no Location attribute")
}
// TODO: implement the DWARF Location bytecode. What we have here only
// recognizes a program with a single literal opAddr bytecode.
if asize := d.unit[0].asize; loc[0] == opAddr && len(loc) == 1+asize {
switch asize {
case 1:
return uint64(loc[1]), nil
case 2:
return uint64(d.order.Uint16(loc[1:])), nil
case 4:
return uint64(d.order.Uint32(loc[1:])), nil
case 8:
return d.order.Uint64(loc[1:]), nil
}
}
return 0, fmt.Errorf("DWARF entry has an unimplemented Location op")
}
// EntryType returns the Type for an Entry.
func (d *Data) EntryType(e *Entry) (Type, error) {
off, err := d.EntryTypeOffset(e)
if err != nil {
return nil, err
}
return d.Type(off)
}
// EntryTypeOffset returns the offset in the given Entry's type attribute.
func (d *Data) EntryTypeOffset(e *Entry) (Offset, error) {
v := e.Val(AttrType)
if v == nil {
return 0, fmt.Errorf("DWARF entry has no Type attribute")
}
off, ok := v.(Offset)
if !ok {
return 0, fmt.Errorf("DWARF entry has an invalid Type attribute")
}
return off, nil
}
// PCToFunction returns the entry and address for the function containing the
// specified PC.
func (d *Data) PCToFunction(pc uint64) (entry *Entry, lowpc uint64, err error) {
p := d.pcToFuncEntries
if len(p) == 0 {
return nil, 0, fmt.Errorf("no function addresses loaded")
}
i := sort.Search(len(p), func(i int) bool { return p[i].pc > pc }) - 1
// The search failed if:
// - pc was before the start of any function.
// - The largest function bound not larger than pc was the end of a function,
// not the start of one.
// - The largest function bound not larger than pc was the start of a function
// that we don't know the end of, and the PC is much larger than the start.
if i == -1 || p[i].entry == nil || (i+1 == len(p) && pc-p[i].pc >= 1<<20) {
return nil, 0, fmt.Errorf("no function at %x", pc)
}
return p[i].entry, p[i].pc, nil
}
// Copyright 2016 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import "fmt"
func main() {
fmt.Print("line 20")
fmt.Print("line 22")
}
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import "fmt"
func main() {
test(1, 2, 3)
}
// This is the function we examine. After the preamble its stack should be
// pulled down 1*addrSize for the return PC plus 3*8 for the three
// arguments. That will be (1+3)*8=32 on 64-bit machines.
func test(a, b, c int64) int64 {
// Put in enough code that it's not inlined.
for a = 0; a < 100; a++ {
b += c
}
afterTest(a, b, c)
return b
}
// This function follows test in the binary. We use it to force arguments
// onto the stack and as a delimiter in the text we scan in the test.
func afterTest(a, b, c int64) {
fmt.Println(a, b, c)
}
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
Linux ELF:
gcc -gdwarf-2 -m64 -c typedef.c && gcc -gdwarf-2 -m64 -o typedef.elf typedef.o
OS X Mach-O:
gcc -gdwarf-2 -m64 -c typedef.c -o typedef.macho
*/
#include <complex.h>
typedef volatile int* t_ptr_volatile_int;
typedef const char *t_ptr_const_char;
typedef long t_long;
typedef unsigned short t_ushort;
typedef int t_func_int_of_float_double(float, double);
typedef int (*t_ptr_func_int_of_float_double)(float, double);
typedef int (*t_ptr_func_int_of_float_complex)(float complex);
typedef int (*t_ptr_func_int_of_double_complex)(double complex);
typedef int (*t_ptr_func_int_of_long_double_complex)(long double complex);
typedef int *t_func_ptr_int_of_char_schar_uchar(char, signed char, unsigned char);
typedef void t_func_void_of_char(char);
typedef void t_func_void_of_void(void);
typedef void t_func_void_of_ptr_char_dots(char*, ...);
typedef struct my_struct {
volatile int vi;
char x : 1;
int y : 4;
int z[0];
long long array[40];
int zz[0];
} t_my_struct;
typedef struct my_struct1 {
int zz [1];
} t_my_struct1;
typedef union my_union {
volatile int vi;
char x : 1;
int y : 4;
long long array[40];
} t_my_union;
typedef enum my_enum {
e1 = 1,
e2 = 2,
e3 = -5,
e4 = 1000000000000000LL,
} t_my_enum;
typedef struct list t_my_list;
struct list {
short val;
t_my_list *next;
};
typedef struct tree {
struct tree *left, *right;
unsigned long long val;
} t_my_tree;
t_ptr_volatile_int *a2;
t_ptr_const_char **a3a;
t_long *a4;
t_ushort *a5;
t_func_int_of_float_double *a6;
t_ptr_func_int_of_float_double *a7;
t_func_ptr_int_of_char_schar_uchar *a8;
t_func_void_of_char *a9;
t_func_void_of_void *a10;
t_func_void_of_ptr_char_dots *a11;
t_my_struct *a12;
t_my_struct1 *a12a;
t_my_union *a12b;
t_my_enum *a13;
t_my_list *a14;
t_my_tree *a15;
t_ptr_func_int_of_float_complex *a16;
t_ptr_func_int_of_double_complex *a17;
t_ptr_func_int_of_long_double_complex *a18;
int main()
{
return 0;
}
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// DWARF type information structures.
// The format is heavily biased toward C, but for simplicity
// the String methods use a pseudo-Go syntax.
package dwarf
import (
"fmt"
"reflect"
"strconv"
)
// A Type conventionally represents a pointer to any of the
// specific Type structures (CharType, StructType, etc.).
type Type interface {
Common() *CommonType
String() string
Size() int64
}
// A CommonType holds fields common to multiple types.
// If a field is not known or not applicable for a given type,
// the zero value is used.
type CommonType struct {
ByteSize int64 // size of value of this type, in bytes
Name string // name that can be used to refer to type
ReflectKind reflect.Kind // the reflect kind of the type.
Offset Offset // the offset at which this type was read
}
func (c *CommonType) Common() *CommonType { return c }
func (c *CommonType) Size() int64 { return c.ByteSize }
// Basic types
// A BasicType holds fields common to all basic types.
type BasicType struct {
CommonType
BitSize int64
BitOffset int64
}
func (b *BasicType) Basic() *BasicType { return b }
func (t *BasicType) String() string {
if t.Name != "" {
return t.Name
}
return "?"
}
// A CharType represents a signed character type.
type CharType struct {
BasicType
}
// A UcharType represents an unsigned character type.
type UcharType struct {
BasicType
}
// An IntType represents a signed integer type.
type IntType struct {
BasicType
}
// A UintType represents an unsigned integer type.
type UintType struct {
BasicType
}
// A FloatType represents a floating point type.
type FloatType struct {
BasicType
}
// A ComplexType represents a complex floating point type.
type ComplexType struct {
BasicType
}
// A BoolType represents a boolean type.
type BoolType struct {
BasicType
}
// An AddrType represents a machine address type.
type AddrType struct {
BasicType
}
// An UnspecifiedType represents an implicit, unknown, ambiguous or nonexistent type.
type UnspecifiedType struct {
BasicType
}
// qualifiers
// A QualType represents a type that has the C/C++ "const", "restrict", or "volatile" qualifier.
type QualType struct {
CommonType
Qual string
Type Type
}
func (t *QualType) String() string { return t.Qual + " " + t.Type.String() }
func (t *QualType) Size() int64 { return t.Type.Size() }
// An ArrayType represents a fixed size array type.
type ArrayType struct {
CommonType
Type Type
StrideBitSize int64 // if > 0, number of bits to hold each element
Count int64 // if == -1, an incomplete array, like char x[].
}
func (t *ArrayType) String() string {
return "[" + strconv.FormatInt(t.Count, 10) + "]" + t.Type.String()
}
func (t *ArrayType) Size() int64 { return t.Count * t.Type.Size() }
// A VoidType represents the C void type.
type VoidType struct {
CommonType
}
func (t *VoidType) String() string { return "void" }
// A PtrType represents a pointer type.
type PtrType struct {
CommonType
Type Type
}
func (t *PtrType) String() string { return "*" + t.Type.String() }
// A StructType represents a struct, union, or C++ class type.
type StructType struct {
CommonType
StructName string
Kind string // "struct", "union", or "class".
Field []*StructField
Incomplete bool // if true, struct, union, class is declared but not defined
}
// A StructField represents a field in a struct, union, or C++ class type.
type StructField struct {
Name string
Type Type
ByteOffset int64
ByteSize int64
BitOffset int64 // within the ByteSize bytes at ByteOffset
BitSize int64 // zero if not a bit field
Embedded bool
}
func (t *StructType) String() string {
if t.StructName != "" {
return t.Kind + " " + t.StructName
}
return t.Defn()
}
func (t *StructType) Defn() string {
s := t.Kind
if t.StructName != "" {
s += " " + t.StructName
}
if t.Incomplete {
s += " /*incomplete*/"
return s
}
s += " {"
for i, f := range t.Field {
if i > 0 {
s += "; "
}
s += f.Name + " " + f.Type.String()
s += "@" + strconv.FormatInt(f.ByteOffset, 10)
if f.BitSize > 0 {
s += " : " + strconv.FormatInt(f.BitSize, 10)
s += "@" + strconv.FormatInt(f.BitOffset, 10)
}
}
s += "}"
return s
}
// A SliceType represents a Go slice type. It looks like a StructType, describing
// the runtime-internal structure, with extra fields.
type SliceType struct {
StructType
ElemType Type
}
func (t *SliceType) String() string {
if t.Name != "" {
return t.Name
}
return "[]" + t.ElemType.String()
}
// A StringType represents a Go string type. It looks like a StructType, describing
// the runtime-internal structure, but we wrap it for neatness.
type StringType struct {
StructType
}
func (t *StringType) String() string {
if t.Name != "" {
return t.Name
}
return "string"
}
// An InterfaceType represents a Go interface.
type InterfaceType struct {
TypedefType
}
func (t *InterfaceType) String() string {
if t.Name != "" {
return t.Name
}
return "Interface"
}
// An EnumType represents an enumerated type.
// The only indication of its native integer type is its ByteSize
// (inside CommonType).
type EnumType struct {
CommonType
EnumName string
Val []*EnumValue
}
// An EnumValue represents a single enumeration value.
type EnumValue struct {
Name string
Val int64
}
func (t *EnumType) String() string {
s := "enum"
if t.EnumName != "" {
s += " " + t.EnumName
}
s += " {"
for i, v := range t.Val {
if i > 0 {
s += "; "
}
s += v.Name + "=" + strconv.FormatInt(v.Val, 10)
}
s += "}"
return s
}
// A FuncType represents a function type.
type FuncType struct {
CommonType
ReturnType Type
ParamType []Type
}
func (t *FuncType) String() string {
s := "func("
for i, t := range t.ParamType {
if i > 0 {
s += ", "
}
s += t.String()
}
s += ")"
if t.ReturnType != nil {
s += " " + t.ReturnType.String()
}
return s
}
// A DotDotDotType represents the variadic ... function parameter.
type DotDotDotType struct {
CommonType
}
func (t *DotDotDotType) String() string { return "..." }
// A TypedefType represents a named type.
type TypedefType struct {
CommonType
Type Type
}
func (t *TypedefType) String() string { return t.Name }
func (t *TypedefType) Size() int64 { return t.Type.Size() }
// A MapType represents a Go map type. It looks like a TypedefType, describing
// the runtime-internal structure, with extra fields.
type MapType struct {
TypedefType
KeyType Type
ElemType Type
}
func (t *MapType) String() string {
if t.Name != "" {
return t.Name
}
return "map[" + t.KeyType.String() + "]" + t.ElemType.String()
}
// A ChanType represents a Go channel type.
type ChanType struct {
TypedefType
ElemType Type
}
func (t *ChanType) String() string {
if t.Name != "" {
return t.Name
}
return "chan " + t.ElemType.String()
}
// typeReader is used to read from either the info section or the
// types section.
type typeReader interface {
Seek(Offset)
Next() (*Entry, error)
clone() typeReader
offset() Offset
// AddressSize returns the size in bytes of addresses in the current
// compilation unit.
AddressSize() int
}
// Type reads the type at off in the DWARF ``info'' section.
func (d *Data) Type(off Offset) (Type, error) {
return d.readType("info", d.Reader(), off, d.typeCache)
}
func getKind(e *Entry) reflect.Kind {
integer, _ := e.Val(AttrGoKind).(int64)
return reflect.Kind(integer)
}
// readType reads a type from r at off of name using and updating a
// type cache.
func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Offset]Type) (Type, error) {
if t, ok := typeCache[off]; ok {
return t, nil
}
r.Seek(off)
e, err := r.Next()
if err != nil {
return nil, err
}
addressSize := r.AddressSize()
if e == nil || e.Offset != off {
return nil, DecodeError{name, off, "no type at offset"}
}
// Parse type from Entry.
// Must always set typeCache[off] before calling
// d.Type recursively, to handle circular types correctly.
var typ Type
nextDepth := 0
// Get next child; set err if error happens.
next := func() *Entry {
if !e.Children {
return nil
}
// Only return direct children.
// Skip over composite entries that happen to be nested
// inside this one. Most DWARF generators wouldn't generate
// such a thing, but clang does.
// See golang.org/issue/6472.
for {
kid, err1 := r.Next()
if err1 != nil {
err = err1
return nil
}
if kid == nil {
err = DecodeError{name, r.offset(), "unexpected end of DWARF entries"}
return nil
}
if kid.Tag == 0 {
if nextDepth > 0 {
nextDepth--
continue
}
return nil
}
if kid.Children {
nextDepth++
}
if nextDepth > 0 {
continue
}
return kid
}
}
// Get Type referred to by Entry's attr.
// Set err if error happens. Not having a type is an error.
typeOf := func(e *Entry, attr Attr) Type {
tval := e.Val(attr)
var t Type
switch toff := tval.(type) {
case Offset:
if t, err = d.readType(name, r.clone(), toff, typeCache); err != nil {
return nil
}
case uint64:
if t, err = d.sigToType(toff); err != nil {
return nil
}
default:
// It appears that no Type means "void".
return new(VoidType)
}
return t
}
switch e.Tag {
case TagArrayType:
// Multi-dimensional array. (DWARF v2 §5.4)
// Attributes:
// AttrType:subtype [required]
// AttrStrideSize: distance in bits between each element of the array
// AttrStride: distance in bytes between each element of the array
// AttrByteSize: size of entire array
// Children:
// TagSubrangeType or TagEnumerationType giving one dimension.
// dimensions are in left to right order.
t := new(ArrayType)
t.Name, _ = e.Val(AttrName).(string)
t.ReflectKind = getKind(e)
typ = t
typeCache[off] = t
if t.Type = typeOf(e, AttrType); err != nil {
goto Error
}
if bytes, ok := e.Val(AttrStride).(int64); ok {
t.StrideBitSize = 8 * bytes
} else if bits, ok := e.Val(AttrStrideSize).(int64); ok {
t.StrideBitSize = bits
} else {
// If there's no stride specified, assume it's the size of the
// array's element type.
t.StrideBitSize = 8 * t.Type.Size()
}
// Accumulate dimensions,
ndim := 0
for kid := next(); kid != nil; kid = next() {
// TODO(rsc): Can also be TagEnumerationType
// but haven't seen that in the wild yet.
switch kid.Tag {
case TagSubrangeType:
count, ok := kid.Val(AttrCount).(int64)
if !ok {
// Old binaries may have an upper bound instead.
count, ok = kid.Val(AttrUpperBound).(int64)
if ok {
count++ // Length is one more than upper bound.
} else {
count = -1 // As in x[].
}
}
if ndim == 0 {
t.Count = count
} else {
// Multidimensional array.
// Create new array type underneath this one.
t.Type = &ArrayType{Type: t.Type, Count: count}
}
ndim++
case TagEnumerationType:
err = DecodeError{name, kid.Offset, "cannot handle enumeration type as array bound"}
goto Error
}
}
if ndim == 0 {
// LLVM generates this for x[].
t.Count = -1
}
case TagBaseType:
// Basic type. (DWARF v2 §5.1)
// Attributes:
// AttrName: name of base type in programming language of the compilation unit [required]
// AttrEncoding: encoding value for type (encFloat etc) [required]
// AttrByteSize: size of type in bytes [required]
// AttrBitOffset: for sub-byte types, size in bits
// AttrBitSize: for sub-byte types, bit offset of high order bit in the AttrByteSize bytes
name, _ := e.Val(AttrName).(string)
enc, ok := e.Val(AttrEncoding).(int64)
if !ok {
err = DecodeError{name, e.Offset, "missing encoding attribute for " + name}
goto Error
}
switch enc {
default:
err = DecodeError{name, e.Offset, "unrecognized encoding attribute value"}
goto Error
case encAddress:
typ = new(AddrType)
case encBoolean:
typ = new(BoolType)
case encComplexFloat:
typ = new(ComplexType)
if name == "complex" {
// clang writes out 'complex' instead of 'complex float' or 'complex double'.
// clang also writes out a byte size that we can use to distinguish.
// See issue 8694.
switch byteSize, _ := e.Val(AttrByteSize).(int64); byteSize {
case 8:
name = "complex float"
case 16:
name = "complex double"
}
}
case encFloat:
typ = new(FloatType)
case encSigned:
typ = new(IntType)
case encUnsigned:
typ = new(UintType)
case encSignedChar:
typ = new(CharType)
case encUnsignedChar:
typ = new(UcharType)
}
typeCache[off] = typ
t := typ.(interface {
Basic() *BasicType
}).Basic()
t.Name = name
t.BitSize, _ = e.Val(AttrBitSize).(int64)
t.BitOffset, _ = e.Val(AttrBitOffset).(int64)
t.ReflectKind = getKind(e)
case TagClassType, TagStructType, TagUnionType:
// Structure, union, or class type. (DWARF v2 §5.5)
// Also Slices and Strings (Go-specific).
// Attributes:
// AttrName: name of struct, union, or class
// AttrByteSize: byte size [required]
// AttrDeclaration: if true, struct/union/class is incomplete
// AttrGoElem: present for slices only.
// Children:
// TagMember to describe one member.
// AttrName: name of member [required]
// AttrType: type of member [required]
// AttrByteSize: size in bytes
// AttrBitOffset: bit offset within bytes for bit fields
// AttrBitSize: bit size for bit fields
// AttrDataMemberLoc: location within struct [required for struct, class]
// There is much more to handle C++, all ignored for now.
t := new(StructType)
t.ReflectKind = getKind(e)
switch t.ReflectKind {
case reflect.Slice:
slice := new(SliceType)
slice.ElemType = typeOf(e, AttrGoElem)
t = &slice.StructType
typ = slice
case reflect.String:
str := new(StringType)
t = &str.StructType
typ = str
default:
typ = t
}
typeCache[off] = typ
switch e.Tag {
case TagClassType:
t.Kind = "class"
case TagStructType:
t.Kind = "struct"
case TagUnionType:
t.Kind = "union"
}
t.Name, _ = e.Val(AttrName).(string)
t.StructName, _ = e.Val(AttrName).(string)
t.Incomplete = e.Val(AttrDeclaration) != nil
t.Field = make([]*StructField, 0, 8)
var lastFieldType Type
var lastFieldBitOffset int64
for kid := next(); kid != nil; kid = next() {
if kid.Tag == TagMember {
f := new(StructField)
if f.Type = typeOf(kid, AttrType); err != nil {
goto Error
}
switch loc := kid.Val(AttrDataMemberLoc).(type) {
case []byte:
// TODO: Should have original compilation
// unit here, not unknownFormat.
if len(loc) == 0 {
// Empty exprloc. f.ByteOffset=0.
break
}
b := makeBuf(d, unknownFormat{}, "location", 0, loc)
op := b.uint8()
switch op {
case opPlusUconst:
// Handle opcode sequence [DW_OP_plus_uconst <uleb128>]
f.ByteOffset = int64(b.uint())
b.assertEmpty()
case opConsts:
// Handle opcode sequence [DW_OP_consts <sleb128> DW_OP_plus]
f.ByteOffset = b.int()
op = b.uint8()
if op != opPlus {
err = DecodeError{name, kid.Offset, fmt.Sprintf("unexpected opcode 0x%x", op)}
goto Error
}
b.assertEmpty()
default:
err = DecodeError{name, kid.Offset, fmt.Sprintf("unexpected opcode 0x%x", op)}
goto Error
}
if b.err != nil {
err = b.err
goto Error
}
case int64:
f.ByteOffset = loc
}
haveBitOffset := false
f.Name, _ = kid.Val(AttrName).(string)
f.ByteSize, _ = kid.Val(AttrByteSize).(int64)
f.BitOffset, haveBitOffset = kid.Val(AttrBitOffset).(int64)
f.BitSize, _ = kid.Val(AttrBitSize).(int64)
f.Embedded, _ = kid.Val(AttrGoEmbeddedField).(bool)
t.Field = append(t.Field, f)
bito := f.BitOffset
if !haveBitOffset {
bito = f.ByteOffset * 8
}
if bito == lastFieldBitOffset && t.Kind != "union" {
// Last field was zero width. Fix array length.
// (DWARF writes out 0-length arrays as if they were 1-length arrays.)
zeroArray(lastFieldType)
}
lastFieldType = f.Type
lastFieldBitOffset = bito
}
}
if t.Kind != "union" {
b, ok := e.Val(AttrByteSize).(int64)
if ok && b*8 == lastFieldBitOffset {
// Final field must be zero width. Fix array length.
zeroArray(lastFieldType)
}
}
case TagConstType, TagVolatileType, TagRestrictType:
// Type modifier (DWARF v2 §5.2)
// Attributes:
// AttrType: subtype
t := new(QualType)
t.Name, _ = e.Val(AttrName).(string)
t.ReflectKind = getKind(e)
typ = t
typeCache[off] = t
if t.Type = typeOf(e, AttrType); err != nil {
goto Error
}
switch e.Tag {
case TagConstType:
t.Qual = "const"
case TagRestrictType:
t.Qual = "restrict"
case TagVolatileType:
t.Qual = "volatile"
}
case TagEnumerationType:
// Enumeration type (DWARF v2 §5.6)
// Attributes:
// AttrName: enum name if any
// AttrByteSize: bytes required to represent largest value
// Children:
// TagEnumerator:
// AttrName: name of constant
// AttrConstValue: value of constant
t := new(EnumType)
t.ReflectKind = getKind(e)
typ = t
typeCache[off] = t
t.Name, _ = e.Val(AttrName).(string)
t.EnumName, _ = e.Val(AttrName).(string)
t.Val = make([]*EnumValue, 0, 8)
for kid := next(); kid != nil; kid = next() {
if kid.Tag == TagEnumerator {
f := new(EnumValue)
f.Name, _ = kid.Val(AttrName).(string)
f.Val, _ = kid.Val(AttrConstValue).(int64)
n := len(t.Val)
if n >= cap(t.Val) {
val := make([]*EnumValue, n, n*2)
copy(val, t.Val)
t.Val = val
}
t.Val = t.Val[0 : n+1]
t.Val[n] = f
}
}
case TagPointerType:
// Type modifier (DWARF v2 §5.2)
// Attributes:
// AttrType: subtype [not required! void* has no AttrType]
// AttrAddrClass: address class [ignored]
t := new(PtrType)
t.Name, _ = e.Val(AttrName).(string)
t.ReflectKind = getKind(e)
typ = t
typeCache[off] = t
if e.Val(AttrType) == nil {
t.Type = &VoidType{}
break
}
t.Type = typeOf(e, AttrType)
case TagSubroutineType:
// Subroutine type. (DWARF v2 §5.7)
// Attributes:
// AttrType: type of return value if any
// AttrName: possible name of type [ignored]
// AttrPrototyped: whether used ANSI C prototype [ignored]
// Children:
// TagFormalParameter: typed parameter
// AttrType: type of parameter
// TagUnspecifiedParameter: final ...
t := new(FuncType)
t.Name, _ = e.Val(AttrName).(string)
t.ReflectKind = getKind(e)
typ = t
typeCache[off] = t
if t.ReturnType = typeOf(e, AttrType); err != nil {
goto Error
}
t.ParamType = make([]Type, 0, 8)
for kid := next(); kid != nil; kid = next() {
var tkid Type
switch kid.Tag {
default:
continue
case TagFormalParameter:
if tkid = typeOf(kid, AttrType); err != nil {
goto Error
}
case TagUnspecifiedParameters:
tkid = &DotDotDotType{}
}
t.ParamType = append(t.ParamType, tkid)
}
case TagTypedef:
// Typedef (DWARF v2 §5.3)
// Also maps and channels (Go-specific).
// Attributes:
// AttrName: name [required]
// AttrType: type definition [required]
// AttrGoKey: present for maps.
// AttrGoElem: present for maps and channels.
t := new(TypedefType)
t.ReflectKind = getKind(e)
switch t.ReflectKind {
case reflect.Map:
m := new(MapType)
m.KeyType = typeOf(e, AttrGoKey)
m.ElemType = typeOf(e, AttrGoElem)
t = &m.TypedefType
typ = m
case reflect.Chan:
c := new(ChanType)
c.ElemType = typeOf(e, AttrGoElem)
t = &c.TypedefType
typ = c
case reflect.Interface:
it := new(InterfaceType)
t = &it.TypedefType
typ = it
default:
typ = t
}
typeCache[off] = typ
t.Name, _ = e.Val(AttrName).(string)
t.Type = typeOf(e, AttrType)
case TagUnspecifiedType:
// Unspecified type (DWARF v3 §5.2)
// Attributes:
// AttrName: name
t := new(UnspecifiedType)
typ = t
typeCache[off] = t
t.Name, _ = e.Val(AttrName).(string)
default:
err = DecodeError{name, off, "unsupported type tag"}
}
if err != nil {
goto Error
}
typ.Common().Offset = off
{
b, ok := e.Val(AttrByteSize).(int64)
if !ok {
b = -1
switch t := typ.(type) {
case *TypedefType:
b = t.Type.Size()
case *MapType:
b = t.Type.Size()
case *ChanType:
b = t.Type.Size()
case *InterfaceType:
b = t.Type.Size()
case *PtrType:
b = int64(addressSize)
}
}
typ.Common().ByteSize = b
}
return typ, nil
Error:
// If the parse fails, take the type out of the cache
// so that the next call with this offset doesn't hit
// the cache and return success.
delete(typeCache, off)
return nil, err
}
func zeroArray(t Type) {
for {
at, ok := t.(*ArrayType)
if !ok {
break
}
at.Count = 0
t = at.Type
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment