diff options
Diffstat (limited to 'libipt/src/windows/pt_section_windows.c')
-rw-r--r-- | libipt/src/windows/pt_section_windows.c | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/libipt/src/windows/pt_section_windows.c b/libipt/src/windows/pt_section_windows.c new file mode 100644 index 0000000000000..b6229a06c9fa7 --- /dev/null +++ b/libipt/src/windows/pt_section_windows.c @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2015-2019, 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. + */ + +#include "pt_section.h" +#include "pt_section_windows.h" +#include "pt_section_file.h" + +#include "intel-pt.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <io.h> + + +static int pt_sec_windows_fstat(const char *filename, struct _stat *stat) +{ + int fd, errcode; + + if (!filename || !stat) + return -pte_internal; + + fd = _open(filename, _O_RDONLY); + if (fd == -1) + return -pte_bad_file; + + errcode = _fstat(fd, stat); + + _close(fd); + + if (errcode) + return -pte_bad_file; + + return 0; +} + +int pt_section_mk_status(void **pstatus, uint64_t *psize, const char *filename) +{ + struct pt_sec_windows_status *status; + struct _stat stat; + int errcode; + + if (!pstatus || !psize) + return -pte_internal; + + errcode = pt_sec_windows_fstat(filename, &stat); + if (errcode < 0) + return errcode; + + if (stat.st_size < 0) + return -pte_bad_image; + + status = malloc(sizeof(*status)); + if (!status) + return -pte_nomem; + + status->stat = stat; + + *pstatus = status; + *psize = stat.st_size; + + return 0; +} + +static int check_file_status(struct pt_section *section, int fd) +{ + struct pt_sec_windows_status *status; + struct _stat stat; + int errcode; + + if (!section) + return -pte_internal; + + errcode = _fstat(fd, &stat); + if (errcode) + return -pte_bad_file; + + 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; +} + +static DWORD granularity(void) +{ + struct _SYSTEM_INFO sysinfo; + + GetSystemInfo(&sysinfo); + + return sysinfo.dwAllocationGranularity; +} + +int pt_sec_windows_map(struct pt_section *section, int fd) +{ + struct pt_sec_windows_mapping *mapping; + uint64_t offset, size, adjustment; + HANDLE fh, mh; + DWORD dsize; + uint8_t *base; + int errcode; + + if (!section) + return -pte_internal; + + offset = section->offset; + size = section->size; + + adjustment = offset % granularity(); + + 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; + + dsize = (DWORD) size; + if ((uint64_t) dsize != size) + return -pte_internal; + + fh = (HANDLE) _get_osfhandle(fd); + + mh = CreateFileMapping(fh, NULL, PAGE_READONLY, 0, 0, NULL); + if (!mh) + return -pte_bad_image; + + base = MapViewOfFile(mh, FILE_MAP_READ, (DWORD) (offset >> 32), + (DWORD) (uint32_t) offset, dsize); + if (!base) { + errcode = -pte_bad_image; + goto out_mh; + } + + mapping = malloc(sizeof(*mapping)); + if (!mapping) { + errcode = -pte_nomem; + goto out_map; + } + + mapping->fd = fd; + mapping->mh = mh; + mapping->base = base; + mapping->begin = base + adjustment; + mapping->end = base + size; + + section->mapping = mapping; + section->unmap = pt_sec_windows_unmap; + section->read = pt_sec_windows_read; + section->memsize = pt_sec_windows_memsize; + + return 0; + +out_map: + UnmapViewOfFile(base); + +out_mh: + CloseHandle(mh); + return errcode; +} + +static int pt_sec_windows_map_success(struct pt_section *section) +{ + uint16_t mcount; + int errcode, status; + + if (!section) + return -pte_internal; + + mcount = section->mcount + 1; + if (!mcount) { + (void) pt_section_unlock(section); + return -pte_overflow; + } + + section->mcount = mcount; + + errcode = pt_section_unlock(section); + if (errcode < 0) + return errcode; + + status = pt_section_on_map(section); + if (status < 0) { + /* We had to release the section lock for pt_section_on_map() so + * @section may have meanwhile been mapped by other threads. + * + * We still want to return the error so we release our mapping. + * Our caller does not yet know whether pt_section_map() + * succeeded. + */ + (void) pt_section_unmap(section); + return status; + } + + return 0; +} + +int pt_section_map(struct pt_section *section) +{ + const char *filename; + HANDLE fh; + FILE *file; + int fd, errcode; + + if (!section) + return -pte_internal; + + errcode = pt_section_lock(section); + if (errcode < 0) + return errcode; + + if (section->mcount) + return pt_sec_windows_map_success(section); + + if (section->mapping) { + errcode = -pte_internal; + goto out_unlock; + } + + filename = section->filename; + if (!filename) { + errcode = -pte_internal; + goto out_unlock; + } + + fh = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (fh == INVALID_HANDLE_VALUE) { + /* We failed to open the file read-only. Let's try to open it + * read-write; maybe our user has the file open for writing. + * + * We will detect changes to the file via fstat(). + */ + + fh = CreateFileA(filename, GENERIC_READ, FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + NULL); + if (fh == INVALID_HANDLE_VALUE) { + errcode = -pte_bad_file; + goto out_unlock; + } + } + + fd = _open_osfhandle((intptr_t) fh, _O_RDONLY); + if (fd == -1) { + errcode = -pte_bad_file; + goto out_fh; + } + + errcode = check_file_status(section, fd); + if (errcode < 0) + goto out_fd; + + /* We leave the file open on success. It will be closed when the + * section is unmapped. + */ + errcode = pt_sec_windows_map(section, fd); + if (!errcode) + return pt_sec_windows_map_success(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) { + errcode = -pte_bad_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) + return pt_sec_windows_map_success(section); + + fclose(file); + goto out_unlock; + +out_fd: + _close(fd); + return errcode; + +out_fh: + CloseHandle(fh); + +out_unlock: + (void) pt_section_unlock(section); + return errcode; +} + +int pt_sec_windows_unmap(struct pt_section *section) +{ + struct pt_sec_windows_mapping *mapping; + + if (!section) + return -pte_internal; + + mapping = section->mapping; + if (!mapping || !section->unmap || !section->read || !section->memsize) + return -pte_internal; + + section->mapping = NULL; + section->unmap = NULL; + section->read = NULL; + section->memsize = NULL; + + UnmapViewOfFile(mapping->begin); + CloseHandle(mapping->mh); + _close(mapping->fd); + free(mapping); + + return 0; +} + +int pt_sec_windows_read(const struct pt_section *section, uint8_t *buffer, + uint16_t size, uint64_t offset) +{ + struct pt_sec_windows_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; +} + + +int pt_sec_windows_memsize(const struct pt_section *section, uint64_t *size) +{ + struct pt_sec_windows_mapping *mapping; + const uint8_t *begin, *end; + + if (!section || !size) + return -pte_internal; + + mapping = section->mapping; + if (!mapping) + return -pte_internal; + + begin = mapping->base; + end = mapping->end; + + if (!begin || !end || end < begin) + return -pte_internal; + + *size = (uint64_t) (end - begin); + + return 0; +} |