動的にリンクされたシンボルを含むポインター演算を実行すると、誤った結果が生じるという奇妙な状況に遭遇しました。単にいくつかのリンカ パラメータが欠落しているのか、それともリンカのバグなのかはわかりません。次の例の何が問題なのか誰か説明できますか?
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
... と に対して同じ結果が期待されるため、これは明らかに正しくありませ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 にロードし、& 演算を即値オペランドを持つ 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 ソースを変更することはできません。このコードは他のアーキテクチャでも問題なく動作しますが、関数ポインタに対してビット単位の操作を行う理由は明らかではありません。