Skip to content

Commit

Permalink
libdrgn: linux_kernel: handle module section/note changes in Linux 6.14
Browse files Browse the repository at this point in the history
Linux 6.14 refactored how loadable kernel module sections and notes are
stored [1], removing explicit array lengths in favor of sysfs's existing
null-terminated arrays. This unfortunately completely breaks our module
handling. Update our section and note reading to fix it.

1: https://lore.kernel.org/all/20241227-sysfs-const-bin_attr-module-v2-0-e267275f0f37@weissschuh.net/.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
  • Loading branch information
osandov committed Feb 3, 2025
1 parent d2a2ebb commit d572768
Showing 1 changed file with 142 additions and 42 deletions.
184 changes: 142 additions & 42 deletions libdrgn/linux_kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -1457,40 +1457,83 @@ kernel_module_set_build_id(struct drgn_module *module,
_cleanup_free_ void *buf = NULL;
size_t capacity = 0;

// n = mod->notes_attrs->notes
uint64_t n;
err = drgn_object_member(&attrs, module_obj, "notes_attrs");
if (err)
return err;
err = drgn_object_member_dereference(&tmp, &attrs, "notes");
if (err)
return err;
err = drgn_object_read_unsigned(&tmp, &n);
if (err)
return err;

// attrs = mod->notes_attrs->attrs
err = drgn_object_member_dereference(&attrs, &attrs, "attrs");
if (err)
bool group = true;
uint64_t n;
err = drgn_object_member_dereference(&attrs, &attrs, "grp");
if (!err) {
// Since Linux kernel commit 4723f16de64e ("module: sysfs: Add
// notes attributes through attribute_group") (in v6.14), we
// have to iterate over struct attribute_group::bin_attrs, a
// null-terminated array of struct bin_attribute pointers.

// attr = mod->notes_attrs->grp.bin_attrs
err = drgn_object_member(&attrs, &attrs, "bin_attrs");
if (err)
return err;
} else if (drgn_error_catch(&err, DRGN_ERROR_LOOKUP)) {
// Before that, there was no struct attribute_group for notes,
// so we iterate over struct module_notes_attrs::attrs, an array
// of struct bin_attribute with a length given by struct
// module_notes_attrs::notes.
group = false;
// n = mod->notes_attrs->notes
err = drgn_object_member_dereference(&tmp, &attrs, "notes");
if (err)
return err;
err = drgn_object_read_unsigned(&tmp, &n);
if (err)
return err;

// attrs = mod->notes_attrs->attrs
err = drgn_object_member_dereference(&attrs, &attrs, "attrs");
if (err)
return err;
} else {
return err;
}

for (uint64_t i = 0; i < n; i++) {
// If we're not using struct attribute_group, we know how many
// attributes there are.
for (uint64_t i = 0; group || i < n; i++) {
// attr = attrs[i]
err = drgn_object_subscript(&attr, &attrs, i);
if (err)
return err;

// address = attr.private
err = drgn_object_member(&tmp, &attr, "private");
if (group) {
// If we're using struct attribute_group, we stop when
// we hit a NULL pointer.
err = drgn_object_read(&attr, &attr);
if (err)
return err;
bool truthy;
err = drgn_object_bool(&attr, &truthy);
if (err)
return err;
if (!truthy)
break;
} else {
// attr = &attrs[i]
err = drgn_object_address_of(&attr, &attr);
if (err)
return err;
}

// address = attr->private
err = drgn_object_member_dereference(&tmp, &attr, "private");
if (err)
return err;
uint64_t address;
err = drgn_object_read_unsigned(&tmp, &address);
if (err)
return err;

// size = attr.size
err = drgn_object_member(&tmp, &attr, "size");
// size = attr->size
err = drgn_object_member_dereference(&tmp, &attr, "size");
if (err)
return err;
uint64_t size;
Expand Down Expand Up @@ -1633,50 +1676,107 @@ kernel_module_set_section_addresses(struct drgn_module *module,
if (err)
return err;

// i = mod->sect_attrs->nsections
bool group = true;
uint64_t nsections;
err = drgn_object_member_dereference(&tmp, &attrs, "nsections");
if (err)
return err;
uint64_t i;
err = drgn_object_read_unsigned(&tmp, &i);
if (err)
return err;
if (drgn_error_catch(&err, DRGN_ERROR_LOOKUP)) {
// Since Linux kernel commit d8959b947a8d ("module: sysfs: Drop
// member 'module_sect_attrs::nsections'") (in v6.14), we have
// to iterate over struct attribute_group::bin_attrs, a
// null-terminated array of struct bin_attribute pointers.

// attrs = mod->sect_attrs->grp.bin_attrs
err = drgn_object_member_dereference(&attrs, &attrs, "grp");
if (err)
return err;
err = drgn_object_member(&attrs, &attrs, "bin_attrs");
if (err)
return err;
} else if (!err) {
// Before that, struct module_sect_attrs::grp still exists.
// However, since Linux kernel commit ed66f991bb19 ("module:
// Refactor section attr into bin attribute") (in v5.8), the
// sections are in struct attribute_group::bin_attrs, and before
// that, they're in struct attribute_group::attrs. Additionally,
// we'd then have to get the containing struct module_sect_attr
// to get the section address.
//
// Instead, it's easier to iterate over struct
// module_sect_attrs::attrs, an array of struct module_sect_attr
// with a length given by struct module_sect_attrs::nsections.
group = false;
// nsections = mod->sect_attrs->nsections
err = drgn_object_read_unsigned(&tmp, &nsections);
if (err)
return err;

// attrs = mod->sect_attrs->attrs
err = drgn_object_member_dereference(&attrs, &attrs, "attrs");
if (err)
// attrs = mod->sect_attrs->attrs
err = drgn_object_member_dereference(&attrs, &attrs, "attrs");
if (err)
return err;
} else {
return err;
}

while (i-- > 0) {
// If we're not using struct attribute_group, we know how many
// attributes there are.
for (uint64_t i = 0; group || i < nsections; i++) {
// attr = attrs[i]
err = drgn_object_subscript(&attr, &attrs, i);
if (err)
return err;

// address = attr.address
err = drgn_object_member(&tmp, &attr, "address");
if (err)
return err;
if (group) {
// If we're using struct attribute_group, we stop when
// we hit a NULL pointer.
err = drgn_object_read(&attr, &attr);
if (err)
return err;
bool truthy;
err = drgn_object_bool(&attr, &truthy);
if (err)
return err;
if (!truthy)
break;
// Since Linux kernel commit 4b2c11e4aaf7 ("module:
// sysfs: Drop member 'module_sect_attr::address'") (in
// v6.14), the section address is in struct
// bin_attribute::private.
err = drgn_object_member_dereference(&tmp, &attr,
"private");
} else {
// Before that, the section address is in struct
// module_sect_attr::address.
err = drgn_object_member(&tmp, &attr, "address");
if (err)
return err;
}
uint64_t address;
err = drgn_object_read_unsigned(&tmp, &address);
if (err)
return err;

// Since Linux kernel commit ed66f991bb19 ("module: Refactor
// section attr into bin attribute") (in v5.8), the section name
// is module_sect_attr.battr.attr.name. Before that, it is
// simply module_sect_attr.name.

// attr = attr.battr.attr
err = drgn_object_member(&attr, &attr, "battr");
if (!err) {
err = drgn_object_member(&attr, &attr, "attr");
if (group) {
// attr = attr->attr
err = drgn_object_member_dereference(&attr, &attr,
"attr");
if (err)
return err;
} else {
if (err->code != DRGN_ERROR_LOOKUP)
// Since Linux kernel commit ed66f991bb19 ("module:
// Refactor section attr into bin attribute") (in v5.8),
// the section name is module_sect_attr.battr.attr.name.
// Before that, it is simply module_sect_attr.name.

// attr = attr.battr.attr
err = drgn_object_member(&attr, &attr, "battr");
if (!err) {
err = drgn_object_member(&attr, &attr, "attr");
if (err)
return err;
} else if (!drgn_error_catch(&err, DRGN_ERROR_LOOKUP)) {
return err;
drgn_error_destroy(err);
}
}
err = drgn_object_member(&tmp, &attr, "name");
if (err)
Expand Down

0 comments on commit d572768

Please sign in to comment.