为什么在C上对动态链接符号执行指针运算时出错?

ham*_*ari 6 c function-pointers dynamic-linking linux-toolchain avr32

我遇到了一个奇怪的情况,即执行涉及动态链接符号的指针算法会导致错误的结果.我不确定是否只是缺少一些链接器参数或者它是否是链接器错误.有人能解释下面的例子中有什么问题吗?

考虑以下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);
}
Run Code Online (Sandbox Code Playgroud)

有问题的操作是地址fn1和变量之间的按位AND mask.application(app.c)只是这样调用fn2:

extern int fn2();

int main()
{
  fn2();

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

它导致以下输出......

mask: ffffffff
fn1:  0x2aab43c0
p:    000003c0
Run Code Online (Sandbox Code Playgroud)

......这显然是不正确的,因为预期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
Run Code Online (Sandbox Code Playgroud)

编译器认为,将变量加载mask到32位寄存器7并将&-operation分成两个具有立即操作数的汇编操作是最佳解决方案 .

$ 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
Run Code Online (Sandbox Code Playgroud)

我假设and指令的立即操作数不会重定位到fn1共享库加载到应用程序地址空间时的加载地址:

  • 这种行为是故意的吗?
  • 如何在链接共享库或加载可执行文件时调查是否出现问题?

背景:这不是学术问题.OpenSSL和LibreSSL使用类似的代码,因此不能更改C源代码.代码在其他体系结构上运行良好,当然在函数指针上进行按位运算有一个不明显的原因.

use*_*249 0

纠正代码中的所有“错误”后,结果是:

#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);
}
Run Code Online (Sandbox Code Playgroud)

输出(在我的 linux 64 位计算机上)是:

mask: ffffffff
fn1:  0x4007c1
p:    004007c1
Run Code Online (Sandbox Code Playgroud)