If CONFIG_KALLSYMS_ALL=n, then /proc/kallsyms won't include lo_fops,
which is a data symbol. Use a function symbol, lo_open, instead. Also
check whether /proc/kallsyms exists in the first place.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
A pointer, array, or function referring to an anonymous type currently
includes the full type definition in its type name. This creates very
badly formatted objects for, e.g., drgn's own hash table types. Instead,
use "struct <anonymous>" in the type name.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Linux kernel commit 47e9624616c8 ("block: remove support for cryptoloop
and the xor transfer") removed the loop_register_transfer function. We
only used that symbol because it and loop_unregister_transfer were the
only global symbols in the loop module. Now that we can get local
symbols by name, we can use the "lo_fops" symbol, which is unlikely to
be removed or renamed.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Global symbols are preferred over weak symbols, and weak symbols are
preferred over other symbols.
dwfl_module_addrinfo() seems to have the same preference, so document
address lookups as having the same behavior. (This is actually incorrect
in the case of STB_GNU_UNIQUE, as dwfl_module_addrinfo() treats anything
other than STB_GLOBAL, STB_WEAK, and STB_LOCAL as having the lowest
precedence, but STB_GNU_UNIQUE is so obscure that it probably doesn't
matter.)
Based on work from Stephen Brennan. Closes#121.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Add some scaffolding to generate ELF files with symbol tables and use it
to test symbol lookups and Elf_Sym -> drgn.Symbol translation.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Only add SHT_NULL and .shstrtab sections if there are other sections to
be added. This allows us to create core dumps with no sections, like
core dumps on Linux.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
readelf warns that a non-zero e_phoff with a zero e_phnum is invalid:
Warning: possibly corrupt ELF header - it has a non-zero program header offset, but no program headers
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Stephen Brennan reported a flaky test while working on #121:
======================================================================
ERROR: test_by_task_struct (tests.helpers.linux.test_stack_trace.TestStackTrace)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/runner/work/drgn/drgn/tests/helpers/linux/test_stack_trace.py", line 22, in test_by_task_struct
self.assertIn("pause", str(self.prog.stack_trace(find_task(self.prog, pid))))
ValueError: cannot unwind stack of running task
The problem is that the stack trace tests wait for the thread state to
change to "S". However, the state is updated while the thread is still
technically running. For example, the pause() system call is implemented
as:
SYSCALL_DEFINE0(pause)
{
while (!signal_pending(current)) {
__set_current_state(TASK_INTERRUPTIBLE);
schedule();
}
return -ERESTARTNOHAND;
}
If Program.stack_trace() accesses the thread after the state is changed
but before the thread has actually been scheduled out (namely, before
task_struct::on_cpu is set to 0), it will fail.
Instead, let's check /proc/$pid/syscall, which contains "running" until
the thread is completely scheduled out.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
These haven't been running in vmtest since they were added. Enable
cgroup2 in vmtest and rework the cgroup tests to create cgroups that we
can test with.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Add a helper, qdisc_lookup(), to get a Qdisc (struct Qdisc *) from a
network device and a major handle number. As an example:
>>> eth0 = netdev_get_by_name(prog, "eth0")
>>> tbf = qdisc_lookup(eth0, 0x20)
>>> tbf.ops.id.string_().decode()
tbf
>>> ingress = qdisc_lookup(eth0, 0xffff)
>>> ingress.ops.id.string_().decode()
ingress
Testing depends on pyroute2. `TestTc` is skipped if pyroute2 is not
found; test_qdisc_lookup() is skipped if the kernel is not built with the
following options:
CONFIG_DUMMY
CONFIG_NET_SCH_PRIO
CONFIG_NET_SCH_SFQ
CONFIG_NET_SCH_TBF
CONFIG_NET_SCH_INGRESS
Suggested-by: Cong Wang <cong.wang@bytedance.com>
Signed-off-by: Peilin Ye <peilin.ye@bytedance.com>
Add a helper, netdev_for_each_tx_queue(), to iterate over all TX queues of
a network device. As an example:
>>> eth0 = netdev_get_by_name(prog, "eth0")
>>> for txq in netdev_for_each_tx_queue(eth0):
... print(txq.qdisc.ops.id.string_().decode())
...
sfq
tbf
prio
pfifo_fast
Set up `net` in setUpClass(), since now several tests use it. Also use
it in test_netdev_get_by_{index,name}(), instead of assuming `init_net`.
Signed-off-by: Peilin Ye <peilin.ye@bytedance.com>
This already caches class variables, and it's shared across all Linux
helper test cases, so it makes more sense as setUpClass. This will also
allow subclasses to use cls.prog in their own setUpClass.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Add a helper, get_net_ns_by_inode(), to get the network namespace
("netns") descriptor (struct net *) given an netns NSFS pseudo-file
inode (struct inode *) e.g. "/proc/$PID/ns/net" or "/run/netns/$NAME".
As an example:
>>> inode = path_lookup(prog, "/run/netns/foo").dentry.d_inode
>>> net = get_net_ns_by_inode(inode)
>>> netdev = netdev_get_by_name(net, "eth3")
>>> netdev.ifindex.value_()
5
Conventionally ip netns files can be found under "/var/run/netns/",
while Docker netns files can be found under "/var/run/docker/netns".
However, as pointed out by Omar, path_lookup() doesn't know how to
deal with symlinks; resolve it using something like "pwd -P" before
passing it to path_lookup().
Also add a get_net_ns_by_fd() wrapper around it as suggested by Omar.
Example:
>>> import os
>>> pid = os.getpid()
>>> task = find_task(prog, pid)
>>> file = open(f"/proc/{pid}/ns/net")
>>> net = get_net_ns_by_fd(task, file.fileno())
Add a test for get_net_ns_by_inode().
Signed-off-by: Peilin Ye <peilin.ye@bytedance.com>
Add a helper to iterate over all network namespaces in the system. As
an example:
>>> for net in for_each_net(prog):
... if netdev_get_by_name(net, "enp0s3"):
... print(net.ipv4.sysctl_ip_early_demux.value_())
...
1
Also add a test for this new helper to tests/helpers/linux/test_net.py.
Suggested-by: Cong Wang <cong.wang@bytedance.com>
Signed-off-by: Peilin Ye <peilin.ye@bytedance.com>
Add a helper to get the network device ("struct net_device *") given an
interface name. As an example:
>>> netdev = netdev_get_by_name(prog["init_net"], "lo")
>>> netdev.ifindex.value_()
1
Or pass a "Program" as the first argument, and let the helper find in
the initial network namespace (i.e. "init_net"):
>>> netdev = netdev_get_by_index(prog, "dummy0")
>>> netdev.ifindex.value_()
2
Also add a test for this new helper to tests/helpers/linux/test_net.py.
This helper simply does a linear search over the name hash table of the
network namespace, since implementing hashing in drgn is non-trivial.
It is obviously slower than net/core/dev.c:netdev_name_node_lookup() in
the kernel, but still useful.
Linux kernel commit ff92741270bf ("net: introduce name_node struct to be
used in hashlist") introduced struct netdev_name_node for name lookups.
Start by assuming that the kernel has this commit, and fall back to the
old path if that fails.
Signed-off-by: Peilin Ye <peilin.ye@bytedance.com>
Mainly unused imports, unused variables, unnecessary f-strings, and
regex literals missing the r prefix. I'm not adding it to the CI linter
because it's too noisy, though.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Add a helper to find the corresponding "struct net_device *" object
given an interface index number. As an example:
>>> netdev = netdev_get_by_index(prog["init_net"], 1)
>>> netdev.name.string_().decode()
'lo'
Or pass a "Program" as the first argument, and let the helper find in
its initial network namespace (i.e. "init_net"):
>>> netdev = netdev_get_by_index(prog, 3)
>>> netdev.name.string_().decode()
'enp0s3'
Also add a test for this new helper to tests/helpers/linux/test_net.py.
For now, a user may combine this new helper with socket.if_nametoindex()
to look up by interface name:
>>> netdev = find_netdev_by_index(prog, socket.if_nametoindex("dummy0"))
>>> netdev.name.string_().decode()
'dummy0'
However, as mentioned by Cong, one should keep in mind that
socket.if_nametoindex() is based on system's current name-to-index
mapping, which may be different from that of e.g. a kdump.
Thus, as suggested by Omar, a better way to do name lookups would be
simply linear-searching the name hash table, which is slower, but less
erorr-prone.
Signed-off-by: Peilin Ye <peilin.ye@bytedance.com>
Extract for_each_set_bit() that was added internally for the cpumask and
nodemask helpers, and add for_each_clear_bit() and test_bit() to go with
it.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Sometimes we want to traverse numa nodes in the system,
so add kernel nodemask helpers to support this.
Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>
Add a way to create an object from raw bytes. One example where I've
wanted this is creating a struct pt_regs from a PRSTATUS note or other
source.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
LEB128 allows for redundant zero/sign bits, but we currently always
treat extra bytes as overflow. Let's check those bytes correctly.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Somehow I missed this form, and I've never seen it used. It's the same
as DW_FORM_exprloc for our purposes, so it's an easy fix.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
The stack trace variable work introduced a regression that causes
objects with size zero to always be marked absent even if they have an
address. This matters because GCC sometimes seems to omit the complete
array type for arrays declared without a length, so an array variable
can end up with an incomplete array type. I saw this with the
"swapper_spaces" variable in mm/swap_state.c from the Linux kernel.
Make sure to use the address of an empty piece if the variable is also
empty.
Fixes: ffcb9ccb19 ("libdrgn: debug_info: implement creating objects from DWARF location descriptions")
Signed-off-by: Omar Sandoval <osandov@osandov.com>
drgn depends heavily on libelf and libdw, so it's useful to know what
version we're using. Add drgn._elfutils_version and use that in the CLI
and in the test cases where we currently check the libdw version.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
After all of the preparatory work, the last two missing pieces are a way
to find a variable by name in the list of scopes that we saved while
unwinding, and a way to find the containing scopes of an inlined
function. With that, we can finally look up parameters and variables in
stack traces.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Add support for evaluating a DWARF location description and translating
it into a drgn object. In this commit, this is just used for global
variables, but an upcoming commit will wire this up to stack traces for
parameters and local variables.
There are a few locations that drgn's object model can't represent yet.
DW_OP_piece/DW_OP_bit_piece can describe objects that are only partially
known or partially in memory; we approximate these where we can. We
don't have a good way to support DW_OP_implicit_pointer at all yet.
This also adds test cases for DWARF expressions, which we couldn't
easily test before.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Define that addresses for memory reads wrap around after the maximum
address rather than the current unpredictable behavior. This is done by:
1. Reworking drgn_memory_reader to work with an inclusive address range
so that a segment can contain UINT64_MAX. drgn_memory_reader remains
agnostic to the maximum address and requires that address ranges do
not overflow a uint64_t.
2. Adding the overflow/wrap-around logic to
drgn_program_add_memory_segment() and drgn_program_read_memory().
3. Changing direct uses of drgn_memory_reader_reader() to
drgn_program_read_memory() now that they are no longer equivalent.
(For some platforms, a fault might be more appropriate than wrapping
around, but this is a step in the right direction.)
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Pick a few DWARF parsing test cases that exercise the interesting cases
for DW_FORM_indirect and run them with and without DW_FORM_indirect. We
only test DW_FORM_indirect if libdw is new enough to support it.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Rather than silently ignoring attributes whose form we don't recognize,
return an error. This way, we won't mysteriously skip indexing DIEs.
While we're doing this, split the form -> instruction mapping to its own
functions.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
The Linux kernel has its own stack unwinding format for x86-64 called
ORC: https://www.kernel.org/doc/html/latest/x86/orc-unwinder.html. It is
essentially a simplified, less complete version of DWARF CFI. ORC is
generated by analyzing machine code, so it is present for all but a few
ignored functions. In contrast, DWARF CFI is generated by the compiler
and is therefore missing for functions written in assembly and inline
assembly (which is widespread in the kernel).
This implements an ORC stack unwinder: it applies ELF relocations to the
ORC sections, adds a new DRGN_CFI_RULE_REGISTER_ADD_OFFSET CFI rule
kind, parses and efficiently stores ORC data, and translates ORC to drgn
CFI rules. This will allow us to stack trace through assembly code,
interrupts, and system calls.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Split the two modes into separate tests and move the environment
variable fiddling into a separate helper function.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
If a member is a bit field, then we should format it with the underlying
Object so that it shows the bit field size.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
drgn_compound_type_from_dwarf() and drgn_enum_type_from_dwarf() check
the DW_AT_declaration flag to decide whether the type is a declaration
of an incomplete type or a definition of a complete type. However, they
check DW_AT_declaration with dwarf_attr_integrate(), which follows the
DW_AT_specification reference if it is present. The DIE referenced by
DW_AT_specification typically is a declaration, so this erroneously
identifies definitions as declarations. Additionally, if
drgn_debug_info_find_complete() finds the same definition, we can end up
recursing until we hit the DWARF parsing depth limit. Fix it by not
using dwarf_attr_integrate() for DW_AT_declaration.
Signed-off-by: Jay Kamat <jaygkamat@gmail.com>
TestCLiteral, TestCIntegerPromotion, TestCCommonRealType,
TestCOperators, and TestCPretty in test_object all test various
operations on objects, but since they're testing language-specific
behavior, they belong in test_language_c.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
We need to use the offset of the member in the outermost object type,
not the offset in the immediate containing type in the case of nested
anonymous structs.
Fixes: e72ecd0e2c ("libdrgn: replace drgn_program_member_info() with drgn_type_find_member()")
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Currently, reference objects and buffer value objects have a byte order.
However, this doesn't always make sense for a couple of reasons:
- Byte order is only meaningful for scalars. What does it mean for a
struct to be big endian? A struct doesn't have a most or least
significant byte; its scalar members do.
- The DWARF specification allows either types or variables to have a
byte order (DW_AT_endianity). The only producer I could find that uses
this is GCC for the scalar_storage_order type attribute, and it only
uses it for base types, not variables. GDB only seems to use to check
it for base types, as well.
So, remove the byte order from objects, and move it to integer, boolean,
floating-point, and pointer types. This model makes more sense, and it
means that we can get the binary representation of any object now.
The only downside is that we can no longer support a bit offset for
non-scalars, but as far as I can tell, nothing needs that.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Rename struct drgn_object_type to struct drgn_operand_type, add a new
struct drgn_object_type which contains all of the type-related fields
from struct drgn_object, and use it to implement drgn_object_type() and
drgn_object_type_operand(), which are replacements for
drgn_object_set_common() and drgn_object_type_encoding_and_size(). This
cleans up a lot of the boilerplate around initializing objects.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
We've nominally supported complex types since commit 75c3679147
("Rewrite drgn core in C"), but parsing them from DWARF has been
incorrect from the start (they don't have a DW_AT_type attribute like we
assume), and we never implemented proper support for complex objects.
Drop the partial implementation; we can bring it back (properly) if
someone requests it.
Signed-off-by: Omar Sandoval <osandov@osandov.com>