| 1 | //===-- sanitizer_procmaps_aix.cpp ----------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // Information about the process mappings (AIX-specific parts). |
| 10 | //===----------------------------------------------------------------------===// |
| 11 | |
| 12 | #include "sanitizer_platform.h" |
| 13 | |
| 14 | #if SANITIZER_AIX |
| 15 | # include <stdio.h> |
| 16 | # include <stdlib.h> |
| 17 | # include <sys/procfs.h> |
| 18 | |
| 19 | # include "sanitizer_common.h" |
| 20 | # include "sanitizer_file.h" |
| 21 | # include "sanitizer_procmaps.h" |
| 22 | |
| 23 | using namespace __sanitizer; |
| 24 | |
| 25 | static int qsort_comp(const void* va, const void* vb) { |
| 26 | auto* a = static_cast<const prmap_t*>(va); |
| 27 | auto* b = static_cast<const prmap_t*>(vb); |
| 28 | |
| 29 | if (a->pr_vaddr < b->pr_vaddr) |
| 30 | return -1; |
| 31 | |
| 32 | if (a->pr_vaddr > b->pr_vaddr) |
| 33 | return 1; |
| 34 | |
| 35 | CHECK_EQ(a, b); |
| 36 | return 0; |
| 37 | } |
| 38 | |
| 39 | static prmap_t* SortProcMapEntries(char* buffer, uptr len) { |
| 40 | prmap_t* begin = reinterpret_cast<prmap_t*>(buffer); |
| 41 | const char* bufferEnd = buffer + len; |
| 42 | prmap_t* mapIter = begin; |
| 43 | // The AIX procmap utility detects the end of the array of `prmap`s by finding |
| 44 | // an entry where pr_size and pr_vaddr are both zero. |
| 45 | while (reinterpret_cast<char*>(mapIter) < bufferEnd && |
| 46 | (mapIter->pr_size != 0 || mapIter->pr_vaddr != 0)) |
| 47 | ++mapIter; |
| 48 | prmap_t* end = mapIter; |
| 49 | |
| 50 | size_t count = end - begin; |
| 51 | size_t elemSize = sizeof(prmap_t); |
| 52 | qsort(begin, count, elemSize, qsort_comp); |
| 53 | |
| 54 | return end; |
| 55 | } |
| 56 | |
| 57 | void __sanitizer::ReadProcMaps(ProcSelfMapsBuff* proc_maps) { |
| 58 | uptr pid = internal_getpid(); |
| 59 | constexpr unsigned BUFFER_SIZE = 128; |
| 60 | char filenameBuf[BUFFER_SIZE] = {}; |
| 61 | int filenameLen = |
| 62 | internal_snprintf(filenameBuf, BUFFER_SIZE, "/proc/%d/map" , pid); |
| 63 | CHECK_GE(filenameLen, 0); |
| 64 | CHECK_LT(filenameLen, static_cast<int>(BUFFER_SIZE)); |
| 65 | if (!ReadFileToBuffer(filenameBuf, &proc_maps->data, &proc_maps->mmaped_size, |
| 66 | &proc_maps->len)) { |
| 67 | proc_maps->data = nullptr; |
| 68 | proc_maps->mmaped_size = 0; |
| 69 | proc_maps->len = 0; |
| 70 | proc_maps->mapEnd = nullptr; |
| 71 | return; |
| 72 | } |
| 73 | |
| 74 | proc_maps->mapEnd = SortProcMapEntries(proc_maps->data, proc_maps->len); |
| 75 | } |
| 76 | |
| 77 | bool __sanitizer::MemoryMappingLayout::Next(MemoryMappedSegment* segment) { |
| 78 | if (Error()) |
| 79 | return false; // simulate empty maps |
| 80 | |
| 81 | auto* mapIter = reinterpret_cast<const prmap_t*>(data_.current); |
| 82 | |
| 83 | if (mapIter >= data_.proc_self_maps.mapEnd) |
| 84 | return false; |
| 85 | |
| 86 | // Skip the kernel segment. |
| 87 | if ((mapIter->pr_mflags & MA_TYPE_MASK) == MA_KERNTEXT) |
| 88 | ++mapIter; |
| 89 | |
| 90 | if (mapIter >= data_.proc_self_maps.mapEnd) |
| 91 | return false; |
| 92 | |
| 93 | // The following has to be a C-style cast because the source type requires |
| 94 | // static_cast in 32-bit mode and reinterpret_cast in 64-bit mode. |
| 95 | segment->start = (uptr)mapIter->pr_vaddr; |
| 96 | segment->end = segment->start + mapIter->pr_size; |
| 97 | |
| 98 | segment->protection = 0; |
| 99 | uint32_t flags = mapIter->pr_mflags; |
| 100 | if (flags & MA_READ) |
| 101 | segment->protection |= kProtectionRead; |
| 102 | if (flags & MA_WRITE) |
| 103 | segment->protection |= kProtectionWrite; |
| 104 | if (flags & MA_EXEC) |
| 105 | segment->protection |= kProtectionExecute; |
| 106 | if (flags & MA_SHARED) |
| 107 | segment->protection |= kProtectionShared; |
| 108 | |
| 109 | // Handle filenames for loaded objects. |
| 110 | // pr_pathoff is non-zero iff the map entry is for a loaded object. |
| 111 | if (segment->filename && mapIter->pr_pathoff) { |
| 112 | // Use path /proc/<pid>/object/<object_id>. |
| 113 | // TODO: Pass a separate path from mapIter->pr_pathoff to display to the |
| 114 | // user. |
| 115 | // FIXME: Append the archive member name if it exists. |
| 116 | int objPathLen = internal_snprintf( |
| 117 | segment->filename, segment->filename_size, "/proc/%d/object/%s" , |
| 118 | internal_getpid(), mapIter->pr_mapname); |
| 119 | if (objPathLen < 0) |
| 120 | segment->filename[0] = 0; |
| 121 | } else if (segment->filename) { |
| 122 | segment->filename[0] = 0; |
| 123 | } |
| 124 | |
| 125 | // AIX does not report the offset into any loaded modules. The offset into the |
| 126 | // module is best handled by recording the segment type and having the |
| 127 | // symbolizer determine the offset using that. |
| 128 | // FIXME: Record the segment type. |
| 129 | segment->offset = 0; |
| 130 | |
| 131 | ++mapIter; |
| 132 | data_.current = reinterpret_cast<const char*>(mapIter); |
| 133 | |
| 134 | return true; |
| 135 | } |
| 136 | |
| 137 | #endif // SANITIZER_AIX |
| 138 | |