180814287SRaphael Isemann //===-- LinuxProcMaps.cpp -------------------------------------------------===//
2026e1bf5SGreg Clayton //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6026e1bf5SGreg Clayton //
7026e1bf5SGreg Clayton //===----------------------------------------------------------------------===//
8026e1bf5SGreg Clayton
9026e1bf5SGreg Clayton #include "LinuxProcMaps.h"
10026e1bf5SGreg Clayton #include "lldb/Target/MemoryRegionInfo.h"
11026e1bf5SGreg Clayton #include "lldb/Utility/Status.h"
12026e1bf5SGreg Clayton #include "lldb/Utility/StringExtractor.h"
1332541685SDavid Spickett #include "llvm/ADT/StringRef.h"
14026e1bf5SGreg Clayton
15026e1bf5SGreg Clayton using namespace lldb_private;
16026e1bf5SGreg Clayton
1732541685SDavid Spickett enum class MapsKind { Maps, SMaps };
18026e1bf5SGreg Clayton
ProcMapError(const char * msg,MapsKind kind)1932541685SDavid Spickett static llvm::Expected<MemoryRegionInfo> ProcMapError(const char *msg,
2032541685SDavid Spickett MapsKind kind) {
2132541685SDavid Spickett return llvm::createStringError(llvm::inconvertibleErrorCode(), msg,
2232541685SDavid Spickett kind == MapsKind::Maps ? "maps" : "smaps");
2332541685SDavid Spickett }
2432541685SDavid Spickett
2532541685SDavid Spickett static llvm::Expected<MemoryRegionInfo>
ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line,MapsKind maps_kind)2632541685SDavid Spickett ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line,
2732541685SDavid Spickett MapsKind maps_kind) {
2832541685SDavid Spickett MemoryRegionInfo region;
29026e1bf5SGreg Clayton StringExtractor line_extractor(maps_line);
30026e1bf5SGreg Clayton
31026e1bf5SGreg Clayton // Format: {address_start_hex}-{address_end_hex} perms offset dev inode
32026e1bf5SGreg Clayton // pathname perms: rwxp (letter is present if set, '-' if not, final
33026e1bf5SGreg Clayton // character is p=private, s=shared).
34026e1bf5SGreg Clayton
35026e1bf5SGreg Clayton // Parse out the starting address
36026e1bf5SGreg Clayton lldb::addr_t start_address = line_extractor.GetHexMaxU64(false, 0);
37026e1bf5SGreg Clayton
38026e1bf5SGreg Clayton // Parse out hyphen separating start and end address from range.
39026e1bf5SGreg Clayton if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != '-'))
4032541685SDavid Spickett return ProcMapError(
4132541685SDavid Spickett "malformed /proc/{pid}/%s entry, missing dash between address range",
4232541685SDavid Spickett maps_kind);
43026e1bf5SGreg Clayton
44026e1bf5SGreg Clayton // Parse out the ending address
45026e1bf5SGreg Clayton lldb::addr_t end_address = line_extractor.GetHexMaxU64(false, start_address);
46026e1bf5SGreg Clayton
47026e1bf5SGreg Clayton // Parse out the space after the address.
48026e1bf5SGreg Clayton if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != ' '))
4932541685SDavid Spickett return ProcMapError(
5032541685SDavid Spickett "malformed /proc/{pid}/%s entry, missing space after range", maps_kind);
51026e1bf5SGreg Clayton
52026e1bf5SGreg Clayton // Save the range.
5332541685SDavid Spickett region.GetRange().SetRangeBase(start_address);
5432541685SDavid Spickett region.GetRange().SetRangeEnd(end_address);
55026e1bf5SGreg Clayton
5632541685SDavid Spickett // Any memory region in /proc/{pid}/(maps|smaps) is by definition mapped
5732541685SDavid Spickett // into the process.
5832541685SDavid Spickett region.SetMapped(MemoryRegionInfo::OptionalBool::eYes);
59026e1bf5SGreg Clayton
60026e1bf5SGreg Clayton // Parse out each permission entry.
61026e1bf5SGreg Clayton if (line_extractor.GetBytesLeft() < 4)
6232541685SDavid Spickett return ProcMapError(
6332541685SDavid Spickett "malformed /proc/{pid}/%s entry, missing some portion of "
6432541685SDavid Spickett "permissions",
6532541685SDavid Spickett maps_kind);
66026e1bf5SGreg Clayton
67026e1bf5SGreg Clayton // Handle read permission.
68026e1bf5SGreg Clayton const char read_perm_char = line_extractor.GetChar();
69026e1bf5SGreg Clayton if (read_perm_char == 'r')
7032541685SDavid Spickett region.SetReadable(MemoryRegionInfo::OptionalBool::eYes);
71026e1bf5SGreg Clayton else if (read_perm_char == '-')
7232541685SDavid Spickett region.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
73026e1bf5SGreg Clayton else
7432541685SDavid Spickett return ProcMapError("unexpected /proc/{pid}/%s read permission char",
7532541685SDavid Spickett maps_kind);
76026e1bf5SGreg Clayton
77026e1bf5SGreg Clayton // Handle write permission.
78026e1bf5SGreg Clayton const char write_perm_char = line_extractor.GetChar();
79026e1bf5SGreg Clayton if (write_perm_char == 'w')
8032541685SDavid Spickett region.SetWritable(MemoryRegionInfo::OptionalBool::eYes);
81026e1bf5SGreg Clayton else if (write_perm_char == '-')
8232541685SDavid Spickett region.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
83026e1bf5SGreg Clayton else
8432541685SDavid Spickett return ProcMapError("unexpected /proc/{pid}/%s write permission char",
8532541685SDavid Spickett maps_kind);
86026e1bf5SGreg Clayton
87026e1bf5SGreg Clayton // Handle execute permission.
88026e1bf5SGreg Clayton const char exec_perm_char = line_extractor.GetChar();
89026e1bf5SGreg Clayton if (exec_perm_char == 'x')
9032541685SDavid Spickett region.SetExecutable(MemoryRegionInfo::OptionalBool::eYes);
91026e1bf5SGreg Clayton else if (exec_perm_char == '-')
9232541685SDavid Spickett region.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
93026e1bf5SGreg Clayton else
9432541685SDavid Spickett return ProcMapError("unexpected /proc/{pid}/%s exec permission char",
9532541685SDavid Spickett maps_kind);
96026e1bf5SGreg Clayton
97*c0702ac0SEmre Kultursay // Handle sharing status (private/shared).
98*c0702ac0SEmre Kultursay const char sharing_char = line_extractor.GetChar();
99*c0702ac0SEmre Kultursay if (sharing_char == 's')
100*c0702ac0SEmre Kultursay region.SetShared(MemoryRegionInfo::OptionalBool::eYes);
101*c0702ac0SEmre Kultursay else if (sharing_char == 'p')
102*c0702ac0SEmre Kultursay region.SetShared(MemoryRegionInfo::OptionalBool::eNo);
103*c0702ac0SEmre Kultursay else
104*c0702ac0SEmre Kultursay region.SetShared(MemoryRegionInfo::OptionalBool::eDontKnow);
105*c0702ac0SEmre Kultursay
106026e1bf5SGreg Clayton line_extractor.SkipSpaces(); // Skip the separator
107026e1bf5SGreg Clayton line_extractor.GetHexMaxU64(false, 0); // Read the offset
108026e1bf5SGreg Clayton line_extractor.GetHexMaxU64(false, 0); // Read the major device number
109026e1bf5SGreg Clayton line_extractor.GetChar(); // Read the device id separator
110026e1bf5SGreg Clayton line_extractor.GetHexMaxU64(false, 0); // Read the major device number
111026e1bf5SGreg Clayton line_extractor.SkipSpaces(); // Skip the separator
112026e1bf5SGreg Clayton line_extractor.GetU64(0, 10); // Read the inode number
113026e1bf5SGreg Clayton
114026e1bf5SGreg Clayton line_extractor.SkipSpaces();
115026e1bf5SGreg Clayton const char *name = line_extractor.Peek();
116026e1bf5SGreg Clayton if (name)
11732541685SDavid Spickett region.SetName(name);
118026e1bf5SGreg Clayton
11932541685SDavid Spickett return region;
120026e1bf5SGreg Clayton }
121026e1bf5SGreg Clayton
ParseLinuxMapRegions(llvm::StringRef linux_map,LinuxMapCallback const & callback)122026e1bf5SGreg Clayton void lldb_private::ParseLinuxMapRegions(llvm::StringRef linux_map,
123026e1bf5SGreg Clayton LinuxMapCallback const &callback) {
124026e1bf5SGreg Clayton llvm::StringRef lines(linux_map);
125026e1bf5SGreg Clayton llvm::StringRef line;
126026e1bf5SGreg Clayton while (!lines.empty()) {
127026e1bf5SGreg Clayton std::tie(line, lines) = lines.split('\n');
12832541685SDavid Spickett if (!callback(ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::Maps)))
129026e1bf5SGreg Clayton break;
130026e1bf5SGreg Clayton }
131026e1bf5SGreg Clayton }
13232541685SDavid Spickett
ParseLinuxSMapRegions(llvm::StringRef linux_smap,LinuxMapCallback const & callback)13332541685SDavid Spickett void lldb_private::ParseLinuxSMapRegions(llvm::StringRef linux_smap,
13432541685SDavid Spickett LinuxMapCallback const &callback) {
13532541685SDavid Spickett // Entries in /smaps look like:
13632541685SDavid Spickett // 00400000-0048a000 r-xp 00000000 fd:03 960637
13732541685SDavid Spickett // Size: 552 kB
13832541685SDavid Spickett // Rss: 460 kB
13932541685SDavid Spickett // <...>
14032541685SDavid Spickett // VmFlags: rd ex mr mw me dw
14132541685SDavid Spickett // 00500000-0058a000 rwxp 00000000 fd:03 960637
14232541685SDavid Spickett // <...>
14332541685SDavid Spickett //
14432541685SDavid Spickett // Where the first line is identical to the /maps format
14532541685SDavid Spickett // and VmFlags is only printed for kernels >= 3.8.
14632541685SDavid Spickett
14732541685SDavid Spickett llvm::StringRef lines(linux_smap);
14832541685SDavid Spickett llvm::StringRef line;
14932541685SDavid Spickett llvm::Optional<MemoryRegionInfo> region;
15032541685SDavid Spickett
15132541685SDavid Spickett while (lines.size()) {
15232541685SDavid Spickett std::tie(line, lines) = lines.split('\n');
15332541685SDavid Spickett
15432541685SDavid Spickett // A property line looks like:
15532541685SDavid Spickett // <word>: <value>
15632541685SDavid Spickett // (no spaces on the left hand side)
15732541685SDavid Spickett // A header will have a ':' but the LHS will contain spaces
15832541685SDavid Spickett llvm::StringRef name;
15932541685SDavid Spickett llvm::StringRef value;
16032541685SDavid Spickett std::tie(name, value) = line.split(':');
16132541685SDavid Spickett
16232541685SDavid Spickett // If this line is a property line
16332541685SDavid Spickett if (!name.contains(' ')) {
16432541685SDavid Spickett if (region) {
16532541685SDavid Spickett if (name == "VmFlags") {
16632541685SDavid Spickett if (value.contains("mt"))
16732541685SDavid Spickett region->SetMemoryTagged(MemoryRegionInfo::eYes);
16832541685SDavid Spickett else
16932541685SDavid Spickett region->SetMemoryTagged(MemoryRegionInfo::eNo);
17032541685SDavid Spickett }
17132541685SDavid Spickett // Ignore anything else
17232541685SDavid Spickett } else {
17332541685SDavid Spickett // Orphaned settings line
17432541685SDavid Spickett callback(ProcMapError(
17532541685SDavid Spickett "Found a property line without a corresponding mapping "
17632541685SDavid Spickett "in /proc/{pid}/%s",
17732541685SDavid Spickett MapsKind::SMaps));
17832541685SDavid Spickett return;
17932541685SDavid Spickett }
18032541685SDavid Spickett } else {
18132541685SDavid Spickett // Must be a new region header
18232541685SDavid Spickett if (region) {
18332541685SDavid Spickett // Save current region
18432541685SDavid Spickett callback(*region);
18532541685SDavid Spickett region.reset();
18632541685SDavid Spickett }
18732541685SDavid Spickett
18832541685SDavid Spickett // Try to start a new region
18932541685SDavid Spickett llvm::Expected<MemoryRegionInfo> new_region =
19032541685SDavid Spickett ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::SMaps);
19132541685SDavid Spickett if (new_region) {
19232541685SDavid Spickett region = *new_region;
19332541685SDavid Spickett } else {
19432541685SDavid Spickett // Stop at first invalid region header
19532541685SDavid Spickett callback(new_region.takeError());
19632541685SDavid Spickett return;
19732541685SDavid Spickett }
19832541685SDavid Spickett }
19932541685SDavid Spickett }
20032541685SDavid Spickett
20132541685SDavid Spickett // Catch last region
20232541685SDavid Spickett if (region)
20332541685SDavid Spickett callback(*region);
20432541685SDavid Spickett }
205