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
23using namespace __sanitizer;
24
25static 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
39static 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
57void __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
77bool __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