まず第一に、#include
ステートメントは とは何の関係もありませんlinking
。#
前に inC
があるものはすべて、コンパイラやリンカーではなく、プリプロセッサを対象としていることを忘れないでください。
しかし、それは機能がリンクされなければならないということですよね?
別々の手順で手順を実行しましょう。
$ gcc -c -Werror --std=c99 st.c
st.c: In function ‘main’:
st.c:5:22: error: implicit declaration of function ‘isdigit’ [-Werror=implicit-function-declaration]
printf("%d %d\n",isdigit(48),isdigit(48.4));
^
cc1: all warnings being treated as errors
ご覧のとおり、gcc の lint (静的アナライザー) が動作しています。
それを無視して何を進めようとも...
$ gcc -c --std=c99 st.c
st.c: In function ‘main’:
st.c:5:22: warning: implicit declaration of function ‘isdigit’ [-Wimplicit-function-declaration]
printf("%d %d\n",isdigit(48),isdigit(48.4));
今回は警告のみ。これで、現在のディレクトリにオブジェクト ファイルができました。調べてみよう…
$ nm st.o
U isdigit
0000000000000000 T main
U printf
ご覧のとおり、printf
とisdigit
は未定義としてリストされています。コードはどこかから来なければなりませんね。
リンクを進めましょう...
$ gcc st.o
$ nm a.out | grep 'printf\|isdigit'
U isdigit@@GLIBC_2.2.5
U printf@@GLIBC_2.2.5
ご覧のとおり、状況はわずかに改善されています。彼らがいたように、無力な孤独ではisdigit
ありません。両方の機能が によって提供されていることがわかります。しかし、それはどこですか?printf
st.o
GLIBC_2.2.5
GLIBC
さて、最終的な実行可能ファイルをもう少し調べてみましょう...
$ ldd a.out
linux-vdso.so.1 => (0x00007ffe58d70000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb66f299000)
/lib64/ld-linux-x86-64.so.2 (0x000055b26631d000)
あはは…ありますねlibc
。したがって、指示はありませんが、リンカーはデフォルトで 3 つのライブラリとリンクしています。そのうちの 1 つは と のlibc
両方を含むprintf
ですisdigit
。
次の方法で、リンカーのデフォルトの動作を確認できます。
$gcc -dumpspec
*link:
%{!r:--build-id} %{!static:--eh-frame-hdr} %{!mandroid|tno-android-ld:%{m16|m32|mx32:;:-m elf_x86_64} %{m16|m32:-m elf_i386} %{mx32:-m elf32_x86_64} --hash-style=gnu --as-needed %{shared:-shared} %{!shared: %{!static: %{rdynamic:-export-dynamic} %{m16|m32:-dynamic-linker %{muclibc:/lib/ld-uClibc.so.0;:%{mbionic:/system/bin/linker;:/lib/ld-linux.so.2}}} %{m16|m32|mx32:;:-dynamic-linker %{muclibc:/lib/ld64-uClibc.so.0;:%{mbionic:/system/bin/linker64;:/lib64/ld-linux-x86-64.so.2}}} %{mx32:-dynamic-linker %{muclibc:/lib/ldx32-uClibc.so.0;:%{mbionic:/system/bin/linkerx32;:/libx32/ld-linux-x32.so.2}}}} %{static:-static}};:%{m16|m32|mx32:;:-m elf_x86_64} %{m16|m32:-m elf_i386} %{mx32:-m elf32_x86_64} --hash-style=gnu --as-needed %{shared:-shared} %{!shared: %{!static: %{rdynamic:-export-dynamic} %{m16|m32:-dynamic-linker %{muclibc:/lib/ld-uClibc.so.0;:%{mbionic:/system/bin/linker;:/lib/ld-linux.so.2}}} %{m16|m32|mx32:;:-dynamic-linker %{muclibc:/lib/ld64-uClibc.so.0;:%{mbionic:/system/bin/linker64;:/lib64/ld-linux-x86-64.so.2}}} %{mx32:-dynamic-linker %{muclibc:/lib/ldx32-uClibc.so.0;:%{mbionic:/system/bin/linkerx32;:/libx32/ld-linux-x32.so.2}}}} %{static:-static}} %{shared: -Bsymbolic}}
他の 2 つのライブラリは何ですか?
を掘り下げたときのことをよく覚えておいa.out
てください。つまり、これらのシンボルに関連付けられたアドレスはありませんでした。printf
isdigit
U
memory
実際には、これが魔法の場所です。これらのライブラリは、古いシステムのようにリンク時ではなく、実行時に実際にロードされました。
それはどのように実装されていますか?まあ、遅延リンクのような専門用語が関連付けられています。それが行うことは、プロセスが関数を呼び出すときに、メモリアドレス(TEXTセクション)がない場合、Trap
(制御が言語エンジンに渡されるときに、高級言語の専門用語で例外のようなもの)を生成することです。カーネルはそのようなものをインターセプトTrap
し、それを動的ローダーに渡します。動的ローダーはライブラリをロードし、関連するメモリ アドレスを呼び出し元プロセスに返します。
物事を怠惰に行う方が事前に行うよりも優れているという理論的な理由は複数あります。これはまったく新しいトピックだと思いますが、それについては別の機会に説明します。