オプション沼(19) gcc、weak symbolの真偽、続-Waddressオプション

Joseph Halfmoon

組み込みで weak symbol を目にするのは割り込み関係かもしれません。ビルド時にエラーにならないように「とりあえず」weakな奴を並べておいて、後でユーザが実体関数を書いたらそっち、みたいな。前回Waddress オプション使ったときにweak symbolについてチラッと書いてあったです。今回はその確認っと。

※『オプション沼』投稿順indexはこちら

※今回は動作確認に以下を使用しています

    • Windows11 WSL2上のUbuntu 20.04 LTS、gccのバージョンは9.4.0
Weak Symbol

Weak Symbolは関数呼び出し(とりあえず今は使わない決意)を、そのままほったらかしにしておくとリンカでエラーになってしまい肝心のオブジェクトが作ってもらえないようなときに重宝します。「とりあえず」weakな属性をシンボルに付加しておけば、実体なくてもリンカは処理してくれます。普通のシンボル(strongとは言わないな~)を別途定義すれば、weakなシンボルは「負け」て普通のシンボルの方が使われます。「後で実体関数で上書き」するのに便利です。

そんな weak についてちょっと引っかかることが書いてありました。何時も参照させていただいております “3.8 Options to Request or Suppress Warnings” ページの -Waddress オプションの項にです。1か所引用させていただきます。

the address of most functions and objects necessarily evaluates to true (the exception are weak symbols)

ちょいと引っかかったのは「例外はweak symbol共よ」という最後の一文です。だいたい上記は関数ポインタに()をつけて関数として呼び出すのでなく、関数ポインタそのものの真偽を判定するという「ちょっとディープな」コンテキストについて語る一文です。英語力が無いので上記だけみると一瞬 weak symbolへのポインタは常に false に見えるようにも読めます。そんなことないだろ~、丁寧に書けば「偽になるケースがあるのはweak symbolのときだ」とリンカ素人も思うわけであります。そこで念のために実際にコードを書いて確かめたみた、の回。

今回実験のソース

今回実験のソースは2ファイルです。

    1. weaksymbol.c
    2. sub1s.c

1番目のものは実験本体です。こんな感じ。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "sub1.h"

void __attribute__((weak)) sub1(char * arg) {
    strcpy(arg, "WEAK symbol.");
}

void __attribute__((weak)) sub2(char * arg);

int main(int argc, char const *argv[])
{
    char temp[64];
    if (sub1) {
        printf("sub1 returns true.\n");
        sub1(temp);
        printf("%s\n", temp);
    } else {
        printf("sub1 returns false.\n");
    }

    if (sub2) {
        printf("sub2 returns true.\n");
    } else {
        printf("sub2 returns false.\n");
    }

    return 0;
}

weak属性付きの関数が2個定義されてます。sub1の方はweakといいつつ、実体ありです。sub2の方はweakでかつ、実体なしのプロトタイプのみです。これを使って挙動を見ようという趣向です。

もうひとつの sub1s.c がこちら。

#include <string.h>

void sub1(char * arg) {
    strcpy(arg, "NOT A WEAK!");
}

こちらはweakでないフツーの関数です。上記の weaksymbol.c 内で定義されているweakな関数 sub1とお名前が被ってます。

ビルドして実行

まず、「weakだけ」な方からコンパイルしてみます。CompileWeak

weak属性ついているので、実体のないsub2のような定義でもOKです。

生成されたオブジェクトを走らせてみると、こんな感じ。weakRuns

weak シンボルでも実体のあるsub1は、真偽値は真と判定され、呼び出された結果、WEAK symbolだと自己申告してます。実体のない sub2の方は偽と判定されてます。ま、これが確かめたかったことですかな。

続いて、「sub1がweakでない」場合、この場合はsub1.cを追加してやるだけでOKっす。CompleNotAweak

weaksymbol.c 内にsub1()関数のweakな定義があるのでそれに便乗し、プロトタイプなどは不要っす。実行するとこんな感じ。notAweakRuns

リンカは、sub1s.c側の実体関数とリンクしてくれるので、sub1が真であるだけでなく、呼び出し結果は WEAKじゃない、ということです。依然、実体のない sub2の方は偽と判定されてます。

これで weak ラベルも完璧か?ホントか?

オプション沼(18) gccの-Waddressオプション、警告オプションその1 へ戻る

オプション沼(20) gcc、-Warray-bounds、最適化のときは世話焼きになる へ進む