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 line_extractor.GetChar(); // Read the private bit 98 line_extractor.SkipSpaces(); // Skip the separator 99 line_extractor.GetHexMaxU64(false, 0); // Read the offset 100 line_extractor.GetHexMaxU64(false, 0); // Read the major device number 101 line_extractor.GetChar(); // Read the device id separator 102 line_extractor.GetHexMaxU64(false, 0); // Read the major device number 103 line_extractor.SkipSpaces(); // Skip the separator 104 line_extractor.GetU64(0, 10); // Read the inode number 105 106 line_extractor.SkipSpaces(); 107 const char *name = line_extractor.Peek(); 108 if (name) 109 region.SetName(name); 110 111 return region; 112 } 113 114 void lldb_private::ParseLinuxMapRegions(llvm::StringRef linux_map, 115 LinuxMapCallback const &callback) { 116 llvm::StringRef lines(linux_map); 117 llvm::StringRef line; 118 while (!lines.empty()) { 119 std::tie(line, lines) = lines.split('\n'); 120 if (!callback(ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::Maps))) 121 break; 122 } 123 } 124 125 void lldb_private::ParseLinuxSMapRegions(llvm::StringRef linux_smap, 126 LinuxMapCallback const &callback) { 127 // Entries in /smaps look like: 128 // 00400000-0048a000 r-xp 00000000 fd:03 960637 129 // Size: 552 kB 130 // Rss: 460 kB 131 // <...> 132 // VmFlags: rd ex mr mw me dw 133 // 00500000-0058a000 rwxp 00000000 fd:03 960637 134 // <...> 135 // 136 // Where the first line is identical to the /maps format 137 // and VmFlags is only printed for kernels >= 3.8. 138 139 llvm::StringRef lines(linux_smap); 140 llvm::StringRef line; 141 llvm::Optional<MemoryRegionInfo> region; 142 143 while (lines.size()) { 144 std::tie(line, lines) = lines.split('\n'); 145 146 // A property line looks like: 147 // <word>: <value> 148 // (no spaces on the left hand side) 149 // A header will have a ':' but the LHS will contain spaces 150 llvm::StringRef name; 151 llvm::StringRef value; 152 std::tie(name, value) = line.split(':'); 153 154 // If this line is a property line 155 if (!name.contains(' ')) { 156 if (region) { 157 if (name == "VmFlags") { 158 if (value.contains("mt")) 159 region->SetMemoryTagged(MemoryRegionInfo::eYes); 160 else 161 region->SetMemoryTagged(MemoryRegionInfo::eNo); 162 } 163 // Ignore anything else 164 } else { 165 // Orphaned settings line 166 callback(ProcMapError( 167 "Found a property line without a corresponding mapping " 168 "in /proc/{pid}/%s", 169 MapsKind::SMaps)); 170 return; 171 } 172 } else { 173 // Must be a new region header 174 if (region) { 175 // Save current region 176 callback(*region); 177 region.reset(); 178 } 179 180 // Try to start a new region 181 llvm::Expected<MemoryRegionInfo> new_region = 182 ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::SMaps); 183 if (new_region) { 184 region = *new_region; 185 } else { 186 // Stop at first invalid region header 187 callback(new_region.takeError()); 188 return; 189 } 190 } 191 } 192 193 // Catch last region 194 if (region) 195 callback(*region); 196 } 197