|
4 | 4 |
|
5 | 5 | #include "internal.h" |
6 | 6 | #include "platform.h" |
| 7 | +#include "program.h" |
7 | 8 | %} |
8 | 9 |
|
9 | 10 | x86-64 |
@@ -274,11 +275,81 @@ out: |
274 | 275 | return err; |
275 | 276 | } |
276 | 277 |
|
| 278 | +static struct drgn_error * |
| 279 | +linux_kernel_get_page_offset_x86_64(struct drgn_program *prog, uint64_t *ret) |
| 280 | +{ |
| 281 | + struct drgn_error *err; |
| 282 | + struct drgn_object obj; |
| 283 | + bool nonzero; |
| 284 | + |
| 285 | + /* |
| 286 | + * If KASLR is enabled, PAGE_OFFSET is easily available via |
| 287 | + * page_offset_base. |
| 288 | + */ |
| 289 | + drgn_object_init(&obj, prog); |
| 290 | + err = drgn_program_find_object(prog, "page_offset_base", NULL, |
| 291 | + DRGN_FIND_OBJECT_VARIABLE, &obj); |
| 292 | + if (!err) { |
| 293 | + err = drgn_object_read_unsigned(&obj, ret); |
| 294 | + goto out; |
| 295 | + } |
| 296 | + if (err->code == DRGN_ERROR_LOOKUP) |
| 297 | + drgn_error_destroy(err); |
| 298 | + else |
| 299 | + goto out; |
| 300 | + |
| 301 | + /* |
| 302 | + * If not, we determine PAGE_OFFSET based on the kernel page table. On |
| 303 | + * x86_64, swapper_pg_dir is a macro defined to init_top_pgt, which |
| 304 | + * itself is defined in assembly. They're both available in VMCOREINFO, |
| 305 | + * but it's easier to get it from init_mm.pgd. |
| 306 | + */ |
| 307 | + err = drgn_program_find_object(prog, "init_mm", NULL, |
| 308 | + DRGN_FIND_OBJECT_VARIABLE, &obj); |
| 309 | + if (err) |
| 310 | + goto out; |
| 311 | + err = drgn_object_member(&obj, &obj, "pgd"); |
| 312 | + if (err) |
| 313 | + goto out; |
| 314 | + |
| 315 | + /* |
| 316 | + * Before Linux kernel commit d52888aa2753 ("x86/mm: Move LDT remap out |
| 317 | + * of KASLR region on 5-level paging") (in v4.20), PAGE_OFFSET was pgd |
| 318 | + * slot 272. After that, it is pgd slot 273, and slot 272 is empty |
| 319 | + * (reserved for Local Descriptor Table mappings for userspace tasks). |
| 320 | + */ |
| 321 | + err = drgn_object_subscript(&obj, &obj, 272); |
| 322 | + if (err) |
| 323 | + goto out; |
| 324 | + err = drgn_object_member(&obj, &obj, "pgd"); |
| 325 | + if (err) |
| 326 | + goto out; |
| 327 | + err = drgn_object_bool(&obj, &nonzero); |
| 328 | + if (err) |
| 329 | + goto out; |
| 330 | + if (nonzero) { |
| 331 | + if (prog->vmcoreinfo.pgtable_l5_enabled) |
| 332 | + *ret = UINT64_C(0xff10000000000000); |
| 333 | + else |
| 334 | + *ret = UINT64_C(0xffff880000000000); |
| 335 | + } else { |
| 336 | + if (prog->vmcoreinfo.pgtable_l5_enabled) |
| 337 | + *ret = UINT64_C(0xff11000000000000); |
| 338 | + else |
| 339 | + *ret = UINT64_C(0xffff888000000000); |
| 340 | + } |
| 341 | + |
| 342 | +out: |
| 343 | + drgn_object_deinit(&obj); |
| 344 | + return err; |
| 345 | +} |
| 346 | + |
277 | 347 | const struct drgn_architecture_info arch_info_x86_64 = { |
278 | 348 | ARCHITECTURE_INFO, |
279 | 349 | .default_flags = (DRGN_PLATFORM_IS_64_BIT | |
280 | 350 | DRGN_PLATFORM_IS_LITTLE_ENDIAN), |
281 | 351 | .frame_registers = frame_registers_x86_64, |
282 | 352 | .num_frame_registers = ARRAY_SIZE(frame_registers_x86_64), |
283 | 353 | .linux_kernel_set_initial_registers = linux_kernel_set_initial_registers_x86_64, |
| 354 | + .linux_kernel_get_page_offset = linux_kernel_get_page_offset_x86_64, |
284 | 355 | }; |
0 commit comments