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