How to find out the base address of the shared library

5,690 views
Skip to first unread message

Peter

unread,
Nov 11, 2010, 12:48:02 PM11/11/10
to android-ndk
Hi,

I want to programmatically retrieve the call stack for debuging
purpose. As the function "backtrace()" doesn't exist, I struggled a
bit but found a way: using "_Unwind_Backtrace()" from unwind.h I can
go through the stack and save the pointer in an array.

This is working fine for a standalone application. The intention is to
send the callstack over network for each memory allocation, so a
desktop tool can analyse the data and find memory leaks.

In a shared library like it is needed when you use the JNI interface,
I face a problem: The addresses are absolute values, but in the symbol
list (generated by "nm") I have addresses which are not relocated yet.
Before sending over the network, I should subtract the base address. I
tried following ideas:

- dladdr() would give me the load address. But I didn't find this on
Android
- dl_iterate_phdr() gives access to all loaded modules, including
dlpi_addr which seems to be my desired value
- /proc/self/maps would also contain the base address of the library,
but it looks like some work is needed to parse it, which I would like
to avoid at the moment

Is there any function which gives me this base address quite painless?
How is actually addr2line doing it?

Peter

Phil Endecott

unread,
Nov 13, 2010, 7:38:07 PM11/13/10
to android-ndk
Hi Peter,

If you've not found a better solution yet, here's a suggestion:

Copy the linker script used for making .so files from build/prebuilt/
linux-x86/arm-eabi-4.4.0/arm-eabi/lib/ldscripts/armelf.xsc to
somewhere local.
Hack your build system to use it (-Xlinker --script=linkscript).
Make sure everything still works.

Now add something like this to the linkscript:

PROVIDE ( start_of_so = 0 );

It doesn't much matter where you put it. Somewhere near the other
symbols defined in the same way would be OK.

Now in your code, just:

extern char* start_of_so;

I have checked this to the extent of verifying that when I read that
variable I get a non-zero value, so either it has worked and 0 has
been relocated by the link-loader, or it is garbage.


Good luck!

alan

unread,
Nov 15, 2010, 4:43:18 AM11/15/10
to android-ndk
Or just check the address of any exported symbol when your library
loads. Then by taking the difference between the runtime address and
the library address you can get the load address for the library

On Nov 14, 12:38 am, Phil Endecott <spam_from_goo...@chezphil.org>
wrote:

Peter

unread,
Nov 15, 2010, 8:30:09 AM11/15/10
to android-ndk
Phil, Alan,

this sounds like a really good idea! It's still not completely
automatic though, but a step forward.
So I could make a static int variable which is the amount I have to
subtract, and I can even use the address of itself to determine the
load address of the library. So I have to supply the program with the
symbol address (from nm) with a parameter or so. Or maybe reading from
a little file.

Peter

alan

unread,
Nov 15, 2010, 9:48:35 AM11/15/10
to android-ndk
Cant you just send the stack addresses and the address of the special
variable over the network and leave it up to the desktop app to lookup
the addresses?

David Turner

unread,
Nov 15, 2010, 10:54:35 AM11/15/10
to andro...@googlegroups.com
Very frankly, parsing /proc/<pid>/smaps is *not* a lot o f work. But maybe that's just me... :-/

size_t  get_library_address(const char*  libname)
{
    char path[256];
    char buff[256];
    int len_libname = strlen(libname);
    FILE* file;
    size_t  addr = 0;

    snprintf(path, sizeof path, "/proc/%d/smaps", getpid());
    file = fopen(path, "rt");
    if (file == NULL)
        return 0;

    while (fgets(buff, sizeof buff, file) != NULL) {
        int  len = strlen(buff);
        if (len > 0 && buff[len-1] == '\n') {
            buff[--len] = '\0';
        }
        if (len <= len_libname || memcmp(buff + len - len_libname, libname, len_libname)) {
            continue;
        }
        size_t start, end, offset;
        char flags[4];
        if (sscanf(buff, "%zx-%zx %c%c%c%c %zx", &start, &end,
                   &flags[0], &flags[1], &flags[2], &flags[3], &offset) != 7) {
            continue;
        }
        if (flags[0] != 'r' || flags[2] != 'x') {
            continue;
        }
        addr = start - offset;
        break;
    }
    fclose(file);
    return addr;
}



