/// <summary> /// Unprotects a block of code so that it can be written to. /// </summary> /// <param name="code">Start of the region of code to unprotect.</param> /// <param name="size">Size of the region of code to unprotect.</param> /// <param name="oldProtection">Variable to receive the old protection state. The meaning of this value is platform-dependant.</param> /// <returns><c>true</c> if successful.</returns> bool unprotectCode(void *code, size_t size, int *oldProtection) { void *alignedCode; size_t alignedSize; pageAlign(code, size, &alignedCode, &alignedSize); const int newProtection = RWXProtection; if (mprotect(alignedCode, alignedSize, newProtection) != 0) { perror("Unprotection failed"); return false; } // Look up old protection state in the protection map if (!protection) { protection = new std::unordered_map<void*, int>(); *oldProtection = DefaultProtection; } else { std::unordered_map<void*, int>::const_iterator it = protection->find(alignedCode); if (it != protection->end()) *oldProtection = it->second; else *oldProtection = DefaultProtection; // No easy way of querying this, so just assume it's default } (*protection)[alignedCode] = newProtection; return true; }
void ControlFile::ensureMapping() { // If the file is not mmaped yet, first do a huge anonymous mmap // so that we never have to change the address, then mmap the // start of the file. if (!_mapBase) { int prot = PROT_READ; int flags = MAP_PRIVATE | MAP_ANON; int fd = -1; size_t length = pageAlign(_maxMapSize + 1); void *addr = mmap(NULL, length, prot, flags, fd, 0); if (!addr) { throwInvalid("Failed to get anonymous memory for control file: %s", strerror(errno)); } _mapBase = static_cast<char *>(addr); extendMapping(); char *s = strstr(_mapBase, "Prefix: "); if (!s) { throwInvalid("Bad format of mapped file. bleh."); } _prefix = s + strlen("Prefix: "); _firstComponent = _mapBase + _maxPrefix + 25; _firstComponent = strchr(_firstComponent, '\n') + 1; } }
void ControlFile::setPrefix(const char *prefix) { if (prefix && !hasPrefix() && _prefix) { char buf[_maxPrefix + 1]; sprintf(buf, "%.*s\n", _maxPrefix - 1, prefix); memcpy(_prefix, buf, strlen(buf)); msync(_mapBase, pageAlign(1), MS_ASYNC | MS_INVALIDATE); } }
void ControlFile::freeMapping() { // If the file is mmapped, release all resource used if (_mapBase && (munmap(_mapBase, pageAlign(_maxMapSize + 1)) < 0)) { LOG(warning, "munmapping of loglevel settings failed: %s", strerror(errno)); } _mapBase = NULL; }
/// <summary> /// Protects a block of code that was previously unprotected with <see cref="unprotectCode"/>. /// </summary> /// <param name="code">Start of the region of code to protect.</param> /// <param name="size">Size of the region of code to protect.</param> /// <param name="protection">The oldProtection value received from <see cref="unprotectCode"/>. /// <returns><c>true</c> if successful.</returns> bool protectCode(void *code, size_t size, int oldProtection) { void *alignedCode; size_t alignedSize; pageAlign(code, size, &alignedCode, &alignedSize); if (mprotect(alignedCode, alignedSize, oldProtection) != 0) return false; // Update protection state in the protection map if (!protection) protection = new std::unordered_map<void*, int>(); if (oldProtection != DefaultProtection) (*protection)[alignedCode] = oldProtection; else protection->erase(alignedCode); return true; }
bool ControlFile::extendMapping() { int fileLen = _fileBacking.size(); if (fileLen == -1) { _fileBacking.unlock(); LOG(error, "Cannot get file size of '%s': %s", _fileName, strerror(errno)); return false; } if (fileLen >= _maxMapSize) { _fileBacking.unlock(); LOG(error, "Log control file is too big at %d bytes (max " "size is %d). Ignoring it for further log components.", fileLen, _maxMapSize - 1); return false; } off_t size = pageAlign(fileLen); int prot = PROT_READ | (_mode == READONLY ? 0 : PROT_WRITE); int flags = MAP_FIXED | MAP_SHARED; int fd = _fileBacking.fd(); if (mmap(_mapBase, size, prot, flags, fd, 0) != _mapBase) { _fileBacking.unlock(); _mappedSize = -1; LOG(error, "failed to mmap lock file: %s", strerror(errno)); return false; } _mappedSize = size; _fileSize = fileLen; return true; }
void MachOObject::loadSegments() { for (Segment* seg : getSegments(*m_file)) { uintptr_t mappingSize; int initprot, maxprot; void* mappingAddr; void* rv; int flags = MAP_PRIVATE; if (strcmp(seg->segname, SEG_PAGEZERO) == 0 || seg->vmsize == 0) continue; assert(seg->vmsize >= seg->filesize); if (!m_base) { mappingAddr = (void*) seg->vmaddr; if (mappingAddr < MachOMgr::instance()->maxAddress()) mappingAddr = MachOMgr::instance()->maxAddress(); } else mappingAddr = (void*) (seg->vmaddr + m_slide); mappingSize = pageAlign(seg->filesize); maxprot = machoProtectionFlagsToMmap(seg->maxprot); initprot = machoProtectionFlagsToMmap(seg->initprot); if (MachOMgr::instance()->printSegments()) std::cerr << "dyld: Mapping segment " << seg->segname << " from " << m_file->filename() << " to " << mappingAddr << ", slide is 0x" << std::hex << m_slide << std::dec << std::endl; #ifndef __arm__ // All executables on iOS are PIE // The first segment can be moved by mmap, but not the following ones auto filetype = m_file->header().filetype; if (m_base || (!(m_file->header().flags & MH_PIE) && filetype != MH_DYLIB && filetype != MH_BUNDLE)) flags |= MAP_FIXED; else #endif { // When letting the system decide where to place the mapping, we need to make sure that the spot chosen // is big enough for all segments uintptr_t size = getTotalMappingSize(); rv = ::mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); if (rv == MAP_FAILED) { std::stringstream ss; ss << "Failed to mmap temporary anonymous range: " << strerror(errno); throw std::runtime_error(ss.str()); } flags |= MAP_FIXED; mappingAddr = rv; } rv = ::mmap(mappingAddr, mappingSize, maxprot, flags, m_file->fd(), m_file->offset() + seg->fileoff); if (rv == MAP_FAILED) { std::stringstream ss; if (errno == EPERM && uintptr_t(mappingAddr) < getMinMappingAddr()) { ss << "This executable is not position independent and your vm.mmap_min_addr is too low to load it. "; ss << "As low as " << uintptr_t(mappingAddr) << " is needed."; } else ss << "Failed to mmap '" << m_file->filename() << "': " << strerror(errno); throw std::runtime_error(ss.str()); } if (!m_base) { m_slide = (intptr_t(rv) - intptr_t(seg->vmaddr)); m_base = rv; mappingAddr = rv; } m_mappings.push_back(Mapping { mappingAddr, pageAlign(seg->vmsize), initprot, maxprot }); if (seg->vmsize > mappingSize) { // Map empty pages to cover the vmsize range mappingAddr = (void*) (seg->vmaddr + m_slide + mappingSize); mappingSize = seg->vmsize - mappingSize; rv = ::mmap(mappingAddr, mappingSize, maxprot, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); //int err = ::mprotect(mappingAddr, mappingSize, maxprot); if (rv == MAP_FAILED) { std::stringstream ss; ss << "Failed to mmap anonymous pages for '" << m_file->filename() << "': " << strerror(errno); throw std::runtime_error(ss.str()); } } } }