Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
Zhou Yaochen
goActuator
Commits
420350df
Commit
420350df
authored
Nov 12, 2021
by
Zhou Yaochen
Browse files
update ignore
parent
fc1f6363
Changes
723
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
0 additions
and
4139 deletions
+0
-4139
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/arch/arch.go
...34.0/cmd/go-cloud-debug-agent/internal/debug/arch/arch.go
+0
-186
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/cmd/debugproxy/main.go
...o-cloud-debug-agent/internal/debug/cmd/debugproxy/main.go
+0
-85
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/doc/ptrace-nptl.txt
...d/go-cloud-debug-agent/internal/debug/doc/ptrace-nptl.txt
+0
-128
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/buf.go
...34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/buf.go
+0
-213
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/cache.go
....0/cmd/go-cloud-debug-agent/internal/debug/dwarf/cache.go
+0
-277
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/const.go
....0/cmd/go-cloud-debug-agent/internal/debug/dwarf/const.go
+0
-480
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/entry.go
....0/cmd/go-cloud-debug-agent/internal/debug/dwarf/entry.go
+0
-417
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/frame.go
....0/cmd/go-cloud-debug-agent/internal/debug/dwarf/frame.go
+0
-309
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/frame_test.go
...d/go-cloud-debug-agent/internal/debug/dwarf/frame_test.go
+0
-158
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/line.go
...4.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/line.go
+0
-463
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/open.go
...4.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/open.go
+0
-106
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/pclntab_test.go
...go-cloud-debug-agent/internal/debug/dwarf/pclntab_test.go
+0
-154
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/symbol.go
...0/cmd/go-cloud-debug-agent/internal/debug/dwarf/symbol.go
+0
-129
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/pclinetest.go
...d-debug-agent/internal/debug/dwarf/testdata/pclinetest.go
+0
-23
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/pcsptest.go
...oud-debug-agent/internal/debug/dwarf/testdata/pcsptest.go
+0
-39
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.c
...cloud-debug-agent/internal/debug/dwarf/testdata/typedef.c
+0
-95
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.elf
...oud-debug-agent/internal/debug/dwarf/testdata/typedef.elf
+0
-0
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.elf4
...ud-debug-agent/internal/debug/dwarf/testdata/typedef.elf4
+0
-0
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.macho
...d-debug-agent/internal/debug/dwarf/testdata/typedef.macho
+0
-0
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/type.go
...4.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/type.go
+0
-877
No files found.
Too many changes to show.
To preserve performance only
723 of 723+
files are displayed.
Plain diff
Email patch
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/arch/arch.go
deleted
100644 → 0
View file @
fc1f6363
// 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
}
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/cmd/debugproxy/main.go
deleted
100644 → 0
View file @
fc1f6363
// 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
}
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/doc/ptrace-nptl.txt
deleted
100644 → 0
View file @
fc1f6363
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.
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/buf.go
deleted
100644 → 0
View file @
fc1f6363
// 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
}
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/cache.go
deleted
100644 → 0
View file @
fc1f6363
// 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
}
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/const.go
deleted
100644 → 0
View file @
fc1f6363
// 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
)
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/entry.go
deleted
100644 → 0
View file @
fc1f6363
// 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
}
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/frame.go
deleted
100644 → 0
View file @
fc1f6363
// 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
}
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/frame_test.go
deleted
100644 → 0
View file @
fc1f6363
// 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
)
}
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/line.go
deleted
100644 → 0
View file @
fc1f6363
// 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
}
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/open.go
deleted
100644 → 0
View file @
fc1f6363
// 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
)
}
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/pclntab_test.go
deleted
100644 → 0
View file @
fc1f6363
// 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
)
}
}
}
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/symbol.go
deleted
100644 → 0
View file @
fc1f6363
// 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
}
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/pclinetest.go
deleted
100644 → 0
View file @
fc1f6363
// 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"
)
}
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/pcsptest.go
deleted
100644 → 0
View file @
fc1f6363
// 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
)
}
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.c
deleted
100644 → 0
View file @
fc1f6363
// 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
;
}
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.elf
deleted
100644 → 0
View file @
fc1f6363
File deleted
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.elf4
deleted
100644 → 0
View file @
fc1f6363
File deleted
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/testdata/typedef.macho
deleted
100644 → 0
View file @
fc1f6363
File deleted
pkg/mod/cloud.google.com/go@v0.34.0/cmd/go-cloud-debug-agent/internal/debug/dwarf/type.go
deleted
100644 → 0
View file @
fc1f6363
// 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
}
}
Prev
1
…
32
33
34
35
36
37
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment