真実はなぜそんなに偽ですか?

真実はなぜそんなに偽ですか?

readいくつかの共通コマンド(例:truefalse

まあ、確かにバイナリファイルです。

sh-4.2$ which true
/usr/bin/true
sh-4.2$ which false
/usr/bin/false
sh-4.2$ file /usr/bin/true
/usr/bin/true: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=2697339d3c19235
06e10af65aa3120b12295277e, stripped
sh-4.2$ file /usr/bin/false
/usr/bin/false: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=b160fa513fcc13
537d7293f05e40444fe5843640, stripped
sh-4.2$

しかし、最も驚いたのはその大きさでした。trueisは基本的にjust exit 0and falseisなので、それぞれ数バイトしかないと予想しましたexit 1

sh-4.2$ true
sh-4.2$ echo $?
0
sh-4.2$ false
sh-4.2$ echo $?
1
sh-4.2$

しかし、両方のファイルのサイズが28KBを超えるという事実に驚きました。

sh-4.2$ stat /usr/bin/true
  File: '/usr/bin/true'
  Size: 28920           Blocks: 64         IO Block: 4096   regular file
Device: fd2ch/64812d    Inode: 530320      Links: 1                     
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2018-01-25 19:46:32.703463708 +0000
Modify: 2016-06-30 09:44:27.000000000 +0100
Change: 2017-12-22 09:43:17.447563336 +0000
 Birth: -
sh-4.2$ stat /usr/bin/false
  File: '/usr/bin/false'
  Size: 28920           Blocks: 64         IO Block: 4096   regular file
Device: fd2ch/64812d    Inode: 530697      Links: 1                     
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2018-01-25 20:06:27.210764704 +0000
Modify: 2016-06-30 09:44:27.000000000 +0100
Change: 2017-12-22 09:43:18.148561245 +0000
 Birth: -
sh-4.2$

だから私の質問は:なぜそれほど大きいのですか?戻りコードに加えて、実行可能ファイルには何がありますか?

PS:私はRHEL 7.4を使用しています。

ベストアンサー1

過去には実際に/bin/trueシェル/bin/falseにスクリプトがありました。

たとえば、PDP/11 Unixシステム7では、次のようになります。

$ ls -la /bin/true /bin/false
-rwxr-xr-x 1 bin         7 Jun  8  1979 /bin/false
-rwxr-xr-x 1 bin         0 Jun  8  1979 /bin/true
$
$ cat /bin/false
exit 1
$
$ cat /bin/true
$  

最近では、少なくともLinuxでは、bashコマンドはtrueシェルfalse組み込みで実装されています。したがって、andディレクティブを使用するコマンドラインで使用してもfalse、シェルスクリプトで使用しても、デフォルトで実行可能なバイナリは呼び出されません。truebash

bashソースからbuiltins/mkbuiltins.c

文字 *posix_buildins[] =
    {
      「エイリアス」、「bg」、「cd」、「コマンド」、「間違った","fc","fg","getopts","作業",
      「殺す」、「newgrp」、「pwd」、「読む」、本物","umask","unalias","ちょっと待って",
      (文字*)NULL
    };

@meuhコメントも参照してください。

$ command -V true false
true is a shell builtin
false is a shell builtin

trueしたがって、実行可能falseファイルが主に存在することは非常に確実であると言えます。他のプログラムから呼び出された

これからの答えは、/bin/trueDebian 9/64ビットパッケージのバイナリに焦点を当てます。coreutils/usr/bin/trueRedHatの実行。両方の coreutilsパッケージはRedHatとDebianで使用され、後者のコンパイル済みバージョンを分析し、より多くの情報を提供します。)

ソースファイルに示すように、false.cEXIT_FAILURE(1)が返されることを除いて/bin/false(ほぼ)同じソースコードにコンパイルされているため、この答えは両方のバイナリに適用できます。/bin/true

#define EXIT_STATUS EXIT_FAILURE
#include "true.c"

これは、同じサイズの2つの実行可能ファイルを使用して確認することもできるためです。

$ ls -l /bin/true /bin/false
-rwxr-xr-x 1 root root 31464 Feb 22  2017 /bin/false
-rwxr-xr-x 1 root root 31464 Feb 22  2017 /bin/true

ああ、質問に直接答えてください。「なぜ真実は偽ですか?」おそらくそうです。もはや最高の成果に関心を持つ緊急の理由がないからです。パフォーマンスには必要なく、bashスクリプトでは使用されなくなりましたbash

サイズにも同様のコメントが適用されます。現在保有しているハードウェアでは26KBが非常に小さいです。一般的なサーバー/デスクトップの場合、スペースはもはや重要ではなく、使用されているディストリビューションに2回配布されているため、同じfalseバイナリを使用または使用するのは面倒ではありません。truecoreutils

しかし、質問の本当の意味で見ると、なぜそれが単純で小さくなければならないのがなぜそれほど大きくなるのですか?

部品の実際の分布は/bin/true26KBバイナリで表され、メインコード+データは約3KBを占めます/bin/true

trueこのユーティリティは長年にわたってよりおおよそのコードを得ており、特におよび標準的な--versionサポートが際立っています--help

しかし、それがそれほど大きな(唯一の)主な理由ではありませんが、動的にリンクしている間(共有ライブラリを使用して)静的ライブラリにリンクされたバイナリがcoreutils通常使用する共通ライブラリの一部も含まれます。実行可能ファイルを構築するために使用されるメタデータは、elf今日標準として見ると、比較的小さなファイルであるバイナリの大部分を構成します。

残りの答えは、/bin/true実行可能なバイナリの構成を詳しく説明する次の図を構成した方法と、そのような結論に達した方法を説明するためのものです。

ビンゼン 暴食2

@Maksが言ったように、バイナリは私の意見に基づいてCでコンパイルされ、coreutilsでも確認されました。私達は著者gitを直接指す。https://github.com/wertarbyte/coreutils/blob/master/src/true.c、@Maksのようなgnu gitの代わりに(同じソース、他のリポジトリ - このリポジトリはcoreutilsライブラリの完全なソースコードを持っているので選択されました)

ここでは、バイナリのさまざまなコンポーネントを見ることができます/bin/true(Debian 9 - 64ビットcoreutils)。

$ file /bin/true
/bin/true: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9ae82394864538fa7b23b7f87b259ea2a20889c4, stripped

$ size /bin/true
    text       data     bss     dec     hex filename
   24583       1160     416   26159    662f true

それら:

  • テキスト(通常コード)は約24KBです。
  • データ(初期化された変数、主に文字列)は約1KBです。
  • bss(初期化されていないデータ) 0.5KB

24KBのうち約1KBが58の外部機能を変更するために使用されます。

残りのコードにはまだ約23KBが残っています。以下に実際のメインファイルを示します。 main()+usage() コードのコンパイルされたサイズは約 1KB で、残りの 22KB の目的を説明します。

バイナリを詳しく見ると、readelf -S trueバイナリは26159バイトですが、実際にコンパイルされたコードは13017バイトで、残りはさまざまなデータ/初期化コードであることがわかります。

しかし、true.cそれはすべてではありません。そのファイルなら、13KBはやや大きいようです。呼び出される関数がmain()elf の外部関数にリストされていないことがわかりますobjdump -T true

外部リンクを持たない追加機能は次main()のとおりです。

  • プログラム名の設定()
  • stdout() を閉じます。
  • バージョンなど()

したがって、私の最初の疑いは部分的に正確でした。ライブラリは動的ライブラリを使用しますが、/bin/trueバイナリは膨大です。なぜなら一部静的ライブラリを含む(しかし、これが唯一の理由ではありません)。

Cコードは通常コンパイルされません。それこのように非効率的な空間が存在しなかったので、最初は何か問題がないかと疑いました。

追加スペースはバイナリサイズのほぼ90%を占め、実際には追加のライブラリ/ elfメタデータです。

関数がどこにあるかを確認するためにバイナリを分解/逆コンパイルするためにHopperを使用すると、true.c/usage()関数用にコンパイルされたバイナリコードは実際には833バイトですが、true.c/main()関数はコンパイルされますバイナリコードは225バイトで、約1KBよりわずかに小さいです。バージョン機能のロジックは、約1KBサイズの静的ライブラリに隠されています。

実際にコンパイルされた main()+usage()+version()+strings+vars は約 3 KB から 3.5 KB だけを使用します。

このように小さく、目立たない電力会社が上記のような理由でますます大きくなるのは確かに皮肉です。

関連質問:Linuxバイナリが実行するタスクの理解

true.c問題のある関数呼び出しを使用した main():

int
main (int argc, char **argv)
{
  /* Recognize --help or --version only if it's the only command-line
     argument.  */
  if (argc == 2)
    {
      initialize_main (&argc, &argv);
      set_program_name (argv[0]);           <-----------
      setlocale (LC_ALL, "");
      bindtextdomain (PACKAGE, LOCALEDIR);
      textdomain (PACKAGE);

      atexit (close_stdout);             <-----

      if (STREQ (argv[1], "--help"))
        usage (EXIT_STATUS);

      if (STREQ (argv[1], "--version"))
        version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,  AUTHORS,  <------
                     (char *) NULL);
    }

  exit (EXIT_STATUS);
}

バイナリ部分の小数点サイズ:

$ size -A -t true 
true  :
section               size      addr
.interp                 28       568
.note.ABI-tag           32       596
.note.gnu.build-id      36       628
.gnu.hash               60       664
.dynsym               1416       728
.dynstr                676      2144
.gnu.version           118      2820
.gnu.version_r          96      2944
.rela.dyn              624      3040
.rela.plt             1104      3664
.init                   23      4768
.plt                   752      4800
.plt.got                 8      5552
.text                13017      5568
.fini                    9     18588
.rodata               3104     18624
.eh_frame_hdr          572     21728
.eh_frame             2908     22304
.init_array              8   2125160
.fini_array              8   2125168
.jcr                     8   2125176
.data.rel.ro            88   2125184
.dynamic               480   2125272
.got                    48   2125752
.got.plt               392   2125824
.data                  128   2126240
.bss                   416   2126368
.gnu_debuglink          52         0
Total                26211

