GNU/Linux >> Linux の 問題 >  >> Linux

true と false の値が大きいのはなぜですか?

以前は /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
$  

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

bash から ソース、builtins/mkbuiltins.c :

char *posix_builtins[] =
    {
      "alias", "bg", "cd", "command", "**false**", "fc", "fg", "getopts", "jobs",
      "kill", "newgrp", "pwd", "read", "**true**", "umask", "unalias", "wait",
      (char *)NULL
    };

また、@meuh コメントごとに:

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

true は高い確率でそう言えます。 と false 実行可能ファイルは、主に他のプログラムから呼び出されるために存在します .

これからは、答えは /bin/true に焦点を当てます coreutils からのバイナリ Debian 9 / 64 ビットのパッケージ。 (/usr/bin/true RedHat を実行しています。 RedHat と Debian は coreutils の両方を使用します パッケージは、後者のコンパイル済みバージョンを分析して、より手元に置いています)。

ソースファイル false.c でわかるように 、 /bin/false /bin/true と (ほぼ) 同じソースコードでコンパイルされます 代わりに EXIT_FAILURE (1) を返すだけなので、この回答は両方のバイナリに適用できます。

#define EXIT_STATUS EXIT_FAILURE
#include "true.c"

同じサイズの両方の実行可能ファイルによっても確認できるため:

$ 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

ああ、答え why are true and false so large? への直接の質問 最高のパフォーマンスを気にする差し迫った理由がなくなったからです。それらは bash にとって必須ではありません パフォーマンス、bash によって使用されなくなりました (スクリプト)。

サイズについても同様のコメントが当てはまります。現在使用している種類のハードウェアでは 26KB は重要ではありません。典型的なサーバー/デスクトップのスペースはもはや貴重ではなく、false に同じバイナリを使用することさえ気にしません。 と true coreutils を使用してディストリビューションに 2 回デプロイされているためです。 .

しかし、質問の真の精神に焦点を当てると、なぜこれほど単純で小さくあるべきものが、これほど大きくなってしまうのでしょうか?

/bin/true のセクションの実際の分布 これらのチャートが示すとおりです。メイン コードとデータは、26KB のバイナリのうち約 3KB になり、これは /bin/true のサイズの 12% になります。 .

true ユーティリティは実際、何年にもわたってより多くの粗悪なコードを取得しました。最も顕著なのは、--version の標準サポートです。 と --help .

ただし、それが非常に大きいことの (唯一の) 主な理由ではなく、(共有ライブラリを使用して) 動的にリンクされている間、coreutils で一般的に使用される汎用ライブラリの一部も持っていることです。 静的ライブラリとしてリンクされたバイナリ。 elf を構築するためのメタデータ 実行可能ファイルもバイナリのかなりの部分を占めており、今日の基準では比較的小さいファイルです。

残りの答えは、/bin/true の構成を詳述する次のチャートをどのように作成したかを説明するためのものです。 実行可能なバイナリ ファイルと、その結論に至った経緯

@Maks が言うように、バイナリは C からコンパイルされました。私のコメントによると、coreutils からのものであることも確認されています。 @Maks (同じソース、異なるリポジトリ - このリポジトリcoreutils の完全なソースがあるため、選択されました ライブラリ)

/bin/true のさまざまな構成要素を見ることができます ここにバイナリ (Debian 9 - coreutils からの 64 ビット) ):

$ 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() で呼び出される関数を確認できます objdump -T true のエルフに見られる外部関数にリストされていないもの;次の場所にある機能:

  • https://github.com/coreutils/gnulib/blob/master/lib/progname.c
  • https://github.com/coreutils/gnulib/blob/master/lib/closeout.c
  • https://github.com/coreutils/gnulib/blob/master/lib/version-etc.c

main() で外部にリンクされていない追加機能

  • set_program_name()
  • close_stdout()
  • version_etc()

したがって、ライブラリが動的ライブラリを使用している間、私の最初の疑いは部分的に正しかったのですが、 /bin/true バイナリは大きい *some があるため 静的ライブラリが含まれています* (ただし、それだけが原因ではありません)。

通常、C コードのコンパイルはそれではありません そのようなスペースが考慮されていないため、効率が悪いため、最初は何かがおかしいのではないかと疑っていました.

余分なスペース (バイナリのサイズのほぼ 90%) は、実際には追加のライブラリ/elf メタデータです。

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

実際にコンパイルされた main()+usage()+version()+strings+vars は、約 3KB から 3.5KB しか使用していません。

皮肉なことに、このような小さくて質素なユーティリティが、上記の理由で規模が大きくなっています。

関連する質問: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);
}

バイナリのさまざまなセクションの 10 進数のサイズ:

$ 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

実装はおそらく GNU coreutils から来ています。これらのバイナリは C からコンパイルされています。デフォルトよりも小さくするための特別な努力はされていません。

