Зачем получать неправильные результаты при выполнении арифметики указателей в 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