|
27 | 27 |
|
28 | 28 | #include <cassert> |
29 | 29 | #include <cstring> |
| 30 | +#include <string> |
| 31 | +#include <string_view> |
| 32 | +#include <vector> |
30 | 33 |
|
31 | 34 | #include "linux/xz.h" |
32 | 35 | #include "logging.h" |
@@ -373,47 +376,95 @@ constexpr inline bool contains(std::string_view a, std::string_view b) { |
373 | 376 | return a.find(b) != std::string_view::npos; |
374 | 377 | } |
375 | 378 |
|
| 379 | +// A clean and simple struct to hold parsed map entry data. |
| 380 | +struct MapEntry { |
| 381 | + uintptr_t start_addr; |
| 382 | + char perms[5] = {0}; // Assured null-termination |
| 383 | + std::string pathname; |
| 384 | +}; |
| 385 | + |
376 | 386 | bool ElfImg::findModuleBase() { |
377 | | - off_t load_addr; |
378 | | - bool found = false; |
| 387 | + // Open the maps file using standard C file I/O. |
379 | 388 | FILE *maps = fopen("/proc/self/maps", "r"); |
| 389 | + if (!maps) { |
| 390 | + LOGE("failed to open /proc/self/maps"); |
| 391 | + return false; |
| 392 | + } |
380 | 393 |
|
381 | | - char *buff = nullptr; |
382 | | - size_t len = 0; |
383 | | - ssize_t nread; |
384 | | - |
385 | | - while ((nread = getline(&buff, &len, maps)) != -1) { |
386 | | - std::string_view line{buff, static_cast<size_t>(nread)}; |
387 | | - |
388 | | - if ((contains(line, "r-xp") || contains(line, "r--p")) && contains(line, elf)) { |
389 | | - LOGD("found: {}", line); |
390 | | - if (auto begin = line.find_last_of(' '); |
391 | | - begin != std::string_view::npos && line[++begin] == '/') { |
392 | | - found = true; |
393 | | - elf = line.substr(begin); |
394 | | - if (elf.back() == '\n') elf.pop_back(); |
395 | | - LOGD("update path: {}", elf); |
396 | | - break; |
397 | | - } |
| 394 | + char line_buffer[512]; // A reasonable fixed-size buffer for map lines. |
| 395 | + std::vector<MapEntry> filtered_list; |
| 396 | + |
| 397 | + // Step 1: Filter all entries containing `elf` in its path. |
| 398 | + while (fgets(line_buffer, sizeof(line_buffer), maps)) { |
| 399 | + // Use an intermediate variable of a known, large type to avoid format warnings. |
| 400 | + // `unsigned long long` and `%llx` are standard and portable. |
| 401 | + unsigned long long temp_start; |
| 402 | + char path_buffer[256] = {0}; |
| 403 | + char p[5] = {0}; |
| 404 | + |
| 405 | + // Use the portable `%llx` specifier. |
| 406 | + int items_parsed = |
| 407 | + sscanf(line_buffer, "%llx-%*x %4s %*x %*s %*d %255s", &temp_start, p, path_buffer); |
| 408 | + |
| 409 | + // The filter condition: must parse the path, and it must contain the elf name. |
| 410 | + if (items_parsed == 3 && strstr(path_buffer, elf.c_str()) != nullptr) { |
| 411 | + MapEntry entry; |
| 412 | + // Safely assign the parsed value to the uintptr_t. |
| 413 | + entry.start_addr = static_cast<uintptr_t>(temp_start); |
| 414 | + strncpy(entry.perms, p, 4); |
| 415 | + entry.pathname = path_buffer; |
| 416 | + filtered_list.push_back(std::move(entry)); |
398 | 417 | } |
399 | 418 | } |
400 | | - if (!found) { |
401 | | - if (buff) free(buff); |
402 | | - LOGE("failed to read load address for {}", elf); |
403 | | - fclose(maps); |
| 419 | + fclose(maps); |
| 420 | + |
| 421 | + if (filtered_list.empty()) { |
| 422 | + LOGE("Could not find any mappings for {}", elf.data()); |
404 | 423 | return false; |
405 | 424 | } |
406 | 425 |
|
407 | | - if (char *next = buff; load_addr = strtoul(buff, &next, 16), next == buff) { |
408 | | - LOGE("failed to read load address for {}", elf); |
| 426 | + // Also part of Step 1: Print the filtered list for debugging. |
| 427 | + LOGD("Found {} filtered map entries for {}:", filtered_list.size(), elf.data()); |
| 428 | + for (const auto &entry : filtered_list) { |
| 429 | + LOGD(" {:#x} {} {}", entry.start_addr, entry.perms, entry.pathname); |
409 | 430 | } |
410 | 431 |
|
411 | | - if (buff) free(buff); |
| 432 | + const MapEntry *found_block = nullptr; |
412 | 433 |
|
413 | | - fclose(maps); |
| 434 | + // Step 2: In the filtered list, search for the first `r--p` whose next entry is `r-xp`. |
| 435 | + for (size_t i = 0; i < filtered_list.size() - 1; ++i) { |
| 436 | + if (strcmp(filtered_list[i].perms, "r--p") == 0 && |
| 437 | + strcmp(filtered_list[i + 1].perms, "r-xp") == 0) { |
| 438 | + found_block = &filtered_list[i]; |
| 439 | + LOGD("Found `r--p` -> `r-xp` pattern. Choosing base from `r--p` block at {:#x}", |
| 440 | + found_block->start_addr); |
| 441 | + break; // Pattern found, exit loop. |
| 442 | + } |
| 443 | + } |
| 444 | + |
| 445 | + // Step 2 (Fallback): If the pattern was not found, find the first `r-xp` entry. |
| 446 | + if (!found_block) { |
| 447 | + LOGD("`r--p` -> `r-xp` pattern not found. Falling back to first `r-xp` entry."); |
| 448 | + for (const auto &entry : filtered_list) { |
| 449 | + if (strcmp(entry.perms, "r-xp") == 0) { |
| 450 | + found_block = &entry; |
| 451 | + LOGD("Found first `r-xp` block at {:#x}", found_block->start_addr); |
| 452 | + break; // Fallback found, exit loop. |
| 453 | + } |
| 454 | + } |
| 455 | + } |
| 456 | + |
| 457 | + if (!found_block) { |
| 458 | + LOGE("Fatal: Could not determine a base address for {}", elf.data()); |
| 459 | + return false; |
| 460 | + } |
| 461 | + |
| 462 | + // Step 3: Use the starting address of the found block as the base address. |
| 463 | + base = reinterpret_cast<void *>(found_block->start_addr); |
| 464 | + elf = found_block->pathname; // Update elf path to the canonical one. |
414 | 465 |
|
415 | | - LOGD("get module base {}: {:#x}", elf, load_addr); |
| 466 | + LOGD("get module base {}: {:#x}", elf, found_block->start_addr); |
| 467 | + LOGD("update path: {}", elf); |
416 | 468 |
|
417 | | - base = reinterpret_cast<void *>(load_addr); |
418 | 469 | return true; |
419 | 470 | } |
0 commit comments