comp.unix.programmer

Re: Get address of function by name at runtime?


gordonb.pxq4j_at_burditt.org (Gordon Burditt) writes:
>> Can something like this be done in Solaris or Linux? i.e. Find the 
>> address of a function from a string variable and call it dynamically at 
>> run time?
>> 
>> void main(void)
>
>Friends don't let friends void main.
>
>There's always the lame, inelegant, slow, but very portable approach:
>make a symbol table with the name of the function in ASCII and a
>function pointer.  Then look up the name and use the function
>pointer.
>
>One of the problems with what you want to do, especially if you had
>any ideas of a user typing in the function name, is that there are
>so many functions that you likely don't want the user to type in,
>like main, fread, abort, fork, or signal, or which don't have
>compatible arguments.  You might end up needing a symbol table
>anyway to figure out what arguments the function needs.
>
>If you put all the functions you would want in a shared object
>linked to with dlopen(), you might avoid fancy undocumented tricks
>with dlopen() and manage to stick to dlopen() the main program
>itself.

One could compile and link with -g, which will include the debugger
symbol table (the DWARF sections in the ELF executable).  Then one
could open the executable with open("/proc/self/exe") (on linux,
slightly different on SVR4 or Solaris), and read and process the
dwarf information.    Beware, it is a dense encoding and quite
complicated to decode.

If the symbols you need are in a shared object, you'll need to build that
with -g and open and read (again via /proc) the libraries then process
the DWARF sections.

A more traditional approach that I used when I wrote the linux kernel
debugger (KDB) was to post-process the executable (linux) with a script
which extracted the symbols (using nm) and created an additional C file
that mapped the symbol names and addresses; when compiled the output
gets linked into the executable.

-----[ Begin KSH script gensyms.ksh ]-------------------------------------
#!/bin/ksh -p
#
#  Create an object section containing a symbol table.
#
#  usage:
#       gensyms.ksh elf generated-c-file

_elf=${1}

_tmpfile=${TMPDIR:-/tmp}/gensym.nm.$$
_ksym_c=${TMPDIR:-/tmp}/gensym.c1.$$
_ksym_d=${TMPDIR:-/tmp}/gensym.c2.$$

trap "rm -f ${_tmpfile} ${_ksym_c} ${_ksym_d} " EXIT HUP INT QUIT TERM

nm -n ${_elf} > ${_tmpfile}

echo "#include " > ${_ksym_c}
echo "#include " >> ${_ksym_c}
echo "struct _sym _syms[] __attribute__((section(\"symtab\"))) = {"                 >> ${_ksym_c}

echo ""  > ${_ksym_d}
echo "const char _symstrings[] __attribute__((aligned(1),section(\"symstrings\"))) = " >> ${_ksym_d}

typeset -i _offset=0
while read _address _type _symbol
do
    typeset -i _len
    _len=$(echo -n "${_symbol}" | wc -c)

    echo "{ 0x${_address}, ${_offset}, 0 }," >> ${_ksym_c}
    echo "      \"${_symbol}\\0\"" >> ${_ksym_d}

    let _offset=_offset+_len+1

done < ${_tmpfile}

echo "};" >> ${_ksym_c}
echo ";" >> ${_ksym_d}

cat ${_ksym_c} ${_ksym_d} > ${2}

exit 0
--------[ End KSH Script ]------------------


For this, you compile and link twice.   The first time to generate the
symbol table, the second to include the symbol table in the executable:

--------[ Begin Makefile Target ]-----------

$(EXE):        exe_version buildit $(LIBRARIES) $(SUBDIRS)
        _at_echo " PRELINK $_at_"
        $(HUSHCOMPILE)ld            -T build/exe.ld            -o $(ELF)            boot/$(OBJDIR)/setup64.o            $(EXE_LIBRARIES) $(EXE_LIBRARIES)           $(LIBGCC)
        _at_echo " GENSYMS"
        $(HUSHCOMPILE)tools/gensyms.ksh $(ELF) $(GENSYMC)
        _at_echo " COMPILE $(GENSYMC)"
        $(HUSHCOMPILE)cc -c $(CFLAGS) -o $(GENSYMO) $(GENSYMC)
        _at_echo " LINK $_at_"
        $(HUSHCOMPILE)ld -M            -T build/exe.ld            -o $(ELF)            boot/$(OBJDIR)/setup64.o            $(GENSYMO)            $(EXE_LIBRARIES) $(EXE_LIBRARIES)            $(LIBGCC)                 > $(EXEMAP)

-------[ End Makefile Target ]--------------

Add to your 'exe.ld' file for the linker:

-------[ Begin LD file fragment ]-----------

    . = ALIGN(64);
    _ssymtab = .;
    symtab : {
        *(symtab)
    }
    _esymtab = .;
    _ssymstrings = .;
    symstrings : {
        *(symstrings)
    }
    _esymstrings = .;

-------[ End LD file fragment ]-------------

With this in your linker control file (along with the rest of
the control file contents), this will include the symbol table
in your executable the second time through:  Four global symbols
are defined that are accessible to your program:

  _ssymtab, _esymtab, _ssymstring, _esymstrings


-------[ debugger/symbols.h ]-----------------

struct _sym {
    uint64      s_address;      // Absolute symbol address
    uint32      s_offset;       // Offset in char _symstrings[] of symbol name
    uint32      s_flags;        // Flags
};

-------[ End debugger/symbols.h ]-------------

_ssymtab points to the start of an array of struct _sym entries for each
symbol.   _esymtab points to the entry following the end of the array.
_ssymstrings points to the start of the symbol strings, _esymstrings
points to one entry past the symbol strings.

scott



Written by scott_at_slp53.sl.home (Scott Lurndal) 15/10/2011 23.33.57
Check some pics on this site!
23/05/2012 22.33.46