brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.5 KiB · f40fba6 Raw
535 lines · cpp
1//===-- sanitizer_procmaps_mac.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// Information about the process mappings (Mac-specific parts).10//===----------------------------------------------------------------------===//11 12#include "sanitizer_platform.h"13#if SANITIZER_APPLE14#include "sanitizer_common.h"15#include "sanitizer_placement_new.h"16#include "sanitizer_procmaps.h"17 18#include <mach-o/dyld.h>19#include <mach-o/loader.h>20#include <mach/mach.h>21 22// These are not available in older macOS SDKs.23#  ifndef CPU_SUBTYPE_X86_64_H24#    define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8) /* Haswell */25#  endif26#  ifndef CPU_SUBTYPE_ARM_V7S27#    define CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t)11) /* Swift */28#  endif29#  ifndef CPU_SUBTYPE_ARM_V7K30#    define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t)12)31#  endif32#  ifndef CPU_TYPE_ARM6433#    define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64)34#  endif35#  ifndef CPU_SUBTYPE_ARM64E36#    define CPU_SUBTYPE_ARM64E ((cpu_subtype_t)2)37#  endif38 39namespace __sanitizer {40 41// Contains information used to iterate through sections.42struct MemoryMappedSegmentData {43  char name[kMaxSegName];44  uptr nsects;45  const char *current_load_cmd_addr;46  u32 lc_type;47  uptr base_virt_addr;48};49 50template <typename Section>51static void NextSectionLoad(LoadedModule *module, MemoryMappedSegmentData *data,52                            bool isWritable) {53  const Section *sc = (const Section *)data->current_load_cmd_addr;54  data->current_load_cmd_addr += sizeof(Section);55 56  uptr sec_start = sc->addr + data->base_virt_addr;57  uptr sec_end = sec_start + sc->size;58  module->addAddressRange(sec_start, sec_end, /*executable=*/false, isWritable,59                          sc->sectname);60}61 62static bool VerifyMemoryMapping(MemoryMappingLayout* mapping) {63  InternalMmapVector<LoadedModule> modules;64  modules.reserve(128);  // matches DumpProcessMap65  mapping->DumpListOfModules(&modules);66 67  InternalMmapVector<LoadedModule::AddressRange> segments;68  for (uptr i = 0; i < modules.size(); ++i) {69    for (auto& range : modules[i].ranges()) {70      segments.push_back(range);71    }72  }73 74  // Verify that none of the segments overlap:75  // 1. Sort the segments by the start address76  // 2. Check that every segment starts after the previous one ends.77  Sort(segments.data(), segments.size(),78       [](LoadedModule::AddressRange& a, LoadedModule::AddressRange& b) {79         return a.beg < b.beg;80       });81 82  // To avoid spam, we only print the report message once-per-process.83  static bool invalid_module_map_reported = false;84  bool well_formed = true;85 86  for (size_t i = 1; i < segments.size(); i++) {87    uptr cur_start = segments[i].beg;88    uptr prev_end = segments[i - 1].end;89    if (cur_start < prev_end) {90      well_formed = false;91      VReport(2, "Overlapping mappings: %s start = %p, %s end = %p\n",92              segments[i].name, (void*)cur_start, segments[i - 1].name,93              (void*)prev_end);94      if (!invalid_module_map_reported) {95        Report(96            "WARN: Invalid dyld module map detected. This is most likely a bug "97            "in the sanitizer.\n");98        Report("WARN: Backtraces may be unreliable.\n");99        invalid_module_map_reported = true;100      }101    }102  }103 104  for (auto& m : modules) m.clear();105 106  mapping->Reset();107  return well_formed;108}109 110void MemoryMappedSegment::AddAddressRanges(LoadedModule *module) {111  // Don't iterate over sections when the caller hasn't set up the112  // data pointer, when there are no sections, or when the segment113  // is executable. Avoid iterating over executable sections because114  // it will confuse libignore, and because the extra granularity115  // of information is not needed by any sanitizers.116  if (!data_ || !data_->nsects || IsExecutable()) {117    module->addAddressRange(start, end, IsExecutable(), IsWritable(),118                            data_ ? data_->name : nullptr);119    return;120  }121 122  do {123    if (data_->lc_type == LC_SEGMENT) {124      NextSectionLoad<struct section>(module, data_, IsWritable());125#ifdef MH_MAGIC_64126    } else if (data_->lc_type == LC_SEGMENT_64) {127      NextSectionLoad<struct section_64>(module, data_, IsWritable());128#endif129    }130  } while (--data_->nsects);131}132 133MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {134  Reset();135  VerifyMemoryMapping(this);136}137 138MemoryMappingLayout::~MemoryMappingLayout() {139}140 141bool MemoryMappingLayout::Error() const {142  return false;143}144 145// More information about Mach-O headers can be found in mach-o/loader.h146// Each Mach-O image has a header (mach_header or mach_header_64) starting with147// a magic number, and a list of linker load commands directly following the148// header.149// A load command is at least two 32-bit words: the command type and the150// command size in bytes. We're interested only in segment load commands151// (LC_SEGMENT and LC_SEGMENT_64), which tell that a part of the file is mapped152// into the task's address space.153// The |vmaddr|, |vmsize| and |fileoff| fields of segment_command or154// segment_command_64 correspond to the memory address, memory size and the155// file offset of the current memory segment.156// Because these fields are taken from the images as is, one needs to add157// _dyld_get_image_vmaddr_slide() to get the actual addresses at runtime.158 159void MemoryMappingLayout::Reset() {160  // Count down from the top.161  // TODO(glider): as per man 3 dyld, iterating over the headers with162  // _dyld_image_count is thread-unsafe. We need to register callbacks for163  // adding and removing images which will invalidate the MemoryMappingLayout164  // state.165  data_.current_image = _dyld_image_count();166  data_.current_load_cmd_count = -1;167  data_.current_load_cmd_addr = 0;168  data_.current_magic = 0;169  data_.current_filetype = 0;170  data_.current_arch = kModuleArchUnknown;171  internal_memset(data_.current_uuid, 0, kModuleUUIDSize);172}173 174// The dyld load address should be unchanged throughout process execution,175// and it is expensive to compute once many libraries have been loaded,176// so cache it here and do not reset.177static mach_header *dyld_hdr = 0;178static const char kDyldPath[] = "/usr/lib/dyld";179static const int kDyldImageIdx = -1;180 181// static182void MemoryMappingLayout::CacheMemoryMappings() {183  // No-op on Mac for now.184}185 186void MemoryMappingLayout::LoadFromCache() {187  // No-op on Mac for now.188}189 190static bool IsDyldHdr(const mach_header *hdr) {191  return (hdr->magic == MH_MAGIC || hdr->magic == MH_MAGIC_64) &&192         hdr->filetype == MH_DYLINKER;193}194 195// _dyld_get_image_header() and related APIs don't report dyld itself.196// We work around this by manually recursing through the memory map197// until we hit a Mach header matching dyld instead. These recurse198// calls are expensive, but the first memory map generation occurs199// early in the process, when dyld is one of the only images loaded,200// so it will be hit after only a few iterations.  These assumptions don't hold201// on macOS 13+ anymore (dyld itself has moved into the shared cache).202static mach_header *GetDyldImageHeaderViaVMRegion() {203  vm_address_t address = 0;204 205  while (true) {206    vm_size_t size = 0;207    unsigned depth = 1;208    struct vm_region_submap_info_64 info;209    mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;210    kern_return_t err =211        vm_region_recurse_64(mach_task_self(), &address, &size, &depth,212                             (vm_region_info_t)&info, &count);213    if (err != KERN_SUCCESS) return nullptr;214 215    if (size >= sizeof(mach_header) && info.protection & kProtectionRead) {216      mach_header *hdr = (mach_header *)address;217      if (IsDyldHdr(hdr)) {218        return hdr;219      }220    }221    address += size;222  }223}224 225extern "C" {226struct dyld_shared_cache_dylib_text_info {227  uint64_t version;  // current version 2228  // following fields all exist in version 1229  uint64_t loadAddressUnslid;230  uint64_t textSegmentSize;231  uuid_t dylibUuid;232  const char *path;  // pointer invalid at end of iterations233  // following fields all exist in version 2234  uint64_t textSegmentOffset;  // offset from start of cache235};236typedef struct dyld_shared_cache_dylib_text_info237    dyld_shared_cache_dylib_text_info;238 239extern bool _dyld_get_shared_cache_uuid(uuid_t uuid);240extern const void *_dyld_get_shared_cache_range(size_t *length);241extern intptr_t _dyld_get_image_slide(const struct mach_header* mh);242extern int dyld_shared_cache_iterate_text(243    const uuid_t cacheUuid,244    void (^callback)(const dyld_shared_cache_dylib_text_info *info));245}  // extern "C"246 247static mach_header *GetDyldImageHeaderViaSharedCache() {248  uuid_t uuid;249  bool hasCache = _dyld_get_shared_cache_uuid(uuid);250  if (!hasCache)251    return nullptr;252 253  size_t cacheLength;254  __block uptr cacheStart = (uptr)_dyld_get_shared_cache_range(&cacheLength);255  CHECK(cacheStart && cacheLength);256 257  __block mach_header *dyldHdr = nullptr;258  int res = dyld_shared_cache_iterate_text(259      uuid, ^(const dyld_shared_cache_dylib_text_info *info) {260        CHECK_GE(info->version, 2);261        mach_header *hdr =262            (mach_header *)(cacheStart + info->textSegmentOffset);263        if (IsDyldHdr(hdr))264          dyldHdr = hdr;265      });266  CHECK_EQ(res, 0);267 268  return dyldHdr;269}270 271const mach_header *get_dyld_hdr() {272  if (!dyld_hdr) {273    // On macOS 13+, dyld itself has moved into the shared cache.  Looking it up274    // via vm_region_recurse_64() causes spins/hangs/crashes.275    if (GetMacosAlignedVersion() >= MacosVersion(13, 0)) {276      dyld_hdr = GetDyldImageHeaderViaSharedCache();277      if (!dyld_hdr) {278        VReport(1,279                "Failed to lookup the dyld image header in the shared cache on "280                "macOS 13+ (or no shared cache in use).  Falling back to "281                "lookup via vm_region_recurse_64().\n");282        dyld_hdr = GetDyldImageHeaderViaVMRegion();283      }284    } else {285      dyld_hdr = GetDyldImageHeaderViaVMRegion();286    }287    CHECK(dyld_hdr);288  }289 290  return dyld_hdr;291}292 293// Next and NextSegmentLoad were inspired by base/sysinfo.cc in294// Google Perftools, https://github.com/gperftools/gperftools.295 296// NextSegmentLoad scans the current image for the next segment load command297// and returns the start and end addresses and file offset of the corresponding298// segment.299// Note that the segment addresses are not necessarily sorted.300template <u32 kLCSegment, typename SegmentCommand>301static bool NextSegmentLoad(MemoryMappedSegment *segment,302                            MemoryMappedSegmentData *seg_data,303                            MemoryMappingLayoutData *layout_data) {304  const char *lc = layout_data->current_load_cmd_addr;305 306  layout_data->current_load_cmd_addr += ((const load_command *)lc)->cmdsize;307  layout_data->current_load_cmd_count--;308  if (((const load_command *)lc)->cmd == kLCSegment) {309    const SegmentCommand* sc = (const SegmentCommand *)lc;310    if (internal_strcmp(sc->segname, "__LINKEDIT") == 0) {311      // The LINKEDIT sections are for internal linker use, and may alias312      // with the LINKEDIT section for other modules. (If we included them,313      // our memory map would contain overlappping sections.)314      return false;315    }316 317    uptr base_virt_addr;318    if (layout_data->current_image == kDyldImageIdx)319      base_virt_addr = (uptr)_dyld_get_image_slide(get_dyld_hdr());320    else321      base_virt_addr =322          (uptr)_dyld_get_image_vmaddr_slide(layout_data->current_image);323 324    segment->start = sc->vmaddr + base_virt_addr;325    segment->end = segment->start + sc->vmsize;326    // Most callers don't need section information, so only fill this struct327    // when required.328    if (seg_data) {329      seg_data->nsects = sc->nsects;330      seg_data->current_load_cmd_addr =331          (const char *)lc + sizeof(SegmentCommand);332      seg_data->lc_type = kLCSegment;333      seg_data->base_virt_addr = base_virt_addr;334      internal_strncpy(seg_data->name, sc->segname,335                       ARRAY_SIZE(seg_data->name));336      seg_data->name[ARRAY_SIZE(seg_data->name) - 1] = 0;337    }338 339    // Return the initial protection.340    segment->protection = sc->initprot;341    segment->offset = (layout_data->current_filetype ==342                       /*MH_EXECUTE*/ 0x2)343                          ? sc->vmaddr344                          : sc->fileoff;345    if (segment->filename) {346      const char *src = (layout_data->current_image == kDyldImageIdx)347                            ? kDyldPath348                            : _dyld_get_image_name(layout_data->current_image);349      internal_strncpy(segment->filename, src, segment->filename_size);350      segment->filename[segment->filename_size - 1] = 0;351    }352    segment->arch = layout_data->current_arch;353    internal_memcpy(segment->uuid, layout_data->current_uuid, kModuleUUIDSize);354    return true;355  }356  return false;357}358 359ModuleArch ModuleArchFromCpuType(cpu_type_t cputype, cpu_subtype_t cpusubtype) {360  cpusubtype = cpusubtype & ~CPU_SUBTYPE_MASK;361  switch (cputype) {362    case CPU_TYPE_I386:363      return kModuleArchI386;364    case CPU_TYPE_X86_64:365      if (cpusubtype == CPU_SUBTYPE_X86_64_ALL)366        return kModuleArchX86_64;367      if (cpusubtype == CPU_SUBTYPE_X86_64_H)368        return kModuleArchX86_64H;369      CHECK(0 && "Invalid subtype of x86_64");370      return kModuleArchUnknown;371    case CPU_TYPE_ARM:372      if (cpusubtype == CPU_SUBTYPE_ARM_V6)373        return kModuleArchARMV6;374      if (cpusubtype == CPU_SUBTYPE_ARM_V7)375        return kModuleArchARMV7;376      if (cpusubtype == CPU_SUBTYPE_ARM_V7S)377        return kModuleArchARMV7S;378      if (cpusubtype == CPU_SUBTYPE_ARM_V7K)379        return kModuleArchARMV7K;380      CHECK(0 && "Invalid subtype of ARM");381      return kModuleArchUnknown;382    case CPU_TYPE_ARM64:383      if (cpusubtype == CPU_SUBTYPE_ARM64E)384        return kModuleArchARM64E;385      return kModuleArchARM64;386    default:387      CHECK(0 && "Invalid CPU type");388      return kModuleArchUnknown;389  }390}391 392static const load_command *NextCommand(const load_command *lc) {393  return (const load_command *)((const char *)lc + lc->cmdsize);394}395 396#  ifdef MH_MAGIC_64397static constexpr size_t header_size = sizeof(mach_header_64);398#  else399static constexpr size_t header_size = sizeof(mach_header);400#  endif401 402static void FindUUID(const load_command *first_lc, const mach_header *hdr,403                     u8 *uuid_output) {404  uint32_t curcmd = 0;405  for (const load_command *lc = first_lc; curcmd < hdr->ncmds;406       curcmd++, lc = NextCommand(lc)) {407    CHECK_LT((const char *)lc,408             (const char *)hdr + header_size + hdr->sizeofcmds);409 410    if (lc->cmd != LC_UUID)411      continue;412 413    const uuid_command *uuid_lc = (const uuid_command *)lc;414    const uint8_t *uuid = &uuid_lc->uuid[0];415    internal_memcpy(uuid_output, uuid, kModuleUUIDSize);416    return;417  }418}419 420static bool IsModuleInstrumented(const load_command *first_lc,421                                 const mach_header *hdr) {422  uint32_t curcmd = 0;423  for (const load_command *lc = first_lc; curcmd < hdr->ncmds;424       curcmd++, lc = NextCommand(lc)) {425    CHECK_LT((const char *)lc,426             (const char *)hdr + header_size + hdr->sizeofcmds);427 428    if (lc->cmd != LC_LOAD_DYLIB)429      continue;430 431    const dylib_command *dylib_lc = (const dylib_command *)lc;432    uint32_t dylib_name_offset = dylib_lc->dylib.name.offset;433    const char *dylib_name = ((const char *)dylib_lc) + dylib_name_offset;434    dylib_name = StripModuleName(dylib_name);435    if (dylib_name != 0 && (internal_strstr(dylib_name, "libclang_rt."))) {436      return true;437    }438  }439  return false;440}441 442const ImageHeader *MemoryMappingLayout::CurrentImageHeader() {443  const mach_header *hdr = (data_.current_image == kDyldImageIdx)444                                ? get_dyld_hdr()445                                : _dyld_get_image_header(data_.current_image);446  return (const ImageHeader *)hdr;447}448 449bool MemoryMappingLayout::Next(MemoryMappedSegment *segment) {450  for (; data_.current_image >= kDyldImageIdx; data_.current_image--) {451    const mach_header *hdr = (const mach_header *)CurrentImageHeader();452    if (!hdr) continue;453    if (data_.current_load_cmd_count < 0) {454      // Set up for this image;455      data_.current_load_cmd_count = hdr->ncmds;456      data_.current_magic = hdr->magic;457      data_.current_filetype = hdr->filetype;458      data_.current_arch = ModuleArchFromCpuType(hdr->cputype, hdr->cpusubtype);459      switch (data_.current_magic) {460#ifdef MH_MAGIC_64461        case MH_MAGIC_64: {462          data_.current_load_cmd_addr =463              (const char *)hdr + sizeof(mach_header_64);464          break;465        }466#endif467        case MH_MAGIC: {468          data_.current_load_cmd_addr = (const char *)hdr + sizeof(mach_header);469          break;470        }471        default: {472          continue;473        }474      }475      FindUUID((const load_command *)data_.current_load_cmd_addr, hdr,476               data_.current_uuid);477      data_.current_instrumented = IsModuleInstrumented(478          (const load_command *)data_.current_load_cmd_addr, hdr);479    }480 481    while (data_.current_load_cmd_count > 0) {482      switch (data_.current_magic) {483        // data_.current_magic may be only one of MH_MAGIC, MH_MAGIC_64.484#ifdef MH_MAGIC_64485        case MH_MAGIC_64: {486          if (NextSegmentLoad<LC_SEGMENT_64, struct segment_command_64>(487                  segment, segment->data_, &data_))488            return true;489          break;490        }491#endif492        case MH_MAGIC: {493          if (NextSegmentLoad<LC_SEGMENT, struct segment_command>(494                  segment, segment->data_, &data_))495            return true;496          break;497        }498      }499    }500    // If we get here, no more load_cmd's in this image talk about501    // segments.  Go on to the next image.502    data_.current_load_cmd_count = -1; // This will trigger loading next image503  }504  return false;505}506 507void MemoryMappingLayout::DumpListOfModules(508    InternalMmapVectorNoCtor<LoadedModule> *modules) {509  Reset();510  InternalMmapVector<char> module_name(kMaxPathLength);511  MemoryMappedSegment segment(module_name.data(), module_name.size());512  MemoryMappedSegmentData data;513  segment.data_ = &data;514  while (Next(&segment)) {515    // skip the __PAGEZERO segment, its vmsize is 0516    if (segment.filename[0] == '\0' || (segment.start == segment.end))517      continue;518    LoadedModule *cur_module = nullptr;519    if (!modules->empty() &&520        0 == internal_strcmp(segment.filename, modules->back().full_name())) {521      cur_module = &modules->back();522    } else {523      modules->push_back(LoadedModule());524      cur_module = &modules->back();525      cur_module->set(segment.filename, segment.start, segment.arch,526                      segment.uuid, data_.current_instrumented);527    }528    segment.AddAddressRanges(cur_module);529  }530}531 532}  // namespace __sanitizer533 534#endif  // SANITIZER_APPLE535