1*061da546Spatrick //===-- LinuxProcMaps.cpp ---------------------------------------*- C++ -*-===//
2*061da546Spatrick //
3*061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5*061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*061da546Spatrick //
7*061da546Spatrick //===----------------------------------------------------------------------===//
8*061da546Spatrick 
9*061da546Spatrick #include "LinuxProcMaps.h"
10*061da546Spatrick #include "llvm/ADT/StringRef.h"
11*061da546Spatrick #include "lldb/Target/MemoryRegionInfo.h"
12*061da546Spatrick #include "lldb/Utility/Status.h"
13*061da546Spatrick #include "lldb/Utility/StringExtractor.h"
14*061da546Spatrick 
15*061da546Spatrick using namespace lldb_private;
16*061da546Spatrick 
17*061da546Spatrick static Status
18*061da546Spatrick ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line,
19*061da546Spatrick                                       MemoryRegionInfo &memory_region_info) {
20*061da546Spatrick   memory_region_info.Clear();
21*061da546Spatrick 
22*061da546Spatrick   StringExtractor line_extractor(maps_line);
23*061da546Spatrick 
24*061da546Spatrick   // Format: {address_start_hex}-{address_end_hex} perms offset  dev   inode
25*061da546Spatrick   // pathname perms: rwxp   (letter is present if set, '-' if not, final
26*061da546Spatrick   // character is p=private, s=shared).
27*061da546Spatrick 
28*061da546Spatrick   // Parse out the starting address
29*061da546Spatrick   lldb::addr_t start_address = line_extractor.GetHexMaxU64(false, 0);
30*061da546Spatrick 
31*061da546Spatrick   // Parse out hyphen separating start and end address from range.
32*061da546Spatrick   if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != '-'))
33*061da546Spatrick     return Status(
34*061da546Spatrick         "malformed /proc/{pid}/maps entry, missing dash between address range");
35*061da546Spatrick 
36*061da546Spatrick   // Parse out the ending address
37*061da546Spatrick   lldb::addr_t end_address = line_extractor.GetHexMaxU64(false, start_address);
38*061da546Spatrick 
39*061da546Spatrick   // Parse out the space after the address.
40*061da546Spatrick   if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != ' '))
41*061da546Spatrick     return Status(
42*061da546Spatrick         "malformed /proc/{pid}/maps entry, missing space after range");
43*061da546Spatrick 
44*061da546Spatrick   // Save the range.
45*061da546Spatrick   memory_region_info.GetRange().SetRangeBase(start_address);
46*061da546Spatrick   memory_region_info.GetRange().SetRangeEnd(end_address);
47*061da546Spatrick 
48*061da546Spatrick   // Any memory region in /proc/{pid}/maps is by definition mapped into the
49*061da546Spatrick   // process.
50*061da546Spatrick   memory_region_info.SetMapped(MemoryRegionInfo::OptionalBool::eYes);
51*061da546Spatrick 
52*061da546Spatrick   // Parse out each permission entry.
53*061da546Spatrick   if (line_extractor.GetBytesLeft() < 4)
54*061da546Spatrick     return Status("malformed /proc/{pid}/maps entry, missing some portion of "
55*061da546Spatrick                   "permissions");
56*061da546Spatrick 
57*061da546Spatrick   // Handle read permission.
58*061da546Spatrick   const char read_perm_char = line_extractor.GetChar();
59*061da546Spatrick   if (read_perm_char == 'r')
60*061da546Spatrick     memory_region_info.SetReadable(MemoryRegionInfo::OptionalBool::eYes);
61*061da546Spatrick   else if (read_perm_char == '-')
62*061da546Spatrick     memory_region_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
63*061da546Spatrick   else
64*061da546Spatrick     return Status("unexpected /proc/{pid}/maps read permission char");
65*061da546Spatrick 
66*061da546Spatrick   // Handle write permission.
67*061da546Spatrick   const char write_perm_char = line_extractor.GetChar();
68*061da546Spatrick   if (write_perm_char == 'w')
69*061da546Spatrick     memory_region_info.SetWritable(MemoryRegionInfo::OptionalBool::eYes);
70*061da546Spatrick   else if (write_perm_char == '-')
71*061da546Spatrick     memory_region_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
72*061da546Spatrick   else
73*061da546Spatrick     return Status("unexpected /proc/{pid}/maps write permission char");
74*061da546Spatrick 
75*061da546Spatrick   // Handle execute permission.
76*061da546Spatrick   const char exec_perm_char = line_extractor.GetChar();
77*061da546Spatrick   if (exec_perm_char == 'x')
78*061da546Spatrick     memory_region_info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes);
79*061da546Spatrick   else if (exec_perm_char == '-')
80*061da546Spatrick     memory_region_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
81*061da546Spatrick   else
82*061da546Spatrick     return Status("unexpected /proc/{pid}/maps exec permission char");
83*061da546Spatrick 
84*061da546Spatrick   line_extractor.GetChar();              // Read the private bit
85*061da546Spatrick   line_extractor.SkipSpaces();           // Skip the separator
86*061da546Spatrick   line_extractor.GetHexMaxU64(false, 0); // Read the offset
87*061da546Spatrick   line_extractor.GetHexMaxU64(false, 0); // Read the major device number
88*061da546Spatrick   line_extractor.GetChar();              // Read the device id separator
89*061da546Spatrick   line_extractor.GetHexMaxU64(false, 0); // Read the major device number
90*061da546Spatrick   line_extractor.SkipSpaces();           // Skip the separator
91*061da546Spatrick   line_extractor.GetU64(0, 10);          // Read the inode number
92*061da546Spatrick 
93*061da546Spatrick   line_extractor.SkipSpaces();
94*061da546Spatrick   const char *name = line_extractor.Peek();
95*061da546Spatrick   if (name)
96*061da546Spatrick     memory_region_info.SetName(name);
97*061da546Spatrick 
98*061da546Spatrick   return Status();
99*061da546Spatrick }
100*061da546Spatrick 
101*061da546Spatrick void lldb_private::ParseLinuxMapRegions(llvm::StringRef linux_map,
102*061da546Spatrick                                         LinuxMapCallback const &callback) {
103*061da546Spatrick   llvm::StringRef lines(linux_map);
104*061da546Spatrick   llvm::StringRef line;
105*061da546Spatrick   while (!lines.empty()) {
106*061da546Spatrick     std::tie(line, lines) = lines.split('\n');
107*061da546Spatrick     MemoryRegionInfo region;
108*061da546Spatrick     Status error = ParseMemoryRegionInfoFromProcMapsLine(line, region);
109*061da546Spatrick     if (!callback(region, error))
110*061da546Spatrick       break;
111*061da546Spatrick   }
112*061da546Spatrick }
113