出力readelf -S true

$ readelf -S true
There are 30 section headers, starting at offset 0x7368:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000000254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000000274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000000298  00000298
       000000000000003c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000000002d8  000002d8
       0000000000000588  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000000860  00000860
       00000000000002a4  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           0000000000000b04  00000b04
       0000000000000076  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000000b80  00000b80
       0000000000000060  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             0000000000000be0  00000be0
       0000000000000270  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             0000000000000e50  00000e50
       0000000000000450  0000000000000018  AI       5    25     8
  [11] .init             PROGBITS         00000000000012a0  000012a0
       0000000000000017  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         00000000000012c0  000012c0
       00000000000002f0  0000000000000010  AX       0     0     16
  [13] .plt.got          PROGBITS         00000000000015b0  000015b0
       0000000000000008  0000000000000000  AX       0     0     8
  [14] .text             PROGBITS         00000000000015c0  000015c0
       00000000000032d9  0000000000000000  AX       0     0     16
  [15] .fini             PROGBITS         000000000000489c  0000489c
       0000000000000009  0000000000000000  AX       0     0     4
  [16] .rodata           PROGBITS         00000000000048c0  000048c0
       0000000000000c20  0000000000000000   A       0     0     32
  [17] .eh_frame_hdr     PROGBITS         00000000000054e0  000054e0
       000000000000023c  0000000000000000   A       0     0     4
  [18] .eh_frame         PROGBITS         0000000000005720  00005720
       0000000000000b5c  0000000000000000   A       0     0     8
  [19] .init_array       INIT_ARRAY       0000000000206d68  00006d68
       0000000000000008  0000000000000008  WA       0     0     8
  [20] .fini_array       FINI_ARRAY       0000000000206d70  00006d70
       0000000000000008  0000000000000008  WA       0     0     8
  [21] .jcr              PROGBITS         0000000000206d78  00006d78
       0000000000000008  0000000000000000  WA       0     0     8
  [22] .data.rel.ro      PROGBITS         0000000000206d80  00006d80
       0000000000000058  0000000000000000  WA       0     0     32
  [23] .dynamic          DYNAMIC          0000000000206dd8  00006dd8
       00000000000001e0  0000000000000010  WA       6     0     8
  [24] .got              PROGBITS         0000000000206fb8  00006fb8
       0000000000000030  0000000000000008  WA       0     0     8
  [25] .got.plt          PROGBITS         0000000000207000  00007000
       0000000000000188  0000000000000008  WA       0     0     8
  [26] .data             PROGBITS         00000000002071a0  000071a0
       0000000000000080  0000000000000000  WA       0     0     32
  [27] .bss              NOBITS           0000000000207220  00007220
       00000000000001a0  0000000000000000  WA       0     0     32
  [28] .gnu_debuglink    PROGBITS         0000000000000000  00007220
       0000000000000034  0000000000000000           0     0     1
  [29] .shstrtab         STRTAB           0000000000000000  00007254
       000000000000010f  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

objdump -T true(実行時に動的に接続された外部関数)出力

$ objdump -T true

true:     file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __uflow
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 getenv
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 free
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 abort
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __errno_location
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 strncmp
0000000000000000  w   D  *UND*  0000000000000000              _ITM_deregisterTMCloneTable
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 _exit
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __fpending
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 textdomain
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fclose
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 bindtextdomain
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 dcgettext
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __ctype_get_mb_cur_max
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 strlen
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.4   __stack_chk_fail
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 mbrtowc
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 strrchr
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 lseek
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 memset
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fscanf
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 close
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __libc_start_main
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 memcmp
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fputs_unlocked
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 calloc
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 strcmp
0000000000000000  w   D  *UND*  0000000000000000              __gmon_start__
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.14  memcpy
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fileno
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 malloc
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fflush
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 nl_langinfo
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 ungetc
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __freading
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 realloc
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fdopen
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 setlocale
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.3.4 __printf_chk
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 error
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 open
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fseeko
0000000000000000  w   D  *UND*  0000000000000000              _Jv_RegisterClasses
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __cxa_atexit
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 exit
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fwrite
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.3.4 __fprintf_chk
0000000000000000  w   D  *UND*  0000000000000000              _ITM_registerTMCloneTable
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 mbsinit
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 iswprint
0000000000000000  w   DF *UND*  0000000000000000  GLIBC_2.2.5 __cxa_finalize
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.3   __ctype_b_loc
0000000000207228 g    DO .bss   0000000000000008  GLIBC_2.2.5 stdout
0000000000207220 g    DO .bss   0000000000000008  GLIBC_2.2.5 __progname
0000000000207230  w   DO .bss   0000000000000008  GLIBC_2.2.5 program_invocation_name
0000000000207230 g    DO .bss   0000000000000008  GLIBC_2.2.5 __progname_full
0000000000207220  w   DO .bss   0000000000000008  GLIBC_2.2.5 program_invocation_short_name
0000000000207240 g    DO .bss   0000000000000008  GLIBC_2.2.5 stderr

おすすめ記事