/* * Copyright (c) 2013-2017, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Intel Corporation nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #define _POSIX_C_SOURCE 1 #define _DARWIN_C_SOURCE 1 #include "pt_section.h" #include "pt_section_posix.h" #include "pt_section_file.h" #include "intel-pt.h" #include #include #include #include #include #include #include int pt_section_mk_status(void **pstatus, uint64_t *psize, const char *filename) { struct pt_sec_posix_status *status; struct stat buffer; int errcode; if (!pstatus || !psize) return -pte_internal; errcode = stat(filename, &buffer); if (errcode < 0) return errcode; if (buffer.st_size < 0) return -pte_bad_image; status = malloc(sizeof(*status)); if (!status) return -pte_nomem; status->stat = buffer; *pstatus = status; *psize = buffer.st_size; return 0; } static int check_file_status(struct pt_section *section, int fd) { struct pt_sec_posix_status *status; struct stat stat; int errcode; if (!section) return -pte_internal; errcode = fstat(fd, &stat); if (errcode) return -pte_bad_image; status = section->status; if (!status) return -pte_internal; if (stat.st_size != status->stat.st_size) return -pte_bad_image; if (stat.st_mtime != status->stat.st_mtime) return -pte_bad_image; return 0; } int pt_sec_posix_map(struct pt_section *section, int fd) { struct pt_sec_posix_mapping *mapping; uint64_t offset, size, adjustment; uint8_t *base; if (!section) return -pte_internal; offset = section->offset; size = section->size; adjustment = offset % sysconf(_SC_PAGESIZE); offset -= adjustment; size += adjustment; /* The section is supposed to fit into the file so we shouldn't * see any overflows, here. */ if (size < section->size) return -pte_internal; base = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, offset); if (base == MAP_FAILED) return -pte_nomem; mapping = malloc(sizeof(*mapping)); if (!mapping) goto out_map; mapping->base = base; mapping->size = size; mapping->begin = base + adjustment; mapping->end = base + size; section->mapping = mapping; section->unmap = pt_sec_posix_unmap; section->read = pt_sec_posix_read; return pt_section_add_bcache(section); out_map: munmap(base, size); return -pte_nomem; } int pt_section_map(struct pt_section *section) { const char *filename; uint16_t mcount; FILE *file; int fd, errcode; if (!section) return -pte_internal; errcode = pt_section_lock(section); if (errcode < 0) return errcode; mcount = section->mcount + 1; if (mcount > 1) { section->mcount = mcount; return pt_section_unlock(section); } errcode = -pte_internal; if (!mcount) goto out_unlock; if (section->mapping) goto out_unlock; filename = section->filename; if (!filename) goto out_unlock; errcode = -pte_bad_image; fd = open(filename, O_RDONLY); if (fd == -1) goto out_unlock; errcode = check_file_status(section, fd); if (errcode < 0) goto out_fd; /* We close the file on success. This does not unmap the section. */ errcode = pt_sec_posix_map(section, fd); if (!errcode) { section->mcount = 1; close(fd); return pt_section_unlock(section); } /* Fall back to file based sections - report the original error * if we fail to convert the file descriptor. */ file = fdopen(fd, "rb"); if (!file) goto out_fd; /* We need to keep the file open on success. It will be closed when * the section is unmapped. */ errcode = pt_sec_file_map(section, file); if (!errcode) { section->mcount = 1; return pt_section_unlock(section); } fclose(file); goto out_unlock; out_fd: close(fd); out_unlock: (void) pt_section_unlock(section); return errcode; } int pt_sec_posix_unmap(struct pt_section *section) { struct pt_sec_posix_mapping *mapping; if (!section) return -pte_internal; mapping = section->mapping; if (!mapping || !section->unmap || !section->read) return -pte_internal; section->mapping = NULL; section->unmap = NULL; section->read = NULL; munmap(mapping->base, mapping->size); free(mapping); return 0; } int pt_sec_posix_read(const struct pt_section *section, uint8_t *buffer, uint16_t size, uint64_t offset) { struct pt_sec_posix_mapping *mapping; const uint8_t *begin; if (!buffer || !section) return -pte_internal; mapping = section->mapping; if (!mapping) return -pte_internal; /* We already checked in pt_section_read() that the requested memory * lies within the section's boundaries. * * And we checked that the entire section was mapped. There's no need * to check for overflows, again. */ begin = mapping->begin + offset; memcpy(buffer, begin, size); return (int) size; }