Зачем получать неправильные результаты при выполнении арифметики указателей в C на динамически связанных символах?

Я столкнулся со странной ситуацией, когда выполнение арифметики указателей с использованием динамически связанных символов приводит к неверным результатам. Я не уверен, что просто отсутствуют некоторые параметры компоновщика или это ошибка компоновщика. Может кто-нибудь объяснить, что не так в следующем примере?

Рассмотрим следующий код (lib.c) простой общей библиотеки:

#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);
}

Рассматриваемая операция является побитовой И между адресом fn1 и переменная mask, Приложение (app.c) просто звонит fn2 как это:

extern int fn2();

int main()
{
  fn2();

  return 0;
}

Это приводит к следующему выводу...

mask: ffffffff
fn1:  0x2aab43c0
p:    000003c0

... что, очевидно, неверно, потому что такой же результат ожидается для fn1а также p, Код работает на архитектуре AVR32 и компилируется следующим образом:

$ 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

Компилятор считает, что это оптимальное решение для загрузки переменнойmask в 32-битный регистр 7 и разбиение &-операции на две операции ассемблера с непосредственными операндами.

$ 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

Я предполагаю, что непосредственные операнды and инструкции не перемещаются на адрес загрузки fn1 когда общая библиотека загружается в адресное пространство приложений:

  • Это поведение преднамеренное?
  • Как я могу узнать, возникает ли проблема при связывании общей библиотеки или при загрузке исполняемого файла?

Справочная информация: это не академические вопросы. OpenSSL и LibreSSL используют схожий код, поэтому изменение исходного кода на C не допускается. Код хорошо работает на других архитектурах, и, конечно, есть неочевидная причина для выполнения побитовых операций с указателями на функции.

1 ответ

После исправления всех ошибок в коде, результат:

#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);
}

и вывод (на моем компьютере Linux 64bit):

mask: ffffffff
fn1:  0x4007c1
p:    004007c1
Другие вопросы по тегам