Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp
39644 views
1
//===-- LinuxProcMaps.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
#include "LinuxProcMaps.h"
10
#include "lldb/Target/MemoryRegionInfo.h"
11
#include "lldb/Utility/Status.h"
12
#include "lldb/Utility/StringExtractor.h"
13
#include "llvm/ADT/StringRef.h"
14
#include <optional>
15
16
using namespace lldb_private;
17
18
enum class MapsKind { Maps, SMaps };
19
20
static llvm::Expected<MemoryRegionInfo> ProcMapError(const char *msg,
21
MapsKind kind) {
22
return llvm::createStringError(llvm::inconvertibleErrorCode(), msg,
23
kind == MapsKind::Maps ? "maps" : "smaps");
24
}
25
26
static llvm::Expected<MemoryRegionInfo>
27
ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line,
28
MapsKind maps_kind) {
29
MemoryRegionInfo region;
30
StringExtractor line_extractor(maps_line);
31
32
// Format: {address_start_hex}-{address_end_hex} perms offset dev inode
33
// pathname perms: rwxp (letter is present if set, '-' if not, final
34
// character is p=private, s=shared).
35
36
// Parse out the starting address
37
lldb::addr_t start_address = line_extractor.GetHexMaxU64(false, 0);
38
39
// Parse out hyphen separating start and end address from range.
40
if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != '-'))
41
return ProcMapError(
42
"malformed /proc/{pid}/%s entry, missing dash between address range",
43
maps_kind);
44
45
// Parse out the ending address
46
lldb::addr_t end_address = line_extractor.GetHexMaxU64(false, start_address);
47
48
// Parse out the space after the address.
49
if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != ' '))
50
return ProcMapError(
51
"malformed /proc/{pid}/%s entry, missing space after range", maps_kind);
52
53
// Save the range.
54
region.GetRange().SetRangeBase(start_address);
55
region.GetRange().SetRangeEnd(end_address);
56
57
// Any memory region in /proc/{pid}/(maps|smaps) is by definition mapped
58
// into the process.
59
region.SetMapped(MemoryRegionInfo::OptionalBool::eYes);
60
61
// Parse out each permission entry.
62
if (line_extractor.GetBytesLeft() < 4)
63
return ProcMapError(
64
"malformed /proc/{pid}/%s entry, missing some portion of "
65
"permissions",
66
maps_kind);
67
68
// Handle read permission.
69
const char read_perm_char = line_extractor.GetChar();
70
if (read_perm_char == 'r')
71
region.SetReadable(MemoryRegionInfo::OptionalBool::eYes);
72
else if (read_perm_char == '-')
73
region.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
74
else
75
return ProcMapError("unexpected /proc/{pid}/%s read permission char",
76
maps_kind);
77
78
// Handle write permission.
79
const char write_perm_char = line_extractor.GetChar();
80
if (write_perm_char == 'w')
81
region.SetWritable(MemoryRegionInfo::OptionalBool::eYes);
82
else if (write_perm_char == '-')
83
region.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
84
else
85
return ProcMapError("unexpected /proc/{pid}/%s write permission char",
86
maps_kind);
87
88
// Handle execute permission.
89
const char exec_perm_char = line_extractor.GetChar();
90
if (exec_perm_char == 'x')
91
region.SetExecutable(MemoryRegionInfo::OptionalBool::eYes);
92
else if (exec_perm_char == '-')
93
region.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
94
else
95
return ProcMapError("unexpected /proc/{pid}/%s exec permission char",
96
maps_kind);
97
98
// Handle sharing status (private/shared).
99
const char sharing_char = line_extractor.GetChar();
100
if (sharing_char == 's')
101
region.SetShared(MemoryRegionInfo::OptionalBool::eYes);
102
else if (sharing_char == 'p')
103
region.SetShared(MemoryRegionInfo::OptionalBool::eNo);
104
else
105
region.SetShared(MemoryRegionInfo::OptionalBool::eDontKnow);
106
107
line_extractor.SkipSpaces(); // Skip the separator
108
line_extractor.GetHexMaxU64(false, 0); // Read the offset
109
line_extractor.GetHexMaxU64(false, 0); // Read the major device number
110
line_extractor.GetChar(); // Read the device id separator
111
line_extractor.GetHexMaxU64(false, 0); // Read the major device number
112
line_extractor.SkipSpaces(); // Skip the separator
113
line_extractor.GetU64(0, 10); // Read the inode number
114
115
line_extractor.SkipSpaces();
116
const char *name = line_extractor.Peek();
117
if (name)
118
region.SetName(name);
119
120
return region;
121
}
122
123
void lldb_private::ParseLinuxMapRegions(llvm::StringRef linux_map,
124
LinuxMapCallback const &callback) {
125
llvm::StringRef lines(linux_map);
126
llvm::StringRef line;
127
while (!lines.empty()) {
128
std::tie(line, lines) = lines.split('\n');
129
if (!callback(ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::Maps)))
130
break;
131
}
132
}
133
134
void lldb_private::ParseLinuxSMapRegions(llvm::StringRef linux_smap,
135
LinuxMapCallback const &callback) {
136
// Entries in /smaps look like:
137
// 00400000-0048a000 r-xp 00000000 fd:03 960637
138
// Size: 552 kB
139
// Rss: 460 kB
140
// <...>
141
// VmFlags: rd ex mr mw me dw
142
// 00500000-0058a000 rwxp 00000000 fd:03 960637
143
// <...>
144
//
145
// Where the first line is identical to the /maps format
146
// and VmFlags is only printed for kernels >= 3.8.
147
148
llvm::StringRef lines(linux_smap);
149
llvm::StringRef line;
150
std::optional<MemoryRegionInfo> region;
151
152
while (lines.size()) {
153
std::tie(line, lines) = lines.split('\n');
154
155
// A property line looks like:
156
// <word>: <value>
157
// (no spaces on the left hand side)
158
// A header will have a ':' but the LHS will contain spaces
159
llvm::StringRef name;
160
llvm::StringRef value;
161
std::tie(name, value) = line.split(':');
162
163
// If this line is a property line
164
if (!name.contains(' ')) {
165
if (region) {
166
if (name == "VmFlags") {
167
if (value.contains("mt"))
168
region->SetMemoryTagged(MemoryRegionInfo::eYes);
169
else
170
region->SetMemoryTagged(MemoryRegionInfo::eNo);
171
}
172
// Ignore anything else
173
} else {
174
// Orphaned settings line
175
callback(ProcMapError(
176
"Found a property line without a corresponding mapping "
177
"in /proc/{pid}/%s",
178
MapsKind::SMaps));
179
return;
180
}
181
} else {
182
// Must be a new region header
183
if (region) {
184
// Save current region
185
callback(*region);
186
region.reset();
187
}
188
189
// Try to start a new region
190
llvm::Expected<MemoryRegionInfo> new_region =
191
ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::SMaps);
192
if (new_region) {
193
region = *new_region;
194
} else {
195
// Stop at first invalid region header
196
callback(new_region.takeError());
197
return;
198
}
199
}
200
}
201
202
// Catch last region
203
if (region)
204
callback(*region);
205
}
206
207