brintos

brintos / llvm-project-archived public Read only

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