2008-08-06 18:36:30 +00:00
|
|
|
// This file is part of BOINC.
|
2007-04-17 02:27:09 +00:00
|
|
|
// http://boinc.berkeley.edu
|
2008-08-06 18:36:30 +00:00
|
|
|
// Copyright (C) 2008 University of California
|
2007-04-17 02:27:09 +00:00
|
|
|
//
|
2008-08-06 18:36:30 +00:00
|
|
|
// BOINC is free software; you can redistribute it and/or modify it
|
|
|
|
// under the terms of the GNU Lesser General Public License
|
|
|
|
// as published by the Free Software Foundation,
|
|
|
|
// either version 3 of the License, or (at your option) any later version.
|
2007-04-17 02:27:09 +00:00
|
|
|
//
|
2008-08-06 18:36:30 +00:00
|
|
|
// BOINC is distributed in the hope that it will be useful,
|
2007-04-17 02:27:09 +00:00
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
// See the GNU Lesser General Public License for more details.
|
|
|
|
//
|
2008-08-06 18:36:30 +00:00
|
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
|
|
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2007-04-17 02:27:09 +00:00
|
|
|
/*
|
|
|
|
* QSymbols.c
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* This is part of a backtrace generator for boinc project applications.
|
|
|
|
*
|
|
|
|
* Adapted from Apple Developer Technical Support Sample Code QCrashReport
|
|
|
|
*
|
|
|
|
* This code handles Mac OS X 10.3.x through 10.4.9. It may require some
|
|
|
|
* adjustment for future OS versions; see the discussion of _sigtramp and
|
|
|
|
* PowerPC Signal Stack Frames in file QBacktrace.c.
|
|
|
|
*
|
|
|
|
* For useful tips on using backtrace information, see Apple Tech Note 2123:
|
|
|
|
* http://developer.apple.com/technotes/tn2004/tn2123.html#SECNOSYMBOLS
|
|
|
|
*
|
|
|
|
* To convert addresses to correct symbols, use the atos command-line tool:
|
|
|
|
* atos -o path/to/executable/with/symbols address
|
|
|
|
* Note: if address 1a23 is hex, use 0x1a23.
|
|
|
|
*
|
|
|
|
* To demangle mangled C++ symbols, use the c++filt command-line tool.
|
|
|
|
* You may need to prefix C++ symbols with an additonal underscore before
|
|
|
|
* passing them to c++filt (so they begin with two underscore characters).
|
|
|
|
*
|
|
|
|
* A very useful shell script to add symbols to a crash dump can be found at:
|
|
|
|
* http://developer.apple.com/tools/xcode/symbolizingcrashdumps.html
|
|
|
|
* Pipe the output of the shell script through c++filt to demangle C++ symbols.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
File: QSymbols.c
|
|
|
|
|
|
|
|
Contains: Code for symbols to addresses and vice versa.
|
|
|
|
|
|
|
|
Written by: DTS
|
|
|
|
|
|
|
|
Copyright: Copyright (c) 2007 Apple Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
|
|
|
|
("Apple") in consideration of your agreement to the following
|
|
|
|
terms, and your use, installation, modification or
|
|
|
|
redistribution of this Apple software constitutes acceptance of
|
|
|
|
these terms. If you do not agree with these terms, please do
|
|
|
|
not use, install, modify or redistribute this Apple software.
|
|
|
|
|
|
|
|
In consideration of your agreement to abide by the following
|
|
|
|
terms, and subject to these terms, Apple grants you a personal,
|
|
|
|
non-exclusive license, under Apple's copyrights in this
|
|
|
|
original Apple software (the "Apple Software"), to use,
|
|
|
|
reproduce, modify and redistribute the Apple Software, with or
|
|
|
|
without modifications, in source and/or binary forms; provided
|
|
|
|
that if you redistribute the Apple Software in its entirety and
|
|
|
|
without modifications, you must retain this notice and the
|
|
|
|
following text and disclaimers in all such redistributions of
|
|
|
|
the Apple Software. Neither the name, trademarks, service marks
|
|
|
|
or logos of Apple Inc. may be used to endorse or promote
|
|
|
|
products derived from the Apple Software without specific prior
|
|
|
|
written permission from Apple. Except as expressly stated in
|
|
|
|
this notice, no other rights or licenses, express or implied,
|
|
|
|
are granted by Apple herein, including but not limited to any
|
|
|
|
patent rights that may be infringed by your derivative works or
|
|
|
|
by other works in which the Apple Software may be incorporated.
|
|
|
|
|
|
|
|
The Apple Software is provided by Apple on an "AS IS" basis.
|
|
|
|
APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
|
|
|
|
WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
|
|
|
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING
|
|
|
|
THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
|
|
|
|
COMBINATION WITH YOUR PRODUCTS.
|
|
|
|
|
|
|
|
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
|
|
|
|
INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
|
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY
|
|
|
|
OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
|
|
|
|
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY
|
|
|
|
OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
|
|
|
|
OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF
|
|
|
|
SUCH DAMAGE.
|
|
|
|
|
|
|
|
Change History (most recent first):
|
|
|
|
|
|
|
|
$Log: QSymbols.c,v $
|
|
|
|
Revision 1.2 2007/03/02 13:00:51
|
|
|
|
Fix an error handling bug in the creation code.
|
|
|
|
|
|
|
|
Revision 1.1 2007/03/02 12:20:29
|
|
|
|
First checked in.
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// Our Prototypes
|
2007-10-03 21:31:20 +00:00
|
|
|
#include "config.h"
|
2007-04-17 02:27:09 +00:00
|
|
|
#include "QSymbols.h"
|
|
|
|
|
|
|
|
// Mac OS Interfaces
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <mach-o/nlist.h>
|
|
|
|
#include <mach-o/arch.h>
|
|
|
|
|
|
|
|
// Project interfaces
|
|
|
|
|
|
|
|
#include "QMachOImage.h"
|
|
|
|
#include "QMachOImageList.h"
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
#pragma mark ***** Performance Notes
|
|
|
|
|
|
|
|
/*
|
|
|
|
I'm currently not happy with the performance characteristics of this module.
|
|
|
|
There are /way/ too many linear searches for my liking. However, there are a
|
|
|
|
number of reasons I implemented it this way. In no particular order, these are:
|
|
|
|
|
|
|
|
o In general I believe in the principle of "make it work, then make it
|
|
|
|
go fast". To that end, I've specifically designed the module to allow
|
|
|
|
for future optimisation.
|
|
|
|
|
|
|
|
o The current performance isn't too bad. While there's lots of linear
|
|
|
|
algorithms, the data structures that we're operating on are all cached
|
|
|
|
in client-side memory, so there's very little IPC cost.
|
|
|
|
|
|
|
|
o If and when I write the optimised code, it will be nice to have the
|
|
|
|
simple code around for testing. That is, I'll be able to automatically
|
|
|
|
test the new code by comparing its results to the old code.
|
|
|
|
|
|
|
|
o This is sample code. I've already spent way too much time on this, and
|
|
|
|
writing the optimised version of the code would consume even more time.
|
|
|
|
optimising
|
|
|
|
|
|
|
|
o The overall goal of this project is to provide infrastructure for crash
|
|
|
|
reporting; crash reporting is not, in general, time sensitive.
|
|
|
|
|
|
|
|
The way I'd make this go fast are as follows:
|
|
|
|
|
|
|
|
o Don't do anything more on QSymbolsRef creation. Rather, create the
|
|
|
|
'go faster' data structures when the first address-to-symbol or
|
|
|
|
symbol-to-address operation is done. This is because the type of
|
|
|
|
data structure you want is different for each operation, and you don't
|
|
|
|
want to spend the time creating the data structures for both types
|
|
|
|
if the client is only going to use one.
|
|
|
|
|
|
|
|
o The address-to-symbol optimising data structure would consist of a
|
|
|
|
sorted array of section starts. This would be created lazily when the
|
|
|
|
first address-to-symbol operation is done. You could binary search
|
|
|
|
this to find the section containing the address. Then, for each section,
|
|
|
|
you would (lazily) create an array of symbols sorted by their start
|
|
|
|
address. So you could binary search that to find the best symbol.
|
|
|
|
|
|
|
|
This would make QSymGetSymbolForAddress O(log2 n) + O(log2 m), where
|
|
|
|
n is the number of sections and m is the number of symbols within a
|
|
|
|
section.
|
|
|
|
|
|
|
|
o The symbol-to-address optimising data structure would start with a
|
|
|
|
hash table that maps library names to image objects. For each image
|
|
|
|
object, there would be another hash table to map symbols to addresses.
|
|
|
|
|
|
|
|
This would make a QSymGetAddressForSymbol call to look up a symbol with
|
|
|
|
a particular library O(1). It would make a flat namespace lookup O(n),
|
|
|
|
where n is the number of images. You could further reduce that to O(1)
|
|
|
|
by creating a global hash table for all symbols from all libraries.
|
|
|
|
|
|
|
|
o These two data structures would also support an O(1) implementation of
|
|
|
|
QSymGetNextSymbol.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// In the QSymbols data structure, we record the index of the image for dyld
|
|
|
|
// and the main executable. We initialise these indexes to kQSymBadImageIndex so
|
|
|
|
// we can tell whether we've found the corresponding image.
|
|
|
|
|
|
|
|
enum {
|
|
|
|
kQSymBadImageIndex = (size_t) -1
|
|
|
|
};
|
|
|
|
|
|
|
|
// QMOImage represents all of the symbols with a process. It's the backing for
|
|
|
|
// the exported QSymbolsRef type.
|
|
|
|
|
|
|
|
struct QSymbols {
|
|
|
|
task_t task; // target task
|
|
|
|
bool didSuspend; // true if we suspended the target task
|
|
|
|
size_t imageCount; // size of images array
|
|
|
|
QMOImageRef * images; // pointer to images array
|
|
|
|
size_t execIndex; // index of main executable in images array
|
|
|
|
size_t dyldIndex; // index of dyld in images array
|
|
|
|
};
|
|
|
|
typedef struct QSymbols QSymbols;
|
|
|
|
|
|
|
|
#if ! defined(NDEBUG)
|
|
|
|
|
|
|
|
static bool QSymIsValid(QSymbolsRef symRef)
|
|
|
|
// Returns true if symRef references a valid QSymbols data structure.
|
|
|
|
{
|
|
|
|
bool valid;
|
|
|
|
size_t imageIndex;
|
|
|
|
|
|
|
|
valid = (symRef != NULL)
|
|
|
|
&& (symRef->imageCount > 0)
|
|
|
|
&& (symRef->images != NULL)
|
|
|
|
&& ( (symRef->execIndex == kQSymBadImageIndex) || (symRef->execIndex < symRef->imageCount) )
|
|
|
|
&& ( (symRef->dyldIndex == kQSymBadImageIndex) || (symRef->dyldIndex < symRef->imageCount) );
|
|
|
|
if (valid) {
|
|
|
|
for (imageIndex = 0; imageIndex < symRef->imageCount; imageIndex++) {
|
|
|
|
if ( symRef->images[imageIndex] == NULL ) {
|
|
|
|
valid = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return valid;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
extern void QSymDestroy(QSymbolsRef symRef)
|
|
|
|
// See comment in header.
|
|
|
|
{
|
|
|
|
kern_return_t krJunk;
|
|
|
|
size_t imageIndex;
|
|
|
|
|
|
|
|
if (symRef != NULL) {
|
|
|
|
if (symRef->images != NULL) {
|
|
|
|
for (imageIndex = 0; imageIndex < symRef->imageCount; imageIndex++) {
|
|
|
|
QMOImageDestroy(symRef->images[imageIndex]);
|
|
|
|
}
|
|
|
|
free(symRef->images);
|
|
|
|
}
|
|
|
|
if (symRef->didSuspend) {
|
|
|
|
krJunk = task_resume(symRef->task);
|
|
|
|
assert(krJunk == KERN_SUCCESS);
|
|
|
|
}
|
|
|
|
free(symRef);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extern int QSymCreateFromTask(
|
|
|
|
task_t task,
|
|
|
|
bool suspend,
|
|
|
|
cpu_type_t cputype,
|
|
|
|
QSymbolsRef * symRefPtr
|
|
|
|
)
|
|
|
|
// See comment in header.
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
kern_return_t kr;
|
|
|
|
QSymbolsRef symRef;
|
|
|
|
size_t imageIndex;
|
|
|
|
|
|
|
|
assert(task != MACH_PORT_NULL);
|
|
|
|
assert( symRefPtr != NULL);
|
|
|
|
assert(*symRefPtr == NULL);
|
|
|
|
|
|
|
|
symRef = NULL;
|
|
|
|
|
|
|
|
// Basic initialisation. This includes getting the image list from the task.
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
if ( (task == mach_task_self()) && suspend ) {
|
|
|
|
err = EINVAL;
|
|
|
|
}
|
|
|
|
if (err == 0) {
|
|
|
|
symRef = (QSymbolsRef) calloc(1, sizeof(*symRef));
|
|
|
|
if (symRef == NULL) {
|
|
|
|
err = ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (err == 0) {
|
|
|
|
symRef->task = task;
|
|
|
|
symRef->execIndex = kQSymBadImageIndex;
|
|
|
|
symRef->dyldIndex = kQSymBadImageIndex;
|
|
|
|
|
|
|
|
if (suspend) {
|
|
|
|
kr = task_suspend(symRef->task);
|
|
|
|
err = QTMErrnoFromMachError(kr);
|
|
|
|
|
|
|
|
symRef->didSuspend = (err == 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (err == 0) {
|
|
|
|
err = QMOImageListFromTask(task, cputype, NULL, 0, &symRef->imageCount);
|
|
|
|
}
|
|
|
|
if (err == 0) {
|
|
|
|
symRef->images = (QMOImageRef *) calloc(symRef->imageCount, sizeof(*symRef->images));
|
|
|
|
if (symRef->images == NULL) {
|
|
|
|
err = ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (err == 0) {
|
|
|
|
err = QMOImageListFromTask(task, cputype, symRef->images, symRef->imageCount, &symRef->imageCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Search for dyld and the main executable.
|
|
|
|
|
|
|
|
if (err == 0) {
|
|
|
|
for (imageIndex = 0; imageIndex < symRef->imageCount; imageIndex++) {
|
|
|
|
int32_t fileType;
|
|
|
|
|
|
|
|
fileType = QMOImageGetFileType(symRef->images[imageIndex]);
|
|
|
|
switch (fileType) {
|
|
|
|
case MH_EXECUTE:
|
|
|
|
assert(symRef->execIndex == kQSymBadImageIndex);
|
|
|
|
symRef->execIndex = imageIndex;
|
|
|
|
case MH_DYLINKER:
|
|
|
|
assert(symRef->dyldIndex == kQSymBadImageIndex);
|
|
|
|
symRef->dyldIndex = imageIndex;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// do nothing
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clean up.
|
|
|
|
|
|
|
|
if (err != 0) {
|
|
|
|
QSymDestroy(symRef);
|
|
|
|
symRef = NULL;
|
|
|
|
}
|
|
|
|
*symRefPtr = symRef;
|
|
|
|
|
|
|
|
assert( (err == 0) == (*symRefPtr != NULL) );
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern int QSymCreateFromSelf(
|
|
|
|
QSymbolsRef * symRefPtr
|
|
|
|
)
|
|
|
|
// See comment in header.
|
|
|
|
{
|
|
|
|
// All of the infrastructure that QSymCreateFromTask uses (specifically,
|
|
|
|
// QMOImageListFromTask and QMOImageCreateFromTask) has a short circuit
|
|
|
|
// implementation when task is mach_task_self. So we can just call that
|
|
|
|
// routine with mach_task_self and be assured that we'll get the simple
|
|
|
|
// implementation.
|
|
|
|
|
|
|
|
// We have to pass in the local CPU type (from QMOGetLocalCPUType), not
|
|
|
|
// CPU_TYPE_ANY. Otherwise, if we're being run using Rosetta, there's a chance
|
|
|
|
// we might find the native dyld rather than the PowerPC one. If that happens,
|
|
|
|
// we end up looking at the wrong list of Mach-O images, and things go south
|
|
|
|
// quickly.
|
|
|
|
|
|
|
|
return QSymCreateFromTask(mach_task_self(), false, QMOGetLocalCPUType(), symRefPtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
extern QMOImageRef QSymGetDyldImage(QSymbolsRef symRef)
|
|
|
|
// See comment in header.
|
|
|
|
{
|
|
|
|
QMOImageRef result;
|
|
|
|
|
|
|
|
assert( QSymIsValid(symRef) );
|
|
|
|
|
|
|
|
result = NULL;
|
|
|
|
if (symRef->dyldIndex != kQSymBadImageIndex) {
|
|
|
|
result = symRef->images[symRef->dyldIndex];
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern QMOImageRef QSymGetExecutableImage(QSymbolsRef symRef)
|
|
|
|
// See comment in header.
|
|
|
|
{
|
|
|
|
QMOImageRef result;
|
|
|
|
|
|
|
|
assert( QSymIsValid(symRef) );
|
|
|
|
|
|
|
|
result = NULL;
|
|
|
|
if (symRef->execIndex != kQSymBadImageIndex) {
|
|
|
|
result = symRef->images[symRef->execIndex];
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern void QSymGetImages(
|
|
|
|
QSymbolsRef symRef,
|
|
|
|
QMOImageRef ** imageArrayPtr,
|
|
|
|
size_t * imageCountPtr
|
|
|
|
)
|
|
|
|
// See comment in header.
|
|
|
|
{
|
|
|
|
assert( QSymIsValid(symRef) );
|
|
|
|
assert(imageArrayPtr != NULL);
|
|
|
|
assert(imageCountPtr != NULL);
|
|
|
|
|
|
|
|
*imageArrayPtr = symRef->images;
|
|
|
|
*imageCountPtr = symRef->imageCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int SymbolToAddressCallback(
|
|
|
|
QMOImageRef qmoImage,
|
|
|
|
const char * name,
|
|
|
|
uint8_t type,
|
|
|
|
uint8_t sect,
|
|
|
|
uint16_t desc,
|
|
|
|
QTMAddr value,
|
|
|
|
void * iteratorRefCon,
|
|
|
|
bool * stopPtr
|
|
|
|
)
|
|
|
|
// The QMOImageIterateSymbols callback for QSymGetAddressForSymbol. See
|
|
|
|
// See QMOSymbolIteratorProc for a description of the parameters.
|
|
|
|
//
|
|
|
|
// iteratorRefCon is a pointer to a QSymSymbolInfo structure. Initially,
|
|
|
|
// only the symbolName field is valid. If this routine finds a matching
|
|
|
|
// symbol, it sets up the symbolType, symbolName, symbolImage, and symbolValue
|
|
|
|
// fields.
|
|
|
|
//
|
|
|
|
// IMPORTANT: We set symbolInfo->symbolName to "name" because the initial
|
|
|
|
// value of symbolName comes from the client, so the lifetime of the referenced
|
|
|
|
// memory is undefined. However, our "name" input parameter comes from the
|
|
|
|
// QMOImageRef object, so it persists until that object is destroy, which
|
|
|
|
// only happens when the QSymbolsRef object is destroyed.
|
|
|
|
{
|
|
|
|
#pragma unused(sect, desc)
|
|
|
|
int err;
|
|
|
|
QSymSymbolInfo * symbolInfo;
|
|
|
|
|
|
|
|
assert(qmoImage != NULL);
|
|
|
|
assert(name != NULL);
|
|
|
|
assert( stopPtr != NULL);
|
|
|
|
assert(*stopPtr == false);
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
// Ignore debugger symbols.
|
|
|
|
|
|
|
|
if ( ! (type & N_STAB) ) {
|
|
|
|
symbolInfo = (QSymSymbolInfo *) iteratorRefCon;
|
|
|
|
|
|
|
|
// Do the names match.
|
|
|
|
|
|
|
|
if ( strcmp(name, symbolInfo->symbolName) == 0 ) {
|
|
|
|
|
|
|
|
// If so, check that it's one of our supported types and, if so,
|
|
|
|
// set up the output structure.
|
|
|
|
|
|
|
|
switch (type & N_TYPE) {
|
|
|
|
case N_ABS:
|
|
|
|
symbolInfo->symbolType = (type & N_EXT) ? kQSymDyldPublicSymbol : kQSymDyldPrivateSymbol;
|
|
|
|
symbolInfo->symbolImage = qmoImage;
|
|
|
|
symbolInfo->symbolName = name;
|
|
|
|
symbolInfo->symbolValue = value;
|
|
|
|
*stopPtr = true;
|
|
|
|
break;
|
|
|
|
case N_SECT:
|
|
|
|
symbolInfo->symbolType = (type & N_EXT) ? kQSymDyldPublicSymbol : kQSymDyldPrivateSymbol;
|
|
|
|
symbolInfo->symbolImage = qmoImage;
|
|
|
|
symbolInfo->symbolName = name;
|
|
|
|
symbolInfo->symbolValue = value + QMOImageGetSlide(qmoImage);
|
|
|
|
*stopPtr = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern char * QSymCreateLibraryNameWithSuffix(const char *libraryName, const char *suffix);
|
|
|
|
// I need to export this for the benefit of the test program. So it isn't
|
|
|
|
// in a header, but it is "extern".
|
|
|
|
|
|
|
|
extern char * QSymCreateLibraryNameWithSuffix(const char *libraryName, const char *suffix)
|
|
|
|
// Add the suffix (for example, "_debug") to the library name (for example,
|
|
|
|
// "/usr/lib/libSystem.B_debug.dylib"). Place the suffix before the extension
|
|
|
|
// if there is one.
|
|
|
|
{
|
|
|
|
const char * nameStart;
|
|
|
|
const char * lastDot;
|
|
|
|
size_t libraryNameLen;
|
|
|
|
const char * extension;
|
|
|
|
char * result;
|
|
|
|
|
|
|
|
assert(libraryName != NULL);
|
|
|
|
assert(suffix != NULL);
|
|
|
|
|
|
|
|
nameStart = strrchr(libraryName, '/');
|
|
|
|
if (nameStart == NULL) {
|
|
|
|
nameStart = libraryName;
|
|
|
|
} else {
|
|
|
|
nameStart += 1;
|
|
|
|
}
|
|
|
|
lastDot = strrchr(nameStart, '.');
|
|
|
|
|
|
|
|
// Default to just concatenating the suffix.
|
|
|
|
|
|
|
|
libraryNameLen = strlen(libraryName);
|
|
|
|
extension = "";
|
|
|
|
|
|
|
|
// If there's an extensiont, break the string at the dot and place the suffix
|
|
|
|
// between the two.
|
|
|
|
|
|
|
|
if (lastDot != NULL) {
|
|
|
|
libraryNameLen = lastDot - libraryName;
|
|
|
|
extension = lastDot;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = NULL;
|
|
|
|
asprintf(&result, "%.*s%s%s", (int) libraryNameLen, libraryName, suffix, extension);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern int QSymGetAddressForSymbol(
|
|
|
|
QSymbolsRef symRef,
|
|
|
|
const char * libraryName,
|
|
|
|
const char * symbolName,
|
|
|
|
QSymSymbolInfo * symbolInfo
|
|
|
|
)
|
|
|
|
// See comment in header.
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
size_t imageIndex;
|
|
|
|
size_t imageIndexStart;
|
|
|
|
size_t imageIndexLimit;
|
|
|
|
|
|
|
|
assert( QSymIsValid(symRef) );
|
|
|
|
// libraryName may be NULL
|
|
|
|
assert(symbolName != NULL);
|
|
|
|
assert(symbolInfo != NULL);
|
|
|
|
|
|
|
|
// First find the library to search, if any. We first try with no suffix,
|
|
|
|
// then with the _debug and _profile suffixes.
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
imageIndexStart = 0;
|
|
|
|
imageIndexLimit = symRef->imageCount;
|
|
|
|
if (libraryName != NULL) {
|
|
|
|
static const char * kSuffixes[] = { "", "_debug", "_profile", NULL };
|
|
|
|
size_t suffixIndex;
|
|
|
|
|
|
|
|
suffixIndex = 0;
|
|
|
|
do {
|
|
|
|
char * libraryNameWithSuffix;
|
|
|
|
|
|
|
|
libraryNameWithSuffix = QSymCreateLibraryNameWithSuffix(libraryName, kSuffixes[suffixIndex]);
|
|
|
|
if (libraryNameWithSuffix == NULL) {
|
|
|
|
err = ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err == 0) {
|
|
|
|
for (imageIndex = 0; imageIndex < symRef->imageCount; imageIndex++) {
|
|
|
|
const char * thisImageName;
|
|
|
|
|
|
|
|
thisImageName = QMOImageGetLibraryID(symRef->images[imageIndex]);
|
|
|
|
if ( (thisImageName != NULL) && (strcmp(thisImageName, libraryNameWithSuffix) == 0) ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (imageIndex == symRef->imageCount) {
|
|
|
|
err = ESRCH;
|
|
|
|
} else {
|
|
|
|
imageIndexStart = imageIndex;
|
|
|
|
imageIndexLimit = imageIndex + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(libraryNameWithSuffix);
|
|
|
|
|
|
|
|
suffixIndex += 1;
|
|
|
|
} while ( (err == ESRCH) && (kSuffixes[suffixIndex] != NULL) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Within the range of libraries specified by imageIndexStart and imageIndexLimit,
|
|
|
|
// search for the symbol.
|
|
|
|
|
|
|
|
if (err == 0) {
|
|
|
|
memset(symbolInfo, 0, sizeof(*symbolInfo));
|
|
|
|
symbolInfo->symbolType = kQSymNoSymbol;
|
|
|
|
symbolInfo->symbolName = symbolName;
|
|
|
|
|
|
|
|
for (imageIndex = imageIndexStart; imageIndex < imageIndexLimit; imageIndex++) {
|
|
|
|
err = QMOImageIterateSymbols(symRef->images[imageIndex], SymbolToAddressCallback, symbolInfo);
|
|
|
|
if ( (err != 0) || (symbolInfo->symbolType != kQSymNoSymbol) ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( (err == 0) && (symbolInfo->symbolType == kQSymNoSymbol) ) {
|
|
|
|
err = ESRCH;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clean up if there's any failure.
|
|
|
|
|
|
|
|
if (err != 0) {
|
|
|
|
memset(symbolInfo, 0, sizeof(*symbolInfo));
|
|
|
|
symbolInfo->symbolType = kQSymNoSymbol;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Post-conditions
|
|
|
|
|
|
|
|
assert( (err == 0) == (symbolInfo->symbolType != kQSymNoSymbol) );
|
|
|
|
assert( (symbolInfo->symbolType != kQSymNoSymbol) == (symbolInfo->symbolImage != NULL) );
|
|
|
|
assert( (symbolInfo->symbolType != kQSymNoSymbol) == (symbolInfo->symbolName != NULL) );
|
|
|
|
assert(symbolInfo->symbolOffset == 0); // for sym -> addr lookup, offset always zero
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern int QSymGetAddressesForSymbols(
|
|
|
|
QSymbolsRef symRef,
|
|
|
|
size_t count,
|
|
|
|
const char * libraryNames[],
|
|
|
|
const char * symbolNames[],
|
|
|
|
QSymSymbolInfo symbolInfos[]
|
|
|
|
)
|
|
|
|
// See comment in header.
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
size_t symbolIndex;
|
|
|
|
|
|
|
|
assert( QSymIsValid(symRef) );
|
|
|
|
assert(count > 0);
|
|
|
|
// libraryNames may be NULL
|
|
|
|
assert(symbolNames != NULL);
|
|
|
|
for (symbolIndex = 0; symbolIndex < count; symbolIndex++) {
|
|
|
|
assert(symbolNames[symbolIndex] != NULL);
|
|
|
|
}
|
|
|
|
assert(symbolInfos != NULL);
|
|
|
|
|
|
|
|
// The current implementation is very naive. I could definitely make this
|
|
|
|
// faster, but see my "Performance Notes" comment at the top of this file.
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
for (symbolIndex = 0; symbolIndex < count; symbolIndex++) {
|
|
|
|
const char * thisLib;
|
|
|
|
|
|
|
|
if (libraryNames == NULL) {
|
|
|
|
thisLib = NULL;
|
|
|
|
} else {
|
|
|
|
thisLib = libraryNames[symbolIndex];
|
|
|
|
}
|
|
|
|
(void) QSymGetAddressForSymbol(symRef, thisLib, symbolNames[symbolIndex], &symbolInfos[symbolIndex]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern int QSymGetImageForAddress(
|
|
|
|
QSymbolsRef symRef,
|
|
|
|
QTMAddr addr,
|
|
|
|
QMOImageRef * qmoImagePtr,
|
|
|
|
uint32_t * sectIndexPtr
|
|
|
|
)
|
|
|
|
// See comment in header.
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
int junk;
|
|
|
|
size_t imageIndex;
|
|
|
|
QTMAddr slide;
|
|
|
|
uint32_t sectCount;
|
|
|
|
uint32_t sectIndex;
|
|
|
|
struct section_64 sect;
|
|
|
|
bool found;
|
|
|
|
|
|
|
|
assert( QSymIsValid(symRef) );
|
|
|
|
assert( qmoImagePtr != NULL );
|
|
|
|
// sectIndexPtr may be NULL
|
|
|
|
|
|
|
|
// Iterate through the libraries looking for the section that contains the address.
|
|
|
|
|
|
|
|
found = false;
|
|
|
|
for (imageIndex = 0; imageIndex < symRef->imageCount; imageIndex++) {
|
|
|
|
slide = QMOImageGetSlide(symRef->images[imageIndex]);
|
|
|
|
|
|
|
|
sectCount = QMOImageGetSectionCount(symRef->images[imageIndex]);
|
|
|
|
|
|
|
|
for (sectIndex = 0; sectIndex < sectCount; sectIndex++) {
|
|
|
|
junk = QMOImageGetSectionByIndex(symRef->images[imageIndex], sectIndex, §);
|
|
|
|
assert(junk == 0);
|
|
|
|
|
|
|
|
if ( (strcmp(sect.segname, "__TEXT") == 0) || (strcmp(sect.segname, "__DATA") == 0) ) {
|
|
|
|
found = (addr >= (sect.addr + slide)) && (addr < (sect.addr + slide + sect.size));
|
|
|
|
if ( found ) {
|
|
|
|
*qmoImagePtr = symRef->images[imageIndex];
|
|
|
|
if (sectIndexPtr != NULL) {
|
|
|
|
*sectIndexPtr = sectIndex;
|
|
|
|
}
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( found ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (found) {
|
|
|
|
err = 0;
|
|
|
|
} else {
|
|
|
|
err = ESRCH;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert( (err != 0) || (*qmoImagePtr != NULL) );
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddressToSymbolContext is the parameter block passed to AddressToSymbolCallback
|
|
|
|
// via its iteratorRefCon.
|
|
|
|
|
|
|
|
struct AddressToSymbolContext {
|
|
|
|
QTMAddr requiredAddr;
|
|
|
|
uint8_t requiredSect;
|
|
|
|
const char * currentSymbol;
|
|
|
|
uint8_t currentSymbolType;
|
|
|
|
QTMAddr currentSymbolValue;
|
|
|
|
QTMOffset currentOffset;
|
|
|
|
};
|
|
|
|
typedef struct AddressToSymbolContext AddressToSymbolContext;
|
|
|
|
|
|
|
|
static int AddressToSymbolCallback(
|
|
|
|
QMOImageRef qmoImage,
|
|
|
|
const char * name,
|
|
|
|
uint8_t type,
|
|
|
|
uint8_t sect,
|
|
|
|
uint16_t desc,
|
|
|
|
QTMAddr value,
|
|
|
|
void * iteratorRefCon,
|
|
|
|
bool * stopPtr
|
|
|
|
)
|
|
|
|
// The QMOImageIterateSymbols callback for QSymGetSymbolForAddress. See
|
|
|
|
// See QMOSymbolIteratorProc for a description of the parameters.
|
|
|
|
//
|
|
|
|
// iteratorRefCon is a pointer to a AddressToSymbolContext structure. Initially,
|
|
|
|
// the requiredSect, requiredAddr, and currentOffset fields are valid. As
|
|
|
|
// this routine finds better matching symbols, it fills out currentSymbol,
|
|
|
|
// currentSymbolType, and currentSymbolValue, and adjusts currentOffset.
|
|
|
|
// In this way, currentOffset slowly decreases until, after all symbols
|
|
|
|
// have been considered, AddressToSymbolContext contains the best match.
|
|
|
|
{
|
|
|
|
#pragma unused(desc)
|
|
|
|
int err;
|
|
|
|
AddressToSymbolContext * iterContext;
|
|
|
|
QTMAddr symValue;
|
|
|
|
QTMOffset symOffset;
|
|
|
|
|
|
|
|
assert(qmoImage != NULL);
|
|
|
|
assert(name != NULL);
|
|
|
|
assert( stopPtr != NULL);
|
|
|
|
assert(*stopPtr == false);
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
// Ignore debugger symbols.
|
|
|
|
|
|
|
|
if ( ! (type & N_STAB) ) {
|
|
|
|
iterContext = (AddressToSymbolContext *) iteratorRefCon;
|
|
|
|
|
|
|
|
// Check that it's a section-based symbol within our section.
|
|
|
|
|
|
|
|
if ( ((type & N_TYPE) == N_SECT) && (sect == iterContext->requiredSect) ) {
|
|
|
|
|
|
|
|
// Check that it's a better match than our current symbol.
|
|
|
|
|
|
|
|
symValue = value + QMOImageGetSlide(qmoImage);
|
|
|
|
symOffset = (iterContext->requiredAddr - symValue);
|
|
|
|
if ( (symValue <= iterContext->requiredAddr) && (symOffset < iterContext->currentOffset) ) {
|
|
|
|
|
|
|
|
// If it is, record the name of the symbol and its offset.
|
|
|
|
|
|
|
|
iterContext->currentSymbol = name;
|
|
|
|
iterContext->currentSymbolType = type;
|
|
|
|
iterContext->currentSymbolValue = symValue;
|
|
|
|
iterContext->currentOffset = symOffset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern int QSymGetSymbolForAddress(
|
|
|
|
QSymbolsRef symRef,
|
|
|
|
QTMAddr addr,
|
|
|
|
QSymSymbolInfo * symbolInfo
|
|
|
|
)
|
|
|
|
// See comment in header.
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
QMOImageRef qmoImage;
|
|
|
|
uint32_t sectIndex;
|
|
|
|
struct section_64 sect;
|
|
|
|
|
|
|
|
assert( QSymIsValid(symRef) );
|
|
|
|
assert(symbolInfo != NULL);
|
|
|
|
|
|
|
|
memset(symbolInfo, 0, sizeof(*symbolInfo));
|
|
|
|
symbolInfo->symbolType = kQSymNoSymbol;
|
|
|
|
|
|
|
|
// Find the section and then get its information.
|
|
|
|
|
|
|
|
err = QSymGetImageForAddress(symRef, addr, &qmoImage, §Index);
|
|
|
|
if (err == 0) {
|
|
|
|
err = QMOImageGetSectionByIndex(qmoImage, sectIndex, §);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Within that image, iterate through the symbols that are relative to the
|
|
|
|
// section we found and that closest match the address.
|
|
|
|
|
|
|
|
if (err == 0) {
|
|
|
|
AddressToSymbolContext iterContext;
|
|
|
|
|
|
|
|
iterContext.requiredAddr = addr;
|
|
|
|
iterContext.requiredSect = sectIndex + 1; // sect is one-based in QMOImageIterateSymbols callback
|
|
|
|
iterContext.currentSymbol = NULL;
|
|
|
|
iterContext.currentSymbolType = 0;
|
|
|
|
iterContext.currentSymbolValue = 0;
|
|
|
|
iterContext.currentOffset = sect.size;
|
|
|
|
|
|
|
|
err = QMOImageIterateSymbols(qmoImage, AddressToSymbolCallback, &iterContext);
|
|
|
|
|
|
|
|
if (err == 0) {
|
|
|
|
if (iterContext.currentSymbol == NULL) {
|
|
|
|
err = ESRCH;
|
|
|
|
} else {
|
|
|
|
symbolInfo->symbolType = (iterContext.currentSymbolType & N_EXT) ? kQSymDyldPublicSymbol : kQSymDyldPrivateSymbol;
|
|
|
|
symbolInfo->symbolImage = qmoImage;
|
|
|
|
symbolInfo->symbolName = iterContext.currentSymbol;
|
|
|
|
symbolInfo->symbolValue = iterContext.currentSymbolValue;
|
|
|
|
symbolInfo->symbolOffset = iterContext.currentOffset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert( (err == 0) == (symbolInfo->symbolType != kQSymNoSymbol) );
|
|
|
|
assert( (symbolInfo->symbolType != kQSymNoSymbol) == (symbolInfo->symbolImage != NULL) );
|
|
|
|
assert( (symbolInfo->symbolType != kQSymNoSymbol) == (symbolInfo->symbolName != NULL) );
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern int QSymGetSymbolsForAddresses(
|
|
|
|
QSymbolsRef symRef,
|
|
|
|
size_t count,
|
|
|
|
QTMAddr addrs[],
|
|
|
|
QSymSymbolInfo symbolInfos[]
|
|
|
|
)
|
|
|
|
// See comment in header.
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
size_t addrIndex;
|
|
|
|
|
|
|
|
assert( QSymIsValid(symRef) );
|
|
|
|
assert(count > 0);
|
|
|
|
assert(addrs != NULL);
|
|
|
|
assert(symbolInfos != NULL);
|
|
|
|
|
|
|
|
// The current implementation is very naive. I could definitely make this
|
|
|
|
// faster, but see my "Performance Notes" comment at the top of this file.
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
for (addrIndex = 0; addrIndex < count; addrIndex++) {
|
|
|
|
(void) QSymGetSymbolForAddress(symRef, addrs[addrIndex], &symbolInfos[addrIndex]);
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
// NextSymbolContext is the parameter block passed to NextSymbolCallback
|
|
|
|
// via its iteratorRefCon.
|
|
|
|
|
|
|
|
struct NextSymbolContext {
|
|
|
|
QTMAddr requiredAddr;
|
|
|
|
const char * currentSymbol;
|
|
|
|
uint8_t currentSymbolType;
|
|
|
|
QTMAddr currentSymbolValue;
|
|
|
|
QTMOffset currentOffset;
|
|
|
|
};
|
|
|
|
typedef struct NextSymbolContext NextSymbolContext;
|
|
|
|
|
|
|
|
static int NextSymbolCallback(
|
|
|
|
QMOImageRef qmoImage,
|
|
|
|
const char * name,
|
|
|
|
uint8_t type,
|
|
|
|
uint8_t sect,
|
|
|
|
uint16_t desc,
|
|
|
|
QTMAddr value,
|
|
|
|
void * iteratorRefCon,
|
|
|
|
bool * stopPtr
|
|
|
|
)
|
|
|
|
// The QMOImageIterateSymbols callback for QSymGetSymbolForAddress. See
|
|
|
|
// See QMOSymbolIteratorProc for a description of the parameters.
|
|
|
|
//
|
|
|
|
// iteratorRefCon is a pointer to a NextSymbolContext structure. Initially,
|
|
|
|
// the requiredAddr and currentOffset fields are valid. As this routine
|
|
|
|
// finds better matching symbols, it fills out currentSymbol,
|
|
|
|
// currentSymbolType, and currentSymbolValue, and adjusts currentOffset.
|
|
|
|
// In this way, currentOffset slowly decreases until, after all symbols
|
|
|
|
// have been considered, NextSymbolContext contains the symbol immediately
|
|
|
|
// following the one at requiredAddr.
|
|
|
|
{
|
|
|
|
#pragma unused(sect, desc)
|
|
|
|
int err;
|
|
|
|
NextSymbolContext * iterContext;
|
|
|
|
QTMAddr symValue;
|
|
|
|
QTMOffset symOffset;
|
|
|
|
|
|
|
|
assert(qmoImage != NULL);
|
|
|
|
assert(name != NULL);
|
|
|
|
assert( stopPtr != NULL);
|
|
|
|
assert(*stopPtr == false);
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
// Ignore debugger symbols.
|
|
|
|
|
|
|
|
if ( ! (type & N_STAB) ) {
|
|
|
|
iterContext = (NextSymbolContext *) iteratorRefCon;
|
|
|
|
|
|
|
|
// Check that it's a section-based symbol. Don't want to get confused by
|
|
|
|
// undefined symbols (N_UNDF) or absolute symbols (N_ABS) or any other
|
|
|
|
// symbol type for that matter.
|
|
|
|
|
|
|
|
if (((type & N_TYPE) == N_SECT)) {
|
|
|
|
|
|
|
|
// Check that it's a better match than our current symbol.
|
|
|
|
|
|
|
|
symValue = value + QMOImageGetSlide(qmoImage);
|
|
|
|
symOffset = (symValue - iterContext->requiredAddr);
|
|
|
|
if ( (symValue > iterContext->requiredAddr) && (symOffset < iterContext->currentOffset) ) {
|
|
|
|
|
|
|
|
// If it is, record the name of the symbol and its offset.
|
|
|
|
|
|
|
|
iterContext->currentSymbol = name;
|
|
|
|
iterContext->currentSymbolType = type;
|
|
|
|
iterContext->currentSymbolValue = symValue;
|
|
|
|
iterContext->currentOffset = symOffset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern int QSymGetNextSymbol(
|
|
|
|
QSymbolsRef symRef,
|
|
|
|
const QSymSymbolInfo * symbol,
|
|
|
|
QSymSymbolInfo * nextSymbol
|
|
|
|
)
|
|
|
|
// See comment in header.
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
NextSymbolContext iterContext;
|
|
|
|
|
|
|
|
assert( QSymIsValid(symRef) );
|
|
|
|
assert(symbol->symbolType != kQSymNoSymbol);
|
|
|
|
assert(symbol->symbolImage != NULL);
|
|
|
|
assert(symbol->symbolName != NULL);
|
|
|
|
// I use symbol->symbolValue but I can't think of any meaningful validity checks.
|
|
|
|
|
|
|
|
// Iterate all of the symbols in the image looking for the one that's closet
|
|
|
|
// to symbol->symbolValue. Set currentOffset to a very large number so that
|
|
|
|
// any symbol greater than symbol->symbolValue is considered closer.
|
|
|
|
|
|
|
|
iterContext.requiredAddr = symbol->symbolValue;
|
|
|
|
iterContext.currentSymbol = NULL;
|
|
|
|
iterContext.currentSymbolType = 0;
|
|
|
|
iterContext.currentSymbolValue = 0;
|
|
|
|
iterContext.currentOffset = (QTMAddr) -1;
|
|
|
|
|
|
|
|
err = QMOImageIterateSymbols(symbol->symbolImage, NextSymbolCallback, &iterContext);
|
|
|
|
if (err == 0) {
|
|
|
|
if (iterContext.currentSymbol == NULL) {
|
|
|
|
err = ESRCH;
|
|
|
|
} else {
|
|
|
|
nextSymbol->symbolType = (iterContext.currentSymbolType & N_EXT) ? kQSymDyldPublicSymbol : kQSymDyldPrivateSymbol;
|
|
|
|
nextSymbol->symbolImage = symbol->symbolImage;
|
|
|
|
nextSymbol->symbolName = iterContext.currentSymbol;
|
|
|
|
nextSymbol->symbolValue = iterContext.currentSymbolValue;
|
|
|
|
nextSymbol->symbolOffset = iterContext.currentOffset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert( (err == 0) == (nextSymbol->symbolType != kQSymNoSymbol) );
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|