Source/PLCrashAsyncImageList.cpp (77 lines of code) (raw):

/* * Author: Landon Fuller <landonf@plausiblelabs.com> * * Copyright (c) 2008-2013 Plausible Labs Cooperative, Inc. * All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "PLCrashAsync.h" #include "PLCrashAsyncImageList.h" #include "PLCrashAsyncLinkedList.hpp" #include <stdlib.h> #include <string.h> #include <assert.h> using namespace plcrash::async; /** * @internal * @ingroup plcrash_async * @defgroup plcrash_async_image Binary Image Handling * * Maintains a linked list of binary images with support for async-safe iteration. Writing may occur concurrently with * async-safe reading, but is not async-safe. * * Atomic compare and swap is used to ensure a consistent view of the list for readers. To simplify implementation, a * write mutex is held for all updates; the implementation is not designed for efficiency in the face of contention * between readers and writers, and it's assumed that no contention should realistically occur. * @{ */ /** * Initialize a new binary image list and issue a memory barrier * * @param list The list structure to be initialized. * @param task The mach task from which all images will be mapped. * * @warning This method is not async safe. */ void plcrash_nasync_image_list_init (plcrash_async_image_list_t *list, mach_port_t task) { memset(list, 0, sizeof(*list)); list->_list = new async_list<plcrash_async_image_t *>(); list->task = task; mach_port_mod_refs(mach_task_self(), list->task, MACH_PORT_RIGHT_SEND, 1); } /** * Free any binary image list resources. * * @warning This method is not async safe. */ void plcrash_nasync_image_list_free (plcrash_async_image_list_t *list) { /* Clean up the image structures */ list->_list->set_reading(true); async_list<plcrash_async_image_t *>::node *next = NULL; while ((next = list->_list->next(next)) != NULL) { plcrash_async_image_t *image = next->value(); /* Deallocate the Mach-O reference. */ plcrash_nasync_macho_free(&image->macho_image); /* Deallocate the actual image value */ free(image); } list->_list->set_reading(false); /* Free the backing list */ delete list->_list; mach_port_mod_refs(mach_task_self(), list->task, MACH_PORT_RIGHT_SEND, -1); } /** * Append a new binary image record to @a list. * * @param list The list to which the image record should be appended. * @param header The image's header address. * @param name The image's name. * * @warning This method is not async safe. */ void plcrash_nasync_image_list_append (plcrash_async_image_list_t *list, pl_vm_address_t header, const char *name) { plcrash_error_t ret; /* Initialize the new entry. */ plcrash_async_image_t *new_entry = (plcrash_async_image_t *) calloc(1, sizeof(plcrash_async_image_t)); if ((ret = plcrash_nasync_macho_init(&new_entry->macho_image, list->task, name, header)) != PLCRASH_ESUCCESS) { PLCF_DEBUG("Unexpected failure initializing Mach-O structure for %s: %d", name, ret); free(new_entry); return; } /* Append */ list->_list->nasync_append(new_entry); } /** * Remove a binary image record from @a list. * * @param header The header address of the record to be removed. The first record matching this address will be removed. If no matching * header is found, the request will be ignored. * * @warning This method is not async safe. */ void plcrash_nasync_image_list_remove (plcrash_async_image_list_t *list, pl_vm_address_t header) { list->_list->set_reading(true); { /* Find a matching entry */ async_list<plcrash_async_image_t *>::node *found = NULL; async_list<plcrash_async_image_t *>::node *next = NULL; while ((next = list->_list->next(next)) != NULL) { if (next->value()->macho_image.header_addr == header) { found = next; break; } } /* If not found, nothing to do */ if (found == NULL) { PLCF_DEBUG("Can't find header addr=%llu in Mach-O image list.", (uint64_t)header); list->_list->set_reading(false); return; } /* Delete the entry */ list->_list->nasync_remove_node(found); } list->_list->set_reading(false); } /** * Retain or release the list for reading. This method is async-safe. * * This must be issued prior to attempting to iterate the list, and must called again once reads have completed. * * @param list The list to be be retained or released for reading. * @param enable If true, the list will be retained. If false, released. */ void plcrash_async_image_list_set_reading (plcrash_async_image_list_t *list, bool enable) { list->_list->set_reading(enable); } /** * Return the image containing the given @a address within its TEXT segment. This method is async-safe. * If image is found, NULL will be returned. * * @param list The list to be iterated. * @param address The address to be searched for. * * @warning The list must be retained for reading via plcrash_async_image_list_set_reading() before calling this function. */ plcrash_async_image_t *plcrash_async_image_containing_address (plcrash_async_image_list_t *list, pl_vm_address_t address) { plcrash_async_image_t *image = NULL; while ((image = plcrash_async_image_list_next(list, image)) != NULL) { if (plcrash_async_macho_contains_address(&image->macho_image, address)) return image; } /* Not found */ return NULL; } /** * Return the next image record. This method is async-safe. If no additional images are available, will return NULL; * * @param list The list to be iterated. * @param current The current image record, or NULL to start iteration. * * @warning The list must be retained for reading via plcrash_async_image_list_set_reading() before calling this function. */ plcrash_async_image_t *plcrash_async_image_list_next (plcrash_async_image_list_t *list, plcrash_async_image_t *current) { /* We can assume that the caller enabled reading here; we can't gaurantee proper behavior otherwise. */ async_list<plcrash_async_image_t *>::node *node; /* Fetch the next node */ if (current != NULL) { node = list->_list->next(current->_node); } else { node = list->_list->next(NULL); } /* Handle end of list */ if (node == NULL) return NULL; /* Lazily swap in the cyclic node reference. This is pessimestic, but there's really not a better time to do it. */ plcrash_async_image_t *image = node->value(); image->_node = node; return node->value(); } /* * @} */