Why getting wrong results when performing pointer arithmetic in C on dynamically linked symbols?

321 views Asked by At

I encountered a weird situation where performing pointer arithmetic involving dynamically linked symbols leads to incorrect results. I'm unsure if there are simply missing some linker parameters or if it's a linker bug. Can someone explain what's wrong in the following example?

Consider the following code (lib.c) of a simple shared library:

#include <inttypes.h>
#include <stdio.h>

uintptr_t getmask()
{
  return 0xffffffff;
}

int fn1() 
{
  return 42;
}

void fn2() 
{
  uintptr_t mask; 
  uintptr_t p;

  mask = getmask();
  p = (uintptr_t)fn1 & mask; 
  printf("mask: %08x\n", mask);
  printf("fn1:  %p\n",   fn1); 
  printf("p:    %08x\n", p);
}

The operation in question is the bitwise AND between the address of fn1 and the variable mask. The application (app.c) just calls fn2 like that:

extern int fn2();

int main()
{
  fn2();

  return 0;
}

It leads to the following output ...

mask: ffffffff
fn1:  0x2aab43c0
p:    000003c0

... which is obviously incorrect, because the same result is expected for fn1 and p. The code runs on an AVR32 architecture and is compiled as follows:

$ avr32-linux-uclibc-gcc -Os -Wextra -Wall -c -o lib.o lib.c
$ avr32-linux-uclibc-gcc -Os -Wextra -Wall -shared -o libfoo.so lib.o
$ avr32-linux-uclibc-gcc -Os -Wextra -Wall -o app app.c -L. -lfoo

The compiler thinks, it is the optimal solution to load the variable mask into 32 bit register 7 and splitting the &-operation into two assembler operations with immediate operands.

$ avr32-linux-uclibc-objdump -d libfoo.so

000003ce <fn1>:
 3ce:   32 ac           mov     r12,42
 3d0:   5e fc           retal   r12

000003d2 <fn2>:
 ...
 3f0:   e4 17 00 00     andh    r7,0x0
 3f4:   e0 17 03 ce     andl    r7,0x3ce

I assume the immediate operands of the and instructions are not relocated to the loading address of fn1 when the shared library is loaded into the applications address space:

  • Is this behaviour intentional?
  • How can I investigate whether problem occurs when linking the shared library or when loading the executable?

Background: This is not an academic questions. OpenSSL and LibreSSL use similar code, so changing the C source is not an option. The code runs well on other architectures and certainly there is an unapparent reason for doing bitwise operations on function pointers.

1

There are 1 answers

3
user3629249 On

after correcting all the 'slopiness' in the code, the result is:

#include <inttypes.h>
#include <stdio.h>

int fn1( void );
void fn2( void );
uintptr_t getmask( void );

int main( void )
{
  fn2();

  return 0;
}

uintptr_t getmask()
{
  return 0xffffffff;
}

int fn1() 
{
  return 42;
}

void fn2() 
{
  uintptr_t mask; 
  uintptr_t p;

  mask = getmask();
  p = (uintptr_t)fn1 & mask; 
  printf("mask: %08x\n", (unsigned int)mask);
  printf("fn1:  %p\n",   fn1); 
  printf("p:    %08x\n", (unsigned int)p);
}

and the output (on my linux 64bit computer) is:

mask: ffffffff
fn1:  0x4007c1
p:    004007c1