blob: 1b1ce05b15af1d3c38498e968e2b7145b9859c04 [file] [log] [blame]
/*
* Copyright (c) 2013-2016, 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/user.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
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 % PAGE_SIZE;
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 0;
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_invalid;
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;
}