true の簡単な実装をコンパイルしてみてください。 サイズがすでに数 KB になっていることに気付くでしょう。たとえば、私のシステムでは:

$ echo 'int main() { return 0; }' | gcc -xc - -o true
$ wc -c true
8136 true

もちろん、バイナリはさらに大きくなります。これは、コマンド ライン引数もサポートしているためです。 /usr/bin/true --help を実行してみてください または /usr/bin/true --version .

文字列データに加えて、バイナリにはコマンド ライン フラグなどを解析するロジックが含まれています。これにより、約 20 KB のコードが追加されるようです。

参考までに、ソース コードは http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/true.c にあります。


それらをコア機能に落としてアセンブラーで書くと、はるかに小さなバイナリが生成されます。

元の true/false バイナリは C で記述されており、その性質上、さまざまなライブラリ + シンボル参照が取り込まれます。 readelf -a /bin/true を実行した場合 これはかなり目立ちます。

352 削除された ELF 静的実行可能ファイルのバイト数 (コードサイズの asm を最適化することで、数バイトを節約する余地があります)。

$ more true.asm false.asm
::::::::::::::
true.asm
::::::::::::::
global _start
_start:
 mov ebx,0
 mov eax,1     ; SYS_exit from asm/unistd_32.h
 int 0x80      ; The 32-bit ABI is supported in 64-bit code, in kernels compiled with IA-32 emulation
::::::::::::::
false.asm
::::::::::::::
global _start
_start:
 mov ebx,1
 mov eax,1
 int 0x80
$ nasm -f elf64 true.asm && ld -s -o true true.o     # -s means strip
$ nasm -f elf64 false.asm && ld -s -o false false.o
$ ll true false
-rwxrwxr-x. 1 steve steve 352 Jan 25 16:03 false
-rwxrwxr-x. 1 steve steve 352 Jan 25 16:03 true
$ ./true ; echo $?
0
$ ./false ; echo $?
1
$

または、少し厄介で独創的なアプローチ (stalkr に称賛) を使用して、独自の ELF ヘッダーを作成し、132 に減らします。 127 バイト。ここからコード ゴルフの領域に入ります。

$ cat true2.asm
BITS 64
  org 0x400000   ; _start is at 0x400080 as usual, but the ELF headers come first

ehdr:           ; Elf64_Ehdr
  db 0x7f, "ELF", 2, 1, 1, 0 ; e_ident
  times 8 db 0
  dw  2         ; e_type
  dw  0x3e      ; e_machine
  dd  1         ; e_version
  dq  _start    ; e_entry
  dq  phdr - $$ ; e_phoff
  dq  0         ; e_shoff
  dd  0         ; e_flags
  dw  ehdrsize  ; e_ehsize
  dw  phdrsize  ; e_phentsize
  dw  1         ; e_phnum
  dw  0         ; e_shentsize
  dw  0         ; e_shnum
  dw  0         ; e_shstrndx
  ehdrsize  equ  $ - ehdr

phdr:           ; Elf64_Phdr
  dd  1         ; p_type
  dd  5         ; p_flags
  dq  0         ; p_offset
  dq  $$        ; p_vaddr
  dq  $$        ; p_paddr
  dq  filesize  ; p_filesz
  dq  filesize  ; p_memsz
  dq  0x1000    ; p_align
  phdrsize  equ  $ - phdr

_start:
  xor  edi,edi         ; int status = 0
      ; or  mov dil,1  for false: high bytes are ignored.
  lea  eax, [rdi+60]   ; rax = 60 = SYS_exit, using a 3-byte instruction: base+disp8 addressing mode
  syscall              ; native 64-bit system call, works without CONFIG_IA32_EMULATION

; less-golfed version:
;      mov  edi, 1    ; for false
;      mov  eax,252   ; SYS_exit_group from asm/unistd_64.h
;      syscall

filesize  equ  $ - $$      ; used earlier in some ELF header fields

$ nasm -f bin -o true2 true2.asm
$ ll true2
-rw-r--r-- 1 peter peter 127 Jan 28 20:08 true2
$ chmod +x true2 ; ./true2 ; echo $?
0
$

Linux
  1. Linuxは複数の連続したパスセパレーター(/ home //// username /// file)をどのように処理しますか?

  2. Bash =〜正規表現とHttps://regex101.com/?

  3. / sbin/nologinと/bin/ falseの違いは何ですか?

  1. なぜ誰もTrueBourneShellを/bin/ shとして使用しないのですか?

  2. #!/usr/bin/env bash と #!/usr/bin/bash の違いは何ですか?

  3. /dev/shm/ と /tmp/ はいつ使用する必要がありますか?

  1. / bin/falseと/sbin/nologinの違い

  2. / usr/binと/usr/ local / bin Linuxの場合?

  3. #!/bin/bash と #!/bin/sh はいつ使用する必要がありますか?