1026e1bf5SGreg Clayton //===-- LinuxProcMaps.cpp ---------------------------------------*- C++ -*-===//
2026e1bf5SGreg Clayton //
3*2946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*2946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
5*2946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6026e1bf5SGreg Clayton //
7026e1bf5SGreg Clayton //===----------------------------------------------------------------------===//
8026e1bf5SGreg Clayton 
9026e1bf5SGreg Clayton #include "LinuxProcMaps.h"
10026e1bf5SGreg Clayton #include "llvm/ADT/StringRef.h"
11026e1bf5SGreg Clayton #include "lldb/Target/MemoryRegionInfo.h"
12026e1bf5SGreg Clayton #include "lldb/Utility/Status.h"
13026e1bf5SGreg Clayton #include "lldb/Utility/StringExtractor.h"
14026e1bf5SGreg Clayton 
15026e1bf5SGreg Clayton using namespace lldb_private;
16026e1bf5SGreg Clayton 
17026e1bf5SGreg Clayton static Status
18026e1bf5SGreg Clayton ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line,
19026e1bf5SGreg Clayton                                       MemoryRegionInfo &memory_region_info) {
20026e1bf5SGreg Clayton   memory_region_info.Clear();
21026e1bf5SGreg Clayton 
22026e1bf5SGreg Clayton   StringExtractor line_extractor(maps_line);
23026e1bf5SGreg Clayton 
24026e1bf5SGreg Clayton   // Format: {address_start_hex}-{address_end_hex} perms offset  dev   inode
25026e1bf5SGreg Clayton   // pathname perms: rwxp   (letter is present if set, '-' if not, final
26026e1bf5SGreg Clayton   // character is p=private, s=shared).
27026e1bf5SGreg Clayton 
28026e1bf5SGreg Clayton   // Parse out the starting address
29026e1bf5SGreg Clayton   lldb::addr_t start_address = line_extractor.GetHexMaxU64(false, 0);
30026e1bf5SGreg Clayton 
31026e1bf5SGreg Clayton   // Parse out hyphen separating start and end address from range.
32026e1bf5SGreg Clayton   if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != '-'))
33026e1bf5SGreg Clayton     return Status(
34026e1bf5SGreg Clayton         "malformed /proc/{pid}/maps entry, missing dash between address range");
35026e1bf5SGreg Clayton 
36026e1bf5SGreg Clayton   // Parse out the ending address
37026e1bf5SGreg Clayton   lldb::addr_t end_address = line_extractor.GetHexMaxU64(false, start_address);
38026e1bf5SGreg Clayton 
39026e1bf5SGreg Clayton   // Parse out the space after the address.
40026e1bf5SGreg Clayton   if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != ' '))
41026e1bf5SGreg Clayton     return Status(
42026e1bf5SGreg Clayton         "malformed /proc/{pid}/maps entry, missing space after range");
43026e1bf5SGreg Clayton 
44026e1bf5SGreg Clayton   // Save the range.
45026e1bf5SGreg Clayton   memory_region_info.GetRange().SetRangeBase(start_address);
46026e1bf5SGreg Clayton   memory_region_info.GetRange().SetRangeEnd(end_address);
47026e1bf5SGreg Clayton 
48026e1bf5SGreg Clayton   // Any memory region in /proc/{pid}/maps is by definition mapped into the
49026e1bf5SGreg Clayton   // process.
50026e1bf5SGreg Clayton   memory_region_info.SetMapped(MemoryRegionInfo::OptionalBool::eYes);
51026e1bf5SGreg Clayton 
52026e1bf5SGreg Clayton   // Parse out each permission entry.
53026e1bf5SGreg Clayton   if (line_extractor.GetBytesLeft() < 4)
54026e1bf5SGreg Clayton     return Status("malformed /proc/{pid}/maps entry, missing some portion of "
55026e1bf5SGreg Clayton                   "permissions");
56026e1bf5SGreg Clayton 
57026e1bf5SGreg Clayton   // Handle read permission.
58026e1bf5SGreg Clayton   const char read_perm_char = line_extractor.GetChar();
59026e1bf5SGreg Clayton   if (read_perm_char == 'r')
60026e1bf5SGreg Clayton     memory_region_info.SetReadable(MemoryRegionInfo::OptionalBool::eYes);
61026e1bf5SGreg Clayton   else if (read_perm_char == '-')
62026e1bf5SGreg Clayton     memory_region_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
63026e1bf5SGreg Clayton   else
64026e1bf5SGreg Clayton     return Status("unexpected /proc/{pid}/maps read permission char");
65026e1bf5SGreg Clayton 
66026e1bf5SGreg Clayton   // Handle write permission.
67026e1bf5SGreg Clayton   const char write_perm_char = line_extractor.GetChar();
68026e1bf5SGreg Clayton   if (write_perm_char == 'w')
69026e1bf5SGreg Clayton     memory_region_info.SetWritable(MemoryRegionInfo::OptionalBool::eYes);
70026e1bf5SGreg Clayton   else if (write_perm_char == '-')
71026e1bf5SGreg Clayton     memory_region_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
72026e1bf5SGreg Clayton   else
73026e1bf5SGreg Clayton     return Status("unexpected /proc/{pid}/maps write permission char");
74026e1bf5SGreg Clayton 
75026e1bf5SGreg Clayton   // Handle execute permission.
76026e1bf5SGreg Clayton   const char exec_perm_char = line_extractor.GetChar();
77026e1bf5SGreg Clayton   if (exec_perm_char == 'x')
78026e1bf5SGreg Clayton     memory_region_info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes);
79026e1bf5SGreg Clayton   else if (exec_perm_char == '-')
80026e1bf5SGreg Clayton     memory_region_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
81026e1bf5SGreg Clayton   else
82026e1bf5SGreg Clayton     return Status("unexpected /proc/{pid}/maps exec permission char");
83026e1bf5SGreg Clayton 
84026e1bf5SGreg Clayton   line_extractor.GetChar();              // Read the private bit
85026e1bf5SGreg Clayton   line_extractor.SkipSpaces();           // Skip the separator
86026e1bf5SGreg Clayton   line_extractor.GetHexMaxU64(false, 0); // Read the offset
87026e1bf5SGreg Clayton   line_extractor.GetHexMaxU64(false, 0); // Read the major device number
88026e1bf5SGreg Clayton   line_extractor.GetChar();              // Read the device id separator
89026e1bf5SGreg Clayton   line_extractor.GetHexMaxU64(false, 0); // Read the major device number
90026e1bf5SGreg Clayton   line_extractor.SkipSpaces();           // Skip the separator
91026e1bf5SGreg Clayton   line_extractor.GetU64(0, 10);          // Read the inode number
92026e1bf5SGreg Clayton 
93026e1bf5SGreg Clayton   line_extractor.SkipSpaces();
94026e1bf5SGreg Clayton   const char *name = line_extractor.Peek();
95026e1bf5SGreg Clayton   if (name)
96026e1bf5SGreg Clayton     memory_region_info.SetName(name);
97026e1bf5SGreg Clayton 
98026e1bf5SGreg Clayton   return Status();
99026e1bf5SGreg Clayton }
100026e1bf5SGreg Clayton 
101026e1bf5SGreg Clayton void lldb_private::ParseLinuxMapRegions(llvm::StringRef linux_map,
102026e1bf5SGreg Clayton                                         LinuxMapCallback const &callback) {
103026e1bf5SGreg Clayton   llvm::StringRef lines(linux_map);
104026e1bf5SGreg Clayton   llvm::StringRef line;
105026e1bf5SGreg Clayton   while (!lines.empty()) {
106026e1bf5SGreg Clayton     std::tie(line, lines) = lines.split('\n');
107026e1bf5SGreg Clayton     MemoryRegionInfo region;
108026e1bf5SGreg Clayton     Status error = ParseMemoryRegionInfoFromProcMapsLine(line, region);
109026e1bf5SGreg Clayton     if (!callback(region, error))
110026e1bf5SGreg Clayton       break;
111026e1bf5SGreg Clayton   }
112026e1bf5SGreg Clayton }
113