--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To post to this group, send email to andro...@googlegroups.com.
To unsubscribe from this group, send email to android-ndk...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/android-ndk?hl=en.


Peter

unread,
Nov 15, 2010, 12:09:12 PM11/15/10
to android-ndk
Wow, this would help a lot! I will give it a try!

In smaps (actually I just knew maps, but seems to be the same), I find
two rows:
81000000-81bf2000 r-xp 00000000 1f:01 686 /.../lib.so
81bf2000-81c33000 rwxp 00bf1000 1f:01 686 /.../lib.so

So if I understand your program right, it will take a row if the name
matches and the flags are R and X. In this case both would match and
the first one will be taken (because you break out of the loop). I
guess a segment with code is always read-only, so maybe a test on R-X
would be more reliable?

Peter Holtwick

unread,
Jan 13, 2011, 8:03:21 AM1/13/11
to android-ndk
I implemented and adapted the solution of David Turner and it works
like a charm!

In order to let other people benefit from this, I will post below my
complete solution to get the callstack at runtime. This way I could
track memory allocations and find memory leaks. I just replaced own
typedef'd datatypes back to 'unsigned int' etc, hopefully without
mistake :)



#ifndef ANDROID_CALL_STACK_H
#define ANDROID_CALL_STACK_H

/**
@file CallStack_Android.h

@brief Getting the callstack under Android
@author Peter Holtwick
*/

#include <unwind.h>
#include <android/log.h>
#include <stdio.h>
#include <string.h>

namespace {

struct CallStackSaver
{
unsigned int* const m_PtrArr;
const unsigned short m_MaxFrames;
unsigned short m_CurrFrame;
static unsigned int m_LibraryAdjustment;

CallStackSaver( unsigned int pFrames[], unsigned short maxFrames )
:m_PtrArr( pFrames )
,m_MaxFrames( maxFrames )
,m_CurrFrame( 0 )
{
if( m_LibraryAdjustment == -1 ) {
m_LibraryAdjustment = GetLibraryAddress();
__android_log_print( ANDROID_LOG_DEBUG,
"CallStackSaver()", "Setting library adjustment to:
%p", m_LibraryAdjustment );
}
}

unsigned short
SaveCallStack()
{
_Unwind_Reason_Code res = _Unwind_Backtrace( &CallbackFunc,
this );
return m_CurrFrame; // number of collected frames
}

static _Unwind_Reason_Code
CallbackFunc( _Unwind_Context* ctx, void* a )
{
CallStackSaver* pThis = reinterpret_cast<CallStackSaver*>(a);
pThis->SaveAddress( _Unwind_GetIP(ctx) );
return _URC_OK;
}


void
SaveAddress( unsigned int rawAddr )
{
if( m_CurrFrame < m_MaxFrames ) {
m_PtrArr[ m_CurrFrame ] = rawAddr - m_LibraryAdjustment;
++m_CurrFrame;
}
}

unsigned int
GetLibraryAddress()
{
FILE* file = fopen( "/proc/self/maps", "rt");
if( file==NULL ) {
return 0;
}

unsigned int addr = 0;
const char* libraryName = "libMyLibraryName.so";
int len_libname = strlen(libraryName);

char buff[256];
while( fgets(buff, sizeof buff, file) != NULL )
{
int len = strlen(buff);
if( len > 0 && buff[len-1] == '\n' ) {
buff[--len] = '\0';
}
if (len <= len_libname || memcmp(buff + len - len_libname,
libraryName, len_libname)) {
continue;
}

unsigned int start, end, offset;
char flags[4];
if( sscanf( buff, "%zx-%zx %c%c%c%c %zx", &start, &end,
&flags[0], &flags[1], &flags[2], &flags[3],
&offset ) != 7 )
{
continue;
}

if( flags[0]=='r' && flags[1]=='-' && flags[2]=='x' ) {
addr = start - offset;
break;
}
} // while

fclose(file);
return addr;
}

};
// static
unsigned int CallStackSaver::m_LibraryAdjustment = -1;



} // namespace


unsigned short GetCallStackSnapshot( unsigned short dwMaxFrames,
unsigned int lpFrames[] )
{
CallStackSaver saver( lpFrames, dwMaxFrames );
return saver.SaveCallStack(); // number of collected frames
}


#endif // ANDROID_CALL_STACK_H
Reply all
Reply to author
Forward
0 new messages