6

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

考虑以下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 源代码。该代码在其他架构上运行良好,当然有一个不明显的原因对函数指针进行按位操作。

4

1 回答 1

0

在更正代码中的所有'slopiness'之后,结果是:

#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 64 位计算机上)是:

mask: ffffffff
fn1:  0x4007c1
p:    004007c1
于 2016-11-27T01:27:09.440 回答