while (1) は GCC を happy にする - "The loop is added only to keep gcc happy"

とある Linux コンテナで動くプロセスを GDBデバッグしている最中に glibc の関数を追っていて、以下のようなコードを見つけました

(gdb) l
156 void
157 __libc_fatal (const char *message)
158 {
159   /* The loop is added only to keep gcc happy.  */
160   while (1)
161     __libc_message ("%s", message);
162 }

はて ... ?

  • The loop is added only to keep gcc happy. とは?
  • この while(1) はなんのためにあるのだ?

わからんので エイっと AI に聞いてみました。

以下は Microsoft 365 Copilot Chat の回答です。他の AI 回答も予々一緒で、妥当っぽい回答な気がする。


この while (1) ループは、意図的に無限ループにしてプログラムを停止させるために使われています。 関数 __libc_fatal は、glibcGNU Cライブラリ)の内部で致命的なエラーが発生したときに呼ばれる関数です。以下のような目的があります:

目的と意味

  1. 致命的エラーの通知 __libc_message("%s", message); によって、エラーメッセージを表示します。

  2. プログラムの停止 while (1) によって、関数から戻らず、プログラムを永久に停止させます。これは、致命的なエラーが発生した後に、処理を続けることが危険であるためです。

  3. 最適化回避

コメントにある「The loop is added only to keep gcc happy.」は、GCCGNU Compiler Collection)が最適化の際にこの関数を「戻る可能性がある」と誤認しないようにするためです。無限ループにすることで、コンパイラに「この関数は絶対に戻らない」と明示できます。

補足

通常、こうした関数は __attribute__((noreturn)) を使って「戻らない関数」として宣言されますが、念のために while (1) を入れておくことで、コンパイラの警告や最適化の問題を防いでいます。

glibc のソースへのリンク

upsteream である glibc 2.34 へのリンクを貼っておきます 1

github.com

ソース

void
__libc_fatal (const char *message)
{
  /* The loop is added only to keep gcc happy.  */
  while (1)
    __libc_message (do_abort, "%s", message);
}
libc_hidden_def (__libc_fatal)

abort() の文字列を見て過去に書いた記事を思い出す

__libc_message() は内部で abort() を呼び出すようです。

/* Abort with an error message.  */
void
__libc_message (enum __libc_message_action action, const char *fmt, ...)
{


... 略


  if ((action & do_abort))
    /* Kill the application.  */
    abort ();
}

そういえば、ずいぶん前に abort() の実装を追って調べて、テックブログにまとめたのを思い出しました。

tech.pepabo.com

abort() にも while(1) で HLT 命令を呼び出すコードがありますね ! 詳細はブログ読んでね (宣伝)

  while (1)
    /* Try for ever and ever.  */
    ABORT_INSTRUCTION; 

  1. ⚠️ gdb で調べていた glibcglibc-2.34-196.amzn2023.0.1.x86_64 でした。Amazon Linux 向けのパッチが当たっているのか、__libc_message() の do_abort の引数がありません。本筋には関係ないので詳細は調べていません