2
votes

Short Version: It is possible to go over all ELF "section" headers during runtime and get relocated address of every "section" header for every loaded shared library.

Long Version: I'm trying to implement the same mechanism for dynamic debug in userspace as it exists in kernel (dyn_debug). The way it works that every LOG macro instance creates static variable in specific "section" in the program __attribute__((section("__verbose"))) This forces compiler to put the variable not in ".data" section but in "__verbose" section. Later start and stop address of this section can be accessed through variables __start___verbose, __stop___verbose. This way some central routine can go over all registered "log" entries and change attributes on demand. This works find for statically linked executable, but when using shared libraries there are several "__verbose" sections (one per each shared library) and one in the executable itself. (I am using -fPIC flag of course in order to be included in the library) Also everything in linked with "-export-dynamic" in order to ensure all symbols are exported.

Each shared library and main executable has attribute(constructor) method for init.

I had observed two different behaviors in 2 cases.

case1: For first case libraries are not loaded with ldopen but by the "libc loader"

  1. referencing __start___verbose always returns same address (that of the main executable), where only "main" executable logging entries are present.

  2. dlsym(RTLD_NEXT, __start___verbose), returns address of the symbol of the "next" resolvable library, so in fact i get all addresses.

case 2: Loading library with ldopen

  1. referencing __start___verbose always returns same address (that of the main executable)
  2. dlsym(RTLD_NEXT, __start___verbose) return NULL.
  3. dlsym(RTLD_DEFAULT, __start___verbose) return "main" process table.
  4. dlsym(handle, __start___verbose) - returns correct section address

Question: Is there any way for library opened with ldopen to obtain that symbol, besides 4, as 4 requires explicit call from the "loader"

Code:

/* Main" */ 
void func1()
{
    static int attribute__((section("__verbose"))) var  = 1;
}

/* Shared library */
void func2()
{
    static int attribute__((section("__verbose"))) var  = 2;
}

/* Both in main and shared library 
 * Prints same address !!! BAD !! */
void __attribute__((constructor)) initializer()
{
    struct int *iter;

    for (iter = __start___verbose; iter != __stop___verbose; ++iter) {
        printf("Value is %d", *iter)
    }
}



/* Works for libraries opened by libc runtime.
 * Does not work for libraries opened with LDOPEN*/
 void __attribute__((constructor)) initializer()
 {
    struct int *iter = ;

    for (iter = dlsym(RTLD_NEXT, "__start___verbose"); iter != __stop___verbose; ++iter) {
        printf("Value is %d", *iter)
    }
}



/* Snippet for main doing dynamic loading */

handle = ldopen('path', RTLD_NOW)
iter = dlsym(handle, "__start___verbose")
for (; iter != __stop___verbose; ++iter) {
    printf("Value is %d", *iter)
}
1
Do you do anything special to have the __start___verbose and __stop___verbose variables because I don't have them. - ysdx
Don't you want to initialize your variables in a (high priority) constructor of the ELF file? Otherwise, they won't be initialized when executing the code of the constructors. - ysdx

1 Answers

1
votes

You are not supposed to access section informations at runtime. Sections are not supposed to be used at run-time and are expected to be possibly removed (stripped) from the executable.

I'd might use a custom linker script with:

.__verbose:
{
  PROVIDE_HIDDEN (__verbose_start = .);
  *(.__verbose)
  PROVIDE_HIDDEN (__verbose_end = .);
}

This defines HIDDEN symbols for the section so each ELF file will have its own version of those symbols.

A constructor (or some other code) in the ELF file can then use them:

struct foo*;
extern struct foo* __verbose_start __attribute__((visibility("hidden")));
extern struct foo* __verbose_stop __attribute__((visibility("hidden")));

void __attribute__((constructor)) initializer()
{
   initialize_logging(__verbose_start,__verbose_stop);  
}