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と変数の間のビットごとの AND ですmask。アプリケーション ( ) は次のようapp.cに呼び出します。fn2

extern int fn2();

int main()
{
  fn2();

  return 0;
}

それは次の出力につながります...

mask: ffffffff
fn1:  0x2aab43c0
p:    000003c0

... と に対して同じ結果が期待されるため、これは明らかに正しくありませfn1p。コードは 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 にロードし、& 演算を即値オペランドを持つ 2 つのアセンブラ演算に分割することが最適なソリューションであると考え ます。

$ 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