2021-11-21 23:59:44 +00:00
|
|
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
2022-11-02 00:05:16 +00:00
|
|
|
// SPDX-License-Identifier: LGPL-2.1-or-later
|
2019-10-25 23:23:02 +01:00
|
|
|
|
|
|
|
#include <inttypes.h>
|
2022-08-26 19:32:10 +01:00
|
|
|
#include <stdio.h>
|
2019-10-25 23:23:02 +01:00
|
|
|
|
2020-09-24 00:02:02 +01:00
|
|
|
#include "drgn.h"
|
2021-12-18 00:56:00 +00:00
|
|
|
#include "helpers.h"
|
2020-10-10 01:59:23 +01:00
|
|
|
#include "minmax.h"
|
2020-09-24 00:02:02 +01:00
|
|
|
#include "platform.h"
|
2019-10-28 09:38:51 +00:00
|
|
|
#include "program.h"
|
2022-02-17 18:45:42 +00:00
|
|
|
#include "util.h"
|
2019-10-25 23:23:02 +01:00
|
|
|
|
2022-07-08 23:34:29 +01:00
|
|
|
static void end_virtual_address_translation(struct drgn_program *prog)
|
2020-05-06 02:21:36 +01:00
|
|
|
{
|
2022-07-08 23:34:29 +01:00
|
|
|
prog->in_address_translation = false;
|
|
|
|
}
|
2020-05-06 02:21:36 +01:00
|
|
|
|
2022-07-08 23:34:29 +01:00
|
|
|
static struct drgn_error *
|
|
|
|
begin_virtual_address_translation(struct drgn_program *prog, uint64_t pgtable,
|
|
|
|
uint64_t virt_addr)
|
|
|
|
{
|
|
|
|
struct drgn_error *err;
|
2020-05-06 02:21:36 +01:00
|
|
|
|
2022-07-08 23:34:29 +01:00
|
|
|
if (prog->in_address_translation) {
|
2020-05-06 08:35:29 +01:00
|
|
|
return drgn_error_create_fault("recursive address translation; "
|
|
|
|
"page table may be missing from core dump",
|
|
|
|
virt_addr);
|
|
|
|
}
|
2022-07-08 23:34:29 +01:00
|
|
|
prog->in_address_translation = true;
|
|
|
|
if (!prog->pgtable_it) {
|
|
|
|
if (!(prog->flags & DRGN_PROGRAM_IS_LINUX_KERNEL)) {
|
|
|
|
err = drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
|
|
|
|
"virtual address translation is only available for the Linux kernel");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (!prog->has_platform) {
|
|
|
|
err = drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
|
|
|
|
"cannot do virtual address translation without platform");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (!prog->platform.arch->linux_kernel_pgtable_iterator_next) {
|
|
|
|
err = drgn_error_format(DRGN_ERROR_INVALID_ARGUMENT,
|
|
|
|
"virtual address translation is not implemented for %s architecture",
|
|
|
|
prog->platform.arch->name);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
err = prog->platform.arch->linux_kernel_pgtable_iterator_create(prog,
|
|
|
|
&prog->pgtable_it);
|
|
|
|
if (err) {
|
|
|
|
prog->pgtable_it = NULL;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
prog->pgtable_it->pgtable = pgtable;
|
|
|
|
prog->pgtable_it->virt_addr = virt_addr;
|
|
|
|
prog->platform.arch->linux_kernel_pgtable_iterator_init(prog, prog->pgtable_it);
|
|
|
|
return NULL;
|
2020-05-06 08:35:29 +01:00
|
|
|
|
2022-07-08 23:34:29 +01:00
|
|
|
err:
|
|
|
|
end_virtual_address_translation(prog);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2022-07-02 21:14:18 +01:00
|
|
|
struct drgn_error *linux_helper_direct_mapping_offset(struct drgn_program *prog,
|
|
|
|
uint64_t *ret)
|
|
|
|
{
|
|
|
|
struct drgn_error *err;
|
|
|
|
|
|
|
|
if (prog->direct_mapping_offset_cached) {
|
|
|
|
*ret = prog->direct_mapping_offset;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The direct mapping offset can vary depending on architecture, kernel
|
|
|
|
// version, configuration options, and KASLR. Rather than dealing with
|
|
|
|
// all of that, get a virtual address in the direct mapping and
|
|
|
|
// translate it to a physical address via the page table. The difference
|
|
|
|
// is the offset.
|
|
|
|
//
|
|
|
|
// The virtual address we pick doesn't matter as long as:
|
|
|
|
//
|
|
|
|
// 1. It is in the direct mapping on all configurations of all supported
|
|
|
|
// kernel versions on all architectures.
|
|
|
|
// 2. That is unlikely to change in the future.
|
|
|
|
//
|
|
|
|
// This is our current arbitrary choice.
|
|
|
|
static const char direct_mapping_variable[] = "saved_command_line";
|
|
|
|
|
|
|
|
struct drgn_object tmp;
|
|
|
|
drgn_object_init(&tmp, prog);
|
|
|
|
err = drgn_program_find_object(prog, direct_mapping_variable, NULL,
|
|
|
|
DRGN_FIND_OBJECT_VARIABLE, &tmp);
|
|
|
|
uint64_t virt_addr;
|
|
|
|
if (!err) {
|
|
|
|
err = drgn_object_read_unsigned(&tmp, &virt_addr);
|
|
|
|
} else if (err->code == DRGN_ERROR_LOOKUP) {
|
|
|
|
// Avoid a confusing error message with our arbitrary variable
|
|
|
|
// name.
|
|
|
|
drgn_error_destroy(err);
|
|
|
|
err = drgn_error_create(DRGN_ERROR_OTHER,
|
|
|
|
"could not find variable in direct mapping");
|
|
|
|
}
|
|
|
|
drgn_object_deinit(&tmp);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = begin_virtual_address_translation(prog,
|
|
|
|
prog->vmcoreinfo.swapper_pg_dir,
|
|
|
|
virt_addr);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
uint64_t start_virt_addr, start_phys_addr;
|
|
|
|
err = prog->platform.arch->linux_kernel_pgtable_iterator_next(prog,
|
|
|
|
prog->pgtable_it,
|
|
|
|
&start_virt_addr,
|
|
|
|
&start_phys_addr);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
if (start_phys_addr == UINT64_MAX) {
|
|
|
|
err = drgn_error_create(DRGN_ERROR_OTHER,
|
|
|
|
"could not determine direct mapping offset");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
prog->direct_mapping_offset = start_virt_addr - start_phys_addr;
|
|
|
|
prog->direct_mapping_offset_cached = true;
|
|
|
|
*ret = prog->direct_mapping_offset;
|
|
|
|
err = NULL;
|
|
|
|
out:
|
|
|
|
end_virtual_address_translation(prog);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2022-07-08 23:34:29 +01:00
|
|
|
struct drgn_error *linux_helper_read_vm(struct drgn_program *prog,
|
|
|
|
uint64_t pgtable, uint64_t virt_addr,
|
|
|
|
void *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct drgn_error *err;
|
|
|
|
|
|
|
|
err = begin_virtual_address_translation(prog, pgtable, virt_addr);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
if (!count) {
|
|
|
|
err = NULL;
|
|
|
|
goto out;
|
2020-05-06 02:21:36 +01:00
|
|
|
}
|
|
|
|
|
2022-07-08 23:34:29 +01:00
|
|
|
struct pgtable_iterator *it = prog->pgtable_it;
|
|
|
|
pgtable_iterator_next_fn *next =
|
|
|
|
prog->platform.arch->linux_kernel_pgtable_iterator_next;
|
|
|
|
uint64_t read_addr = 0;
|
|
|
|
size_t read_size = 0;
|
|
|
|
do {
|
|
|
|
uint64_t start_virt_addr, start_phys_addr;
|
|
|
|
err = next(prog, it, &start_virt_addr, &start_phys_addr);
|
2020-05-06 02:21:36 +01:00
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
if (start_phys_addr == UINT64_MAX) {
|
|
|
|
err = drgn_error_create_fault("address is not mapped",
|
|
|
|
virt_addr);
|
|
|
|
break;
|
|
|
|
}
|
2022-07-08 23:34:29 +01:00
|
|
|
|
2022-07-08 23:42:14 +01:00
|
|
|
uint64_t phys_addr =
|
|
|
|
start_phys_addr + (virt_addr - start_virt_addr);
|
2022-07-08 23:34:29 +01:00
|
|
|
size_t n = min(it->virt_addr - virt_addr, (uint64_t)count);
|
2022-07-08 23:42:14 +01:00
|
|
|
if (read_size && phys_addr == read_addr + read_size) {
|
2020-05-06 02:21:36 +01:00
|
|
|
read_size += n;
|
|
|
|
} else {
|
|
|
|
if (read_size) {
|
|
|
|
err = drgn_program_read_memory(prog, buf,
|
|
|
|
read_addr,
|
|
|
|
read_size, true);
|
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
buf = (char *)buf + read_size;
|
|
|
|
}
|
2022-07-08 23:42:14 +01:00
|
|
|
read_addr = phys_addr;
|
2020-05-06 02:21:36 +01:00
|
|
|
read_size = n;
|
|
|
|
}
|
2022-07-08 23:34:29 +01:00
|
|
|
virt_addr = it->virt_addr;
|
2020-05-06 02:21:36 +01:00
|
|
|
count -= n;
|
|
|
|
} while (count);
|
|
|
|
if (!err) {
|
|
|
|
err = drgn_program_read_memory(prog, buf, read_addr, read_size,
|
|
|
|
true);
|
|
|
|
}
|
2022-07-08 23:34:29 +01:00
|
|
|
out:
|
|
|
|
end_virtual_address_translation(prog);
|
2020-05-06 02:21:36 +01:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2021-12-21 22:39:50 +00:00
|
|
|
struct drgn_error *linux_helper_per_cpu_ptr(struct drgn_object *res,
|
|
|
|
const struct drgn_object *ptr,
|
|
|
|
uint64_t cpu)
|
|
|
|
{
|
|
|
|
struct drgn_error *err;
|
|
|
|
struct drgn_program *prog = drgn_object_program(ptr);
|
|
|
|
|
|
|
|
struct drgn_object tmp;
|
|
|
|
drgn_object_init(&tmp, prog);
|
|
|
|
err = drgn_program_find_object(prog, "__per_cpu_offset", NULL,
|
|
|
|
DRGN_FIND_OBJECT_ANY, &tmp);
|
2021-12-22 00:51:15 +00:00
|
|
|
if (!err) {
|
|
|
|
err = drgn_object_subscript(&tmp, &tmp, cpu);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
union drgn_value per_cpu_offset;
|
|
|
|
err = drgn_object_read_integer(&tmp, &per_cpu_offset);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2021-12-21 22:39:50 +00:00
|
|
|
|
2021-12-22 00:51:15 +00:00
|
|
|
uint64_t ptr_value;
|
|
|
|
err = drgn_object_read_unsigned(ptr, &ptr_value);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2021-12-21 22:39:50 +00:00
|
|
|
|
2021-12-22 00:51:15 +00:00
|
|
|
err = drgn_object_set_unsigned(res,
|
|
|
|
drgn_object_qualified_type(ptr),
|
|
|
|
ptr_value + per_cpu_offset.uvalue,
|
|
|
|
0);
|
|
|
|
} else if (err->code == DRGN_ERROR_LOOKUP) {
|
|
|
|
drgn_error_destroy(err);
|
|
|
|
err = drgn_object_copy(res, ptr);
|
|
|
|
}
|
2021-12-21 22:39:50 +00:00
|
|
|
out:
|
|
|
|
drgn_object_deinit(&tmp);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2021-12-22 00:03:25 +00:00
|
|
|
struct drgn_error *linux_helper_idle_task(struct drgn_object *res, uint64_t cpu)
|
2021-12-21 22:40:57 +00:00
|
|
|
{
|
|
|
|
struct drgn_error *err;
|
|
|
|
struct drgn_program *prog = drgn_object_program(res);
|
|
|
|
|
|
|
|
struct drgn_object tmp;
|
|
|
|
drgn_object_init(&tmp, prog);
|
2021-12-22 00:03:25 +00:00
|
|
|
err = drgn_program_find_object(prog, "runqueues", NULL,
|
2021-12-21 22:40:57 +00:00
|
|
|
DRGN_FIND_OBJECT_ANY, &tmp);
|
2021-12-22 00:03:25 +00:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_address_of(&tmp, &tmp);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = linux_helper_per_cpu_ptr(&tmp, &tmp, cpu);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_member_dereference(res, &tmp, "idle");
|
2021-12-21 22:40:57 +00:00
|
|
|
out:
|
|
|
|
drgn_object_deinit(&tmp);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2022-10-04 00:08:53 +01:00
|
|
|
struct drgn_error *linux_helper_task_cpu(const struct drgn_object *task,
|
|
|
|
uint64_t *ret)
|
|
|
|
{
|
|
|
|
struct drgn_error *err;
|
|
|
|
struct drgn_object tmp;
|
|
|
|
drgn_object_init(&tmp, drgn_object_program(task));
|
|
|
|
|
|
|
|
// If CONFIG_THREAD_INFO_IN_TASK=y and since Linux kernel commit
|
|
|
|
// bcf9033e5449 ("sched: move CPU field back into thread_info if
|
|
|
|
// THREAD_INFO_IN_TASK=y") (in v5.16), the CPU is task->thread_info.cpu.
|
|
|
|
//
|
|
|
|
// If CONFIG_THREAD_INFO_IN_TASK=y but before that commit, the cpu is
|
|
|
|
// task->cpu.
|
|
|
|
//
|
|
|
|
// If CONFIG_THREAD_INFO_IN_TASK=n or before Linux kernel commit
|
|
|
|
// c65eacbe290b ("sched/core: Allow putting thread_info into
|
|
|
|
// task_struct") (in v4.9), the CPU is
|
|
|
|
// ((struct thread_info *)task->stack)->cpu.
|
|
|
|
//
|
|
|
|
// If none of those exist, then the kernel must be !SMP.
|
|
|
|
err = drgn_object_member_dereference(&tmp, task, "thread_info");
|
|
|
|
if (!err) {
|
|
|
|
err = drgn_object_member(&tmp, &tmp, "cpu");
|
|
|
|
if (err && err->code == DRGN_ERROR_LOOKUP) {
|
|
|
|
drgn_error_destroy(err);
|
|
|
|
err = drgn_object_member_dereference(&tmp, task, "cpu");
|
|
|
|
}
|
|
|
|
} else if (err->code == DRGN_ERROR_LOOKUP) {
|
|
|
|
// CONFIG_THREAD_INFO_IN_TASK=n
|
|
|
|
drgn_error_destroy(err);
|
|
|
|
err = drgn_object_member_dereference(&tmp, task, "stack");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
struct drgn_qualified_type thread_info_type;
|
|
|
|
err = drgn_program_find_type(drgn_object_program(task),
|
|
|
|
"struct thread_info *", NULL,
|
|
|
|
&thread_info_type);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_cast(&tmp, thread_info_type, &tmp);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_member_dereference(&tmp, &tmp, "cpu");
|
|
|
|
}
|
|
|
|
if (!err) {
|
|
|
|
union drgn_value value;
|
|
|
|
err = drgn_object_read_integer(&tmp, &value);
|
|
|
|
if (!err)
|
|
|
|
*ret = value.uvalue;
|
|
|
|
} else if (err->code == DRGN_ERROR_LOOKUP) {
|
|
|
|
// CONFIG_SMP=n
|
|
|
|
drgn_error_destroy(err);
|
|
|
|
*ret = 0;
|
|
|
|
err = NULL;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
drgn_object_deinit(&tmp);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2022-01-10 20:13:04 +00:00
|
|
|
struct drgn_error *
|
2022-12-14 01:10:22 +00:00
|
|
|
linux_helper_xa_load(struct drgn_object *res,
|
|
|
|
const struct drgn_object *xa, uint64_t index)
|
2019-10-25 23:23:02 +01:00
|
|
|
{
|
2022-01-10 20:13:04 +00:00
|
|
|
struct drgn_error *err;
|
2022-12-14 01:10:22 +00:00
|
|
|
|
|
|
|
struct drgn_object entry, node, tmp;
|
2022-01-10 20:13:04 +00:00
|
|
|
struct drgn_qualified_type node_type;
|
2022-12-14 01:10:22 +00:00
|
|
|
uint64_t internal_flag, node_min;
|
2022-01-10 20:13:04 +00:00
|
|
|
|
2022-12-14 01:10:22 +00:00
|
|
|
drgn_object_init(&entry, drgn_object_program(res));
|
2022-01-10 20:13:04 +00:00
|
|
|
drgn_object_init(&node, drgn_object_program(res));
|
|
|
|
drgn_object_init(&tmp, drgn_object_program(res));
|
|
|
|
|
2022-12-14 01:10:22 +00:00
|
|
|
// See xa_for_each() in drgn/helpers/linux/xarray.py for a description
|
|
|
|
// of the cases we have to handle.
|
|
|
|
// entry = xa->xa_head
|
|
|
|
err = drgn_object_member_dereference(&entry, xa, "xa_head");
|
2019-10-25 23:23:02 +01:00
|
|
|
if (!err) {
|
2022-12-14 01:10:22 +00:00
|
|
|
err = drgn_object_read(&entry, &entry);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
// node_type = struct xa_node *
|
2022-01-10 20:13:04 +00:00
|
|
|
err = drgn_program_find_type(drgn_object_program(res),
|
|
|
|
"struct xa_node *", NULL,
|
|
|
|
&node_type);
|
2019-10-25 23:23:02 +01:00
|
|
|
if (err)
|
2022-01-10 20:13:04 +00:00
|
|
|
goto out;
|
2022-12-14 01:10:22 +00:00
|
|
|
internal_flag = 2;
|
|
|
|
node_min = 4097;
|
2019-10-25 23:23:02 +01:00
|
|
|
} else if (err->code == DRGN_ERROR_LOOKUP) {
|
|
|
|
drgn_error_destroy(err);
|
2022-12-14 01:10:22 +00:00
|
|
|
// entry = (void *)xa->rnode
|
|
|
|
err = drgn_object_member_dereference(&entry, xa, "rnode");
|
2019-10-25 23:23:02 +01:00
|
|
|
if (err)
|
2022-01-10 20:13:04 +00:00
|
|
|
goto out;
|
2022-12-14 01:10:22 +00:00
|
|
|
// node_type = typeof(xa->rnode)
|
|
|
|
node_type = drgn_object_qualified_type(&entry);
|
|
|
|
struct drgn_qualified_type voidp_type;
|
2022-01-10 20:13:04 +00:00
|
|
|
err = drgn_program_find_type(drgn_object_program(res), "void *",
|
2022-12-14 01:10:22 +00:00
|
|
|
NULL, &voidp_type);
|
2019-10-25 23:23:02 +01:00
|
|
|
if (err)
|
2022-01-10 20:13:04 +00:00
|
|
|
goto out;
|
2022-12-14 01:10:22 +00:00
|
|
|
err = drgn_object_cast(&entry, voidp_type, &entry);
|
2019-10-25 23:23:02 +01:00
|
|
|
if (err)
|
2022-01-10 20:13:04 +00:00
|
|
|
goto out;
|
2022-12-14 01:10:22 +00:00
|
|
|
internal_flag = 1;
|
|
|
|
node_min = 0;
|
2019-10-25 23:23:02 +01:00
|
|
|
} else {
|
2022-01-10 20:13:04 +00:00
|
|
|
goto out;
|
2019-10-25 23:23:02 +01:00
|
|
|
}
|
|
|
|
|
2022-12-14 01:10:22 +00:00
|
|
|
// xa_is_node() or radix_tree_is_internal_node()
|
|
|
|
#define is_node(entry_value) \
|
|
|
|
(((entry_value) & 3) == internal_flag && (entry_value) >= node_min)
|
|
|
|
|
2020-12-15 01:58:44 +00:00
|
|
|
struct drgn_type_member *member;
|
|
|
|
uint64_t member_bit_offset;
|
2022-01-10 20:13:04 +00:00
|
|
|
err = drgn_type_find_member(drgn_type_type(node_type.type).type,
|
2020-12-15 01:58:44 +00:00
|
|
|
"slots", &member, &member_bit_offset);
|
2019-10-25 23:23:02 +01:00
|
|
|
if (err)
|
2022-01-10 20:13:04 +00:00
|
|
|
goto out;
|
2020-12-15 01:58:44 +00:00
|
|
|
struct drgn_qualified_type member_type;
|
2020-12-18 22:11:29 +00:00
|
|
|
err = drgn_member_type(member, &member_type, NULL);
|
2021-11-19 00:46:59 +00:00
|
|
|
if (err)
|
2020-12-15 01:58:44 +00:00
|
|
|
goto out;
|
2022-01-10 20:13:04 +00:00
|
|
|
if (drgn_type_kind(member_type.type) != DRGN_TYPE_ARRAY) {
|
|
|
|
err = drgn_error_create(DRGN_ERROR_TYPE,
|
2022-12-14 01:10:22 +00:00
|
|
|
"struct xa_node slots member is not an array");
|
2022-01-10 20:13:04 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2022-12-14 01:10:22 +00:00
|
|
|
uint64_t XA_CHUNK_MASK = drgn_type_length(member_type.type) - 1;
|
|
|
|
uint64_t sizeof_slots;
|
|
|
|
if (node_min == 0) { // !xarray
|
|
|
|
err = drgn_type_sizeof(member_type.type, &sizeof_slots);
|
2019-10-25 23:23:02 +01:00
|
|
|
if (err)
|
|
|
|
goto out;
|
2022-12-14 01:10:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t entry_value;
|
|
|
|
err = drgn_object_read_unsigned(&entry, &entry_value);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
if (is_node(entry_value)) {
|
|
|
|
// node = xa_to_node(entry)
|
|
|
|
// or
|
|
|
|
// node = entry_to_node(entry)
|
2019-10-25 23:23:02 +01:00
|
|
|
err = drgn_object_set_unsigned(&node, node_type,
|
2022-12-14 01:10:22 +00:00
|
|
|
entry_value - internal_flag, 0);
|
2019-10-25 23:23:02 +01:00
|
|
|
if (err)
|
|
|
|
goto out;
|
2022-12-14 01:10:22 +00:00
|
|
|
// node_shift = node->shift
|
2019-10-25 23:23:02 +01:00
|
|
|
err = drgn_object_member_dereference(&tmp, &node, "shift");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2022-12-14 01:10:22 +00:00
|
|
|
union drgn_value node_shift;
|
|
|
|
err = drgn_object_read_integer(&tmp, &node_shift);
|
2019-10-25 23:23:02 +01:00
|
|
|
if (err)
|
|
|
|
goto out;
|
2022-12-14 01:10:22 +00:00
|
|
|
|
|
|
|
uint64_t offset;
|
|
|
|
if (node_shift.uvalue >= 64) // Avoid undefined behavior.
|
2019-10-25 23:23:02 +01:00
|
|
|
offset = 0;
|
|
|
|
else
|
2022-12-14 01:10:22 +00:00
|
|
|
offset = index >> node_shift.uvalue;
|
|
|
|
if (offset > XA_CHUNK_MASK)
|
|
|
|
goto null;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
// entry = node->slots[offset]
|
|
|
|
err = drgn_object_member_dereference(&tmp, &node,
|
|
|
|
"slots");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_subscript(&entry, &tmp, offset);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_read(&entry, &entry);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_read_unsigned(&entry, &entry_value);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if ((entry_value & 3) == internal_flag) {
|
|
|
|
if (node_min != 0 && // xarray
|
|
|
|
entry_value < 256) { // xa_is_sibling()
|
|
|
|
// entry = node->slots[xa_to_sibling(entry)]
|
|
|
|
err = drgn_object_subscript(&entry,
|
|
|
|
&tmp,
|
|
|
|
entry_value >> 2);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_read(&entry, &entry);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_read_unsigned(&entry,
|
|
|
|
&entry_value);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
} else if (node_min == 0 && // !xarray
|
|
|
|
tmp.address <= entry_value &&
|
|
|
|
entry_value < tmp.address + sizeof_slots) { // is_sibling_entry()
|
|
|
|
// entry = *(void **)entry_to_node(entry)
|
|
|
|
struct drgn_qualified_type voidpp_type;
|
|
|
|
err = drgn_program_find_type(drgn_object_program(res),
|
|
|
|
"void **",
|
|
|
|
NULL,
|
|
|
|
&voidpp_type);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_set_unsigned(&entry,
|
|
|
|
voidpp_type,
|
|
|
|
entry_value - 1,
|
|
|
|
0);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_dereference(&entry,
|
|
|
|
&entry);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_read(&entry, &entry);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_read_unsigned(&entry,
|
|
|
|
&entry_value);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node_shift.uvalue == 0 || !is_node(entry_value))
|
|
|
|
break;
|
|
|
|
|
|
|
|
// node = xa_to_node(entry)
|
|
|
|
// or
|
|
|
|
// node = entry_to_node(entry)
|
|
|
|
err = drgn_object_set_unsigned(&node, node_type,
|
|
|
|
entry_value - internal_flag,
|
|
|
|
0);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
// node_shift = node->shift
|
|
|
|
err = drgn_object_member_dereference(&tmp, &node,
|
|
|
|
"shift");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_read_integer(&tmp, &node_shift);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (node_shift.uvalue >= 64) // Avoid undefined behavior.
|
|
|
|
offset = 0;
|
|
|
|
else
|
|
|
|
offset = (index >> node_shift.uvalue) & XA_CHUNK_MASK;
|
|
|
|
}
|
|
|
|
} else if (index) {
|
|
|
|
goto null;
|
2019-10-25 23:23:02 +01:00
|
|
|
}
|
|
|
|
|
2022-12-14 01:10:22 +00:00
|
|
|
err = drgn_object_copy(res, &entry);
|
2019-10-25 23:23:02 +01:00
|
|
|
out:
|
|
|
|
drgn_object_deinit(&tmp);
|
|
|
|
drgn_object_deinit(&node);
|
2022-12-14 01:10:22 +00:00
|
|
|
drgn_object_deinit(&entry);
|
2019-10-25 23:23:02 +01:00
|
|
|
return err;
|
2022-12-14 01:10:22 +00:00
|
|
|
|
|
|
|
null:
|
|
|
|
err = drgn_object_set_unsigned(res, drgn_object_qualified_type(&entry),
|
|
|
|
0, 0);
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
#undef is_node
|
2019-10-25 23:23:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
struct drgn_error *linux_helper_idr_find(struct drgn_object *res,
|
|
|
|
const struct drgn_object *idr,
|
|
|
|
uint64_t id)
|
|
|
|
{
|
|
|
|
struct drgn_error *err;
|
|
|
|
struct drgn_object tmp;
|
|
|
|
|
2020-08-27 19:26:39 +01:00
|
|
|
drgn_object_init(&tmp, drgn_object_program(res));
|
2019-10-25 23:23:02 +01:00
|
|
|
|
|
|
|
/* id -= idr->idr_base */
|
|
|
|
err = drgn_object_member_dereference(&tmp, idr, "idr_base");
|
|
|
|
if (!err) {
|
|
|
|
union drgn_value idr_base;
|
|
|
|
|
|
|
|
err = drgn_object_read_integer(&tmp, &idr_base);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
id -= idr_base.uvalue;
|
|
|
|
} else if (err->code == DRGN_ERROR_LOOKUP) {
|
|
|
|
/* idr_base was added in v4.16. */
|
|
|
|
drgn_error_destroy(err);
|
|
|
|
} else {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* radix_tree_lookup(&idr->idr_rt, id) */
|
|
|
|
err = drgn_object_member_dereference(&tmp, idr, "idr_rt");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_address_of(&tmp, &tmp);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2022-12-14 01:10:22 +00:00
|
|
|
err = linux_helper_xa_load(res, &tmp, id);
|
2019-10-25 23:23:02 +01:00
|
|
|
out:
|
|
|
|
drgn_object_deinit(&tmp);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Before Linux kernel commit 95846ecf9dac ("pid: replace pid bitmap
|
|
|
|
* implementation with IDR API") (in v4.15), (struct pid_namespace).idr does not
|
|
|
|
* exist, so we have to search pid_hash. We could implement pid_hashfn() and
|
|
|
|
* only search that bucket, but it's different for 32-bit and 64-bit systems,
|
|
|
|
* and it has changed at least once, in v4.7. Searching the whole hash table is
|
|
|
|
* slower but foolproof.
|
|
|
|
*/
|
|
|
|
static struct drgn_error *
|
|
|
|
find_pid_in_pid_hash(struct drgn_object *res, const struct drgn_object *ns,
|
|
|
|
const struct drgn_object *pid_hash, uint64_t pid)
|
|
|
|
{
|
|
|
|
struct drgn_error *err;
|
|
|
|
|
2020-12-15 01:58:44 +00:00
|
|
|
struct drgn_qualified_type pidp_type;
|
2022-01-10 20:13:04 +00:00
|
|
|
err = drgn_program_find_type(drgn_object_program(res), "struct pid *",
|
|
|
|
NULL, &pidp_type);
|
2019-10-25 23:23:02 +01:00
|
|
|
if (err)
|
|
|
|
return err;
|
2020-12-15 01:58:44 +00:00
|
|
|
|
2022-01-10 20:13:04 +00:00
|
|
|
struct drgn_qualified_type upid_type;
|
|
|
|
err = drgn_program_find_type(drgn_object_program(res), "struct upid",
|
|
|
|
NULL, &upid_type);
|
2019-10-25 23:23:02 +01:00
|
|
|
if (err)
|
|
|
|
return err;
|
2020-12-15 01:58:44 +00:00
|
|
|
|
|
|
|
struct drgn_type_member *pid_chain_member;
|
|
|
|
uint64_t pid_chain_bit_offset;
|
|
|
|
err = drgn_type_find_member(upid_type.type, "pid_chain",
|
|
|
|
&pid_chain_member, &pid_chain_bit_offset);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
struct drgn_type_member *nr_member;
|
|
|
|
uint64_t nr_bit_offset;
|
|
|
|
err = drgn_type_find_member(upid_type.type, "nr", &nr_member,
|
|
|
|
&nr_bit_offset);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
struct drgn_qualified_type nr_type;
|
2020-12-18 22:11:29 +00:00
|
|
|
err = drgn_member_type(nr_member, &nr_type, NULL);
|
2019-10-25 23:23:02 +01:00
|
|
|
if (err)
|
|
|
|
return err;
|
2020-12-15 01:58:44 +00:00
|
|
|
|
|
|
|
struct drgn_type_member *ns_member;
|
|
|
|
uint64_t ns_bit_offset;
|
|
|
|
err = drgn_type_find_member(upid_type.type, "ns", &ns_member,
|
|
|
|
&ns_bit_offset);
|
2019-10-25 23:23:02 +01:00
|
|
|
if (err)
|
|
|
|
return err;
|
2020-12-15 01:58:44 +00:00
|
|
|
struct drgn_qualified_type ns_type;
|
2020-12-18 22:11:29 +00:00
|
|
|
err = drgn_member_type(ns_member, &ns_type, NULL);
|
2019-10-25 23:23:02 +01:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2022-01-10 20:13:04 +00:00
|
|
|
struct drgn_object node, tmp;
|
|
|
|
drgn_object_init(&node, drgn_object_program(res));
|
|
|
|
drgn_object_init(&tmp, drgn_object_program(res));
|
|
|
|
|
|
|
|
err = drgn_object_read(&tmp, ns);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
uint64_t ns_addr;
|
|
|
|
err = drgn_object_read_unsigned(&tmp, &ns_addr);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
union drgn_value ns_level;
|
|
|
|
err = drgn_object_member_dereference(&tmp, &tmp, "level");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_read_integer(&tmp, &ns_level);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* i = 1 << pidhash_shift */
|
|
|
|
err = drgn_program_find_object(drgn_object_program(res),
|
|
|
|
"pidhash_shift", NULL,
|
|
|
|
DRGN_FIND_OBJECT_ANY, &tmp);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
union drgn_value pidhash_shift;
|
|
|
|
err = drgn_object_read_integer(&tmp, &pidhash_shift);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
uint64_t i;
|
|
|
|
if (pidhash_shift.uvalue >= 64)
|
|
|
|
i = 0;
|
|
|
|
else
|
|
|
|
i = UINT64_C(1) << pidhash_shift.uvalue;
|
2019-10-25 23:23:02 +01:00
|
|
|
while (i--) {
|
|
|
|
/* for (node = pid_hash[i].first; node; node = node->next) */
|
|
|
|
err = drgn_object_subscript(&node, pid_hash, i);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_member(&node, &node, "first");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
for (;;) {
|
|
|
|
uint64_t addr, tmp_addr;
|
|
|
|
union drgn_value node_nr;
|
|
|
|
uint64_t node_ns;
|
|
|
|
|
|
|
|
err = drgn_object_read(&node, &node);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_read_unsigned(&node, &addr);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
if (!addr)
|
|
|
|
break;
|
2020-12-15 01:58:44 +00:00
|
|
|
addr -= pid_chain_bit_offset / 8;
|
2019-10-25 23:23:02 +01:00
|
|
|
|
|
|
|
/* tmp = container_of(node, struct upid, pid_chain)->nr */
|
2020-12-15 01:58:44 +00:00
|
|
|
tmp_addr = addr + nr_bit_offset / 8;
|
|
|
|
err = drgn_object_set_reference(&tmp, nr_type, tmp_addr,
|
Track byte order in scalar types instead of objects
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>
2021-02-18 00:13:23 +00:00
|
|
|
0, 0);
|
2019-10-25 23:23:02 +01:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_read_integer(&tmp, &node_nr);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
if (node_nr.uvalue != pid)
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
/* tmp = container_of(node, struct upid, pid_chain)->ns */
|
2020-12-15 01:58:44 +00:00
|
|
|
tmp_addr = addr + ns_bit_offset / 8;
|
|
|
|
err = drgn_object_set_reference(&tmp, ns_type, tmp_addr,
|
Track byte order in scalar types instead of objects
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>
2021-02-18 00:13:23 +00:00
|
|
|
0, 0);
|
2019-10-25 23:23:02 +01:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = drgn_object_read_unsigned(&tmp, &node_ns);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
if (node_ns != ns_addr)
|
|
|
|
goto next;
|
|
|
|
|
2022-02-11 10:59:41 +00:00
|
|
|
#define FORMAT "numbers[%" PRIu64 "].pid_chain"
|
|
|
|
char member[sizeof(FORMAT)
|
|
|
|
- sizeof("%" PRIu64)
|
|
|
|
+ max_decimal_length(uint64_t)
|
|
|
|
+ 1];
|
|
|
|
snprintf(member, sizeof(member), FORMAT,
|
|
|
|
ns_level.uvalue);
|
|
|
|
#undef FORMAT
|
2019-10-25 23:23:02 +01:00
|
|
|
err = drgn_object_container_of(res, &node,
|
|
|
|
drgn_type_type(pidp_type.type),
|
|
|
|
member);
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
next:
|
|
|
|
err = drgn_object_member_dereference(&node, &node, "next");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = drgn_object_set_unsigned(res, pidp_type, 0, 0);
|
|
|
|
out:
|
|
|
|
drgn_object_deinit(&tmp);
|
|
|
|
drgn_object_deinit(&node);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct drgn_error *linux_helper_find_pid(struct drgn_object *res,
|
|
|
|
const struct drgn_object *ns,
|
|
|
|
uint64_t pid)
|
|
|
|
{
|
|
|
|
struct drgn_error *err;
|
|
|
|
struct drgn_object tmp;
|
|
|
|
|
2020-08-27 19:26:39 +01:00
|
|
|
drgn_object_init(&tmp, drgn_object_program(res));
|
2019-10-25 23:23:02 +01:00
|
|
|
|
|
|
|
/* (struct pid *)idr_find(&ns->idr, pid) */
|
|
|
|
err = drgn_object_member_dereference(&tmp, ns, "idr");
|
|
|
|
if (!err) {
|
|
|
|
struct drgn_qualified_type qualified_type;
|
|
|
|
|
|
|
|
err = drgn_object_address_of(&tmp, &tmp);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = linux_helper_idr_find(&tmp, &tmp, pid);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2020-08-27 19:26:39 +01:00
|
|
|
err = drgn_program_find_type(drgn_object_program(res),
|
|
|
|
"struct pid *", NULL,
|
2019-10-25 23:23:02 +01:00
|
|
|
&qualified_type);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_cast(res, qualified_type, &tmp);
|
|
|
|
} else if (err->code == DRGN_ERROR_LOOKUP) {
|
|
|
|
drgn_error_destroy(err);
|
2020-08-27 19:26:39 +01:00
|
|
|
err = drgn_program_find_object(drgn_object_program(res),
|
|
|
|
"pid_hash", NULL,
|
2019-10-25 23:23:02 +01:00
|
|
|
DRGN_FIND_OBJECT_ANY, &tmp);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = find_pid_in_pid_hash(res, ns, &tmp, pid);
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
drgn_object_deinit(&tmp);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct drgn_error *linux_helper_pid_task(struct drgn_object *res,
|
|
|
|
const struct drgn_object *pid,
|
|
|
|
uint64_t pid_type)
|
|
|
|
{
|
|
|
|
struct drgn_error *err;
|
|
|
|
struct drgn_qualified_type task_structp_type;
|
|
|
|
struct drgn_qualified_type task_struct_type;
|
|
|
|
bool truthy;
|
|
|
|
struct drgn_object first;
|
|
|
|
|
2020-08-27 19:26:39 +01:00
|
|
|
drgn_object_init(&first, drgn_object_program(res));
|
2019-10-25 23:23:02 +01:00
|
|
|
|
2020-08-27 19:26:39 +01:00
|
|
|
err = drgn_program_find_type(drgn_object_program(res),
|
|
|
|
"struct task_struct *", NULL,
|
2019-10-25 23:23:02 +01:00
|
|
|
&task_structp_type);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
task_struct_type = drgn_type_type(task_structp_type.type);
|
|
|
|
|
|
|
|
err = drgn_object_bool(pid, &truthy);
|
2021-12-08 21:37:01 +00:00
|
|
|
if (err)
|
|
|
|
goto out;
|
2019-10-25 23:23:02 +01:00
|
|
|
if (!truthy)
|
|
|
|
goto null;
|
|
|
|
|
|
|
|
/* first = &pid->tasks[pid_type].first */
|
|
|
|
err = drgn_object_member_dereference(&first, pid, "tasks");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_subscript(&first, &first, pid_type);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_member(&first, &first, "first");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = drgn_object_bool(&first, &truthy);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
if (!truthy)
|
|
|
|
goto null;
|
|
|
|
|
|
|
|
/* container_of(first, struct task_struct, pid_links[pid_type]) */
|
2022-02-11 10:59:41 +00:00
|
|
|
#define PID_LINKS_FORMAT "pid_links[%" PRIu64 "]"
|
|
|
|
#define PIDS_NODE_FORMAT "pids[%" PRIu64 "].node"
|
|
|
|
char member[max_iconst(sizeof(PID_LINKS_FORMAT),
|
|
|
|
sizeof(PIDS_NODE_FORMAT))
|
|
|
|
- sizeof("%" PRIu64)
|
|
|
|
+ max_decimal_length(uint64_t)
|
|
|
|
+ 1];
|
|
|
|
snprintf(member, sizeof(member), PID_LINKS_FORMAT, pid_type);
|
2019-10-25 23:23:02 +01:00
|
|
|
err = drgn_object_container_of(res, &first, task_struct_type, member);
|
|
|
|
if (err && err->code == DRGN_ERROR_LOOKUP) {
|
|
|
|
drgn_error_destroy(err);
|
|
|
|
/* container_of(first, struct task_struct, pids[pid_type].node) */
|
2022-02-11 10:59:41 +00:00
|
|
|
snprintf(member, sizeof(member), PIDS_NODE_FORMAT, pid_type);
|
|
|
|
#undef PID_LINKS_FORMAT
|
|
|
|
#undef PIDS_NODE_FORMAT
|
2019-10-25 23:23:02 +01:00
|
|
|
err = drgn_object_container_of(res, &first, task_struct_type,
|
|
|
|
member);
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
drgn_object_deinit(&first);
|
|
|
|
return err;
|
|
|
|
|
|
|
|
null:
|
|
|
|
err = drgn_object_set_unsigned(res, task_structp_type, 0, 0);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct drgn_error *linux_helper_find_task(struct drgn_object *res,
|
|
|
|
const struct drgn_object *ns,
|
|
|
|
uint64_t pid)
|
|
|
|
{
|
|
|
|
struct drgn_error *err;
|
|
|
|
struct drgn_object pid_obj;
|
|
|
|
struct drgn_object pid_type_obj;
|
|
|
|
union drgn_value pid_type;
|
|
|
|
|
2020-08-27 19:26:39 +01:00
|
|
|
drgn_object_init(&pid_obj, drgn_object_program(res));
|
|
|
|
drgn_object_init(&pid_type_obj, drgn_object_program(res));
|
2019-10-25 23:23:02 +01:00
|
|
|
|
|
|
|
err = linux_helper_find_pid(&pid_obj, ns, pid);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2020-08-27 19:26:39 +01:00
|
|
|
err = drgn_program_find_object(drgn_object_program(res), "PIDTYPE_PID",
|
|
|
|
NULL, DRGN_FIND_OBJECT_CONSTANT,
|
2019-10-25 23:23:02 +01:00
|
|
|
&pid_type_obj);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_read_integer(&pid_type_obj, &pid_type);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = linux_helper_pid_task(res, &pid_obj, pid_type.uvalue);
|
|
|
|
out:
|
|
|
|
drgn_object_deinit(&pid_type_obj);
|
|
|
|
drgn_object_deinit(&pid_obj);
|
|
|
|
return err;
|
|
|
|
}
|
2021-12-18 00:56:00 +00:00
|
|
|
|
|
|
|
struct drgn_error *
|
|
|
|
linux_helper_task_iterator_init(struct linux_helper_task_iterator *it,
|
|
|
|
struct drgn_program *prog)
|
|
|
|
{
|
|
|
|
struct drgn_error *err;
|
|
|
|
it->done = false;
|
|
|
|
drgn_object_init(&it->task, prog);
|
|
|
|
err = drgn_program_find_object(prog, "init_task", NULL,
|
|
|
|
DRGN_FIND_OBJECT_VARIABLE, &it->task);
|
|
|
|
if (err)
|
|
|
|
goto err;
|
|
|
|
it->task_struct_type = drgn_object_qualified_type(&it->task);
|
|
|
|
err = drgn_object_address_of(&it->task, &it->task);
|
|
|
|
if (err)
|
|
|
|
goto err;
|
|
|
|
err = drgn_object_read_unsigned(&it->task, &it->init_task_address);
|
|
|
|
if (err)
|
|
|
|
goto err;
|
|
|
|
it->thread_group_address = it->init_task_address;
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
err:
|
|
|
|
drgn_object_deinit(&it->task);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
void linux_helper_task_iterator_deinit(struct linux_helper_task_iterator *it)
|
|
|
|
{
|
|
|
|
drgn_object_deinit(&it->task);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct drgn_error *
|
|
|
|
linux_helper_task_iterator_next(struct linux_helper_task_iterator *it,
|
2021-11-19 00:46:59 +00:00
|
|
|
const struct drgn_object **ret)
|
2021-12-18 00:56:00 +00:00
|
|
|
{
|
|
|
|
if (it->done) {
|
|
|
|
*ret = NULL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
struct drgn_error *err;
|
|
|
|
struct drgn_object *task = &it->task;
|
|
|
|
err = drgn_object_member_dereference(task, task, "thread_group");
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
err = drgn_object_member(task, task, "next");
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
err = drgn_object_container_of(task, task, it->task_struct_type,
|
|
|
|
"thread_group");
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
uint64_t task_address;
|
|
|
|
err = drgn_object_read_unsigned(task, &task_address);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
if (task_address == it->thread_group_address) {
|
|
|
|
err = drgn_object_member_dereference(task, task, "tasks");
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
err = drgn_object_member(task, task, "next");
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
err = drgn_object_container_of(task, task,
|
|
|
|
it->task_struct_type, "tasks");
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
err = drgn_object_read_unsigned(task,
|
|
|
|
&it->thread_group_address);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
if (it->thread_group_address == it->init_task_address)
|
|
|
|
it->done = true;
|
|
|
|
}
|
|
|
|
*ret = task;
|
|
|
|
return NULL;
|
|
|
|
}
|