2015/04/25

[gcc?]本体側による共有ライブラリ側extern変数のオーバーライド

言葉で説明すると、こんな感じのソースコードに出会った。

Linuxで、プロセス側のソースコードに定義してあるグローバル変数を共有ライブラリでexternして参照している。

ifdefとかがいっぱいあるので深く追っていないのだが、grepしただけだと値渡しもアドレス渡しもしてるように見えない。
そんなことできるんだっけ?と思って新しくグローバル変数をプロセス側に追加し、共有ライブラリでexternしてみたけど、コンパイルは通るがリンクで未定義になる。
うん、そうだよな。
でも既存の部分は動いているようだ。
なんなんだ、これは??


よくわからんので「共有ライブラリ グローバル変数」みたいなキーワードで検索。
出てくるのは、メモリの確保はプロセス側でやるのだよ、みたいなことばかりだったが、ようやく見つけた。

いまさらC言語のexternで悩む | ビットログ

そうそう、こんな感じ。
これは同じコンパイル単位だけど、それが共有ライブラリでも起きているという感じ。
試してみよう。

 

shared_lib.h
#ifndef SHARED_LIB_H
#define SHARED_LIB_H
extern int gValues[];
extern int gSize;
int shared_lib_func(int *p_size);
#endif /* SHARED_LIB_H */

 

shared_lib.c

#include <stdio.h>
#include "shared_lib.h"
int gValues[] = { 0 };
int gSize = sizeof(gValues) / sizeof(gValues[0]);
int shared_lib_func(int *p_size)
{
    *p_size = gSize;
    return gValues[0];
}

 

main.c

#include <stdio.h>
#include "shared_lib.h"
int gValues[] = { 1, 2, 3 };
int gSize = sizeof(gValues) / sizeof(gValues[0]);
int main(int argc, char *argv[])
{
    int size = 0;
    int ret = shared_lib_func(&size);
    printf("ret = %d / size = %d\n", ret, size);
    return 0;
}

 

$ gcc -o libshared.so -fPIC -shared shared_lib.c
$ gcc -o tst main.c -L. -lshared
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
$ ./tst
ret = 1 / size = 3

ほう!
本体も共有ライブラリもgValues[]を持っているけど、本体側の方が優先されているのだ。
感触としては、WEAKの動作と似ている。
hiro99ma blog: [nrf51]app_error_handler()はWEAK

気持ちとしては「共有ライブラリ側にあるグローバル変数なんて本体側は知らないだろうから、本体側を優先させるのが筋よね」なんだろうが、共有ライブラリの動作も変わるとなると、なんかすごいな。

まあ、普通はグローバル変数をさらけだすなんてことはないから、今回見たコードもわざとやってるんだとは思うのだけど、わかりにくいよね・・・。
テクニックとして使っているつもりだったらコメントくらい入れてほしいし、それよりも普通にポインタをもらうような実装にしてもらった方がメンテナンス性はよいのだけどね。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。

注: コメントを投稿できるのは、このブログのメンバーだけです。