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" 13*32541685SDavid Spickett #include "llvm/ADT/StringRef.h" 14026e1bf5SGreg Clayton 15026e1bf5SGreg Clayton using namespace lldb_private; 16026e1bf5SGreg Clayton 17*32541685SDavid Spickett enum class MapsKind { Maps, SMaps }; 18026e1bf5SGreg Clayton 19*32541685SDavid Spickett static llvm::Expected<MemoryRegionInfo> ProcMapError(const char *msg, 20*32541685SDavid Spickett MapsKind kind) { 21*32541685SDavid Spickett return llvm::createStringError(llvm::inconvertibleErrorCode(), msg, 22*32541685SDavid Spickett kind == MapsKind::Maps ? "maps" : "smaps"); 23*32541685SDavid Spickett } 24*32541685SDavid Spickett 25*32541685SDavid Spickett static llvm::Expected<MemoryRegionInfo> 26*32541685SDavid Spickett ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line, 27*32541685SDavid Spickett MapsKind maps_kind) { 28*32541685SDavid 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() != '-')) 40*32541685SDavid Spickett return ProcMapError( 41*32541685SDavid Spickett "malformed /proc/{pid}/%s entry, missing dash between address range", 42*32541685SDavid 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() != ' ')) 49*32541685SDavid Spickett return ProcMapError( 50*32541685SDavid Spickett "malformed /proc/{pid}/%s entry, missing space after range", maps_kind); 51026e1bf5SGreg Clayton 52026e1bf5SGreg Clayton // Save the range. 53*32541685SDavid Spickett region.GetRange().SetRangeBase(start_address); 54*32541685SDavid Spickett region.GetRange().SetRangeEnd(end_address); 55026e1bf5SGreg Clayton 56*32541685SDavid Spickett // Any memory region in /proc/{pid}/(maps|smaps) is by definition mapped 57*32541685SDavid Spickett // into the process. 58*32541685SDavid Spickett region.SetMapped(MemoryRegionInfo::OptionalBool::eYes); 59026e1bf5SGreg Clayton 60026e1bf5SGreg Clayton // Parse out each permission entry. 61026e1bf5SGreg Clayton if (line_extractor.GetBytesLeft() < 4) 62*32541685SDavid Spickett return ProcMapError( 63*32541685SDavid Spickett "malformed /proc/{pid}/%s entry, missing some portion of " 64*32541685SDavid Spickett "permissions", 65*32541685SDavid 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') 70*32541685SDavid Spickett region.SetReadable(MemoryRegionInfo::OptionalBool::eYes); 71026e1bf5SGreg Clayton else if (read_perm_char == '-') 72*32541685SDavid Spickett region.SetReadable(MemoryRegionInfo::OptionalBool::eNo); 73026e1bf5SGreg Clayton else 74*32541685SDavid Spickett return ProcMapError("unexpected /proc/{pid}/%s read permission char", 75*32541685SDavid 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') 80*32541685SDavid Spickett region.SetWritable(MemoryRegionInfo::OptionalBool::eYes); 81026e1bf5SGreg Clayton else if (write_perm_char == '-') 82*32541685SDavid Spickett region.SetWritable(MemoryRegionInfo::OptionalBool::eNo); 83026e1bf5SGreg Clayton else 84*32541685SDavid Spickett return ProcMapError("unexpected /proc/{pid}/%s write permission char", 85*32541685SDavid 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') 90*32541685SDavid Spickett region.SetExecutable(MemoryRegionInfo::OptionalBool::eYes); 91026e1bf5SGreg Clayton else if (exec_perm_char == '-') 92*32541685SDavid Spickett region.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); 93026e1bf5SGreg Clayton else 94*32541685SDavid Spickett return ProcMapError("unexpected /proc/{pid}/%s exec permission char", 95*32541685SDavid Spickett maps_kind); 96026e1bf5SGreg Clayton 97026e1bf5SGreg Clayton line_extractor.GetChar(); // Read the private bit 98026e1bf5SGreg Clayton line_extractor.SkipSpaces(); // Skip the separator 99026e1bf5SGreg Clayton line_extractor.GetHexMaxU64(false, 0); // Read the offset 100026e1bf5SGreg Clayton line_extractor.GetHexMaxU64(false, 0); // Read the major device number 101026e1bf5SGreg Clayton line_extractor.GetChar(); // Read the device id separator 102026e1bf5SGreg Clayton line_extractor.GetHexMaxU64(false, 0); // Read the major device number 103026e1bf5SGreg Clayton line_extractor.SkipSpaces(); // Skip the separator 104026e1bf5SGreg Clayton line_extractor.GetU64(0, 10); // Read the inode number 105026e1bf5SGreg Clayton 106026e1bf5SGreg Clayton line_extractor.SkipSpaces(); 107026e1bf5SGreg Clayton const char *name = line_extractor.Peek(); 108026e1bf5SGreg Clayton if (name) 109*32541685SDavid Spickett region.SetName(name); 110026e1bf5SGreg Clayton 111*32541685SDavid Spickett return region; 112026e1bf5SGreg Clayton } 113026e1bf5SGreg Clayton 114026e1bf5SGreg Clayton void lldb_private::ParseLinuxMapRegions(llvm::StringRef linux_map, 115026e1bf5SGreg Clayton LinuxMapCallback const &callback) { 116026e1bf5SGreg Clayton llvm::StringRef lines(linux_map); 117026e1bf5SGreg Clayton llvm::StringRef line; 118026e1bf5SGreg Clayton while (!lines.empty()) { 119026e1bf5SGreg Clayton std::tie(line, lines) = lines.split('\n'); 120*32541685SDavid Spickett if (!callback(ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::Maps))) 121026e1bf5SGreg Clayton break; 122026e1bf5SGreg Clayton } 123026e1bf5SGreg Clayton } 124*32541685SDavid Spickett 125*32541685SDavid Spickett void lldb_private::ParseLinuxSMapRegions(llvm::StringRef linux_smap, 126*32541685SDavid Spickett LinuxMapCallback const &callback) { 127*32541685SDavid Spickett // Entries in /smaps look like: 128*32541685SDavid Spickett // 00400000-0048a000 r-xp 00000000 fd:03 960637 129*32541685SDavid Spickett // Size: 552 kB 130*32541685SDavid Spickett // Rss: 460 kB 131*32541685SDavid Spickett // <...> 132*32541685SDavid Spickett // VmFlags: rd ex mr mw me dw 133*32541685SDavid Spickett // 00500000-0058a000 rwxp 00000000 fd:03 960637 134*32541685SDavid Spickett // <...> 135*32541685SDavid Spickett // 136*32541685SDavid Spickett // Where the first line is identical to the /maps format 137*32541685SDavid Spickett // and VmFlags is only printed for kernels >= 3.8. 138*32541685SDavid Spickett 139*32541685SDavid Spickett llvm::StringRef lines(linux_smap); 140*32541685SDavid Spickett llvm::StringRef line; 141*32541685SDavid Spickett llvm::Optional<MemoryRegionInfo> region; 142*32541685SDavid Spickett 143*32541685SDavid Spickett while (lines.size()) { 144*32541685SDavid Spickett std::tie(line, lines) = lines.split('\n'); 145*32541685SDavid Spickett 146*32541685SDavid Spickett // A property line looks like: 147*32541685SDavid Spickett // <word>: <value> 148*32541685SDavid Spickett // (no spaces on the left hand side) 149*32541685SDavid Spickett // A header will have a ':' but the LHS will contain spaces 150*32541685SDavid Spickett llvm::StringRef name; 151*32541685SDavid Spickett llvm::StringRef value; 152*32541685SDavid Spickett std::tie(name, value) = line.split(':'); 153*32541685SDavid Spickett 154*32541685SDavid Spickett // If this line is a property line 155*32541685SDavid Spickett if (!name.contains(' ')) { 156*32541685SDavid Spickett if (region) { 157*32541685SDavid Spickett if (name == "VmFlags") { 158*32541685SDavid Spickett if (value.contains("mt")) 159*32541685SDavid Spickett region->SetMemoryTagged(MemoryRegionInfo::eYes); 160*32541685SDavid Spickett else 161*32541685SDavid Spickett region->SetMemoryTagged(MemoryRegionInfo::eNo); 162*32541685SDavid Spickett } 163*32541685SDavid Spickett // Ignore anything else 164*32541685SDavid Spickett } else { 165*32541685SDavid Spickett // Orphaned settings line 166*32541685SDavid Spickett callback(ProcMapError( 167*32541685SDavid Spickett "Found a property line without a corresponding mapping " 168*32541685SDavid Spickett "in /proc/{pid}/%s", 169*32541685SDavid Spickett MapsKind::SMaps)); 170*32541685SDavid Spickett return; 171*32541685SDavid Spickett } 172*32541685SDavid Spickett } else { 173*32541685SDavid Spickett // Must be a new region header 174*32541685SDavid Spickett if (region) { 175*32541685SDavid Spickett // Save current region 176*32541685SDavid Spickett callback(*region); 177*32541685SDavid Spickett region.reset(); 178*32541685SDavid Spickett } 179*32541685SDavid Spickett 180*32541685SDavid Spickett // Try to start a new region 181*32541685SDavid Spickett llvm::Expected<MemoryRegionInfo> new_region = 182*32541685SDavid Spickett ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::SMaps); 183*32541685SDavid Spickett if (new_region) { 184*32541685SDavid Spickett region = *new_region; 185*32541685SDavid Spickett } else { 186*32541685SDavid Spickett // Stop at first invalid region header 187*32541685SDavid Spickett callback(new_region.takeError()); 188*32541685SDavid Spickett return; 189*32541685SDavid Spickett } 190*32541685SDavid Spickett } 191*32541685SDavid Spickett } 192*32541685SDavid Spickett 193*32541685SDavid Spickett // Catch last region 194*32541685SDavid Spickett if (region) 195*32541685SDavid Spickett callback(*region); 196*32541685SDavid Spickett } 197