참과 거짓이 왜 그렇게 큰가?


80

몇 가지 일반적인 명령 (예 read:)이 실제로 Bash 내장 이라는 것을 알게 된 후 프롬프트에서 명령을 실행할 때 실제로는 2 줄 셸 스크립트를 실행하고 있습니다. 마찬가지입니다 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$

그러나 가장 놀라운 것은 크기였습니다. 나는 true기본적으로 just exit 0falseis 인 것처럼 각각 몇 바이트 만 기대했습니다 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$

그래서 제 질문은 : 왜 그렇게 큰가요? 리턴 코드 이외의 실행 파일에는 무엇이 있습니까?

추신 : RHEL 7.4를 사용하고 있습니다


9
사용 command -V true하지 않아야 which합니다. true is a shell builtinbash 용으로 출력됩니다 .
meuh

32
true하고 false 있는 모든 현대 쉘 내장 명령은,하지만 시스템은 또한 이 프로그램에 직접 명령을 호출이 (쉘을 우회)하도록 표준 시스템의 일부이기 때문에 사용할 수 있습니다 그들의 외부 프로그램 버전을 포함합니다. which내장을 무시하고 외부 명령 만 조회하므로 외부 명령 만 표시합니다. 시도 type -a true하고 type -a false대신.
mtraceur

74
그것은 당신이 말을 같은 긴 질문을 작성하는 것이 아이러니 "왜 truefalse반환 코드보다 실행 기타의 내용물을 각각 29KB를?"
David Richerby

7
유닉스의 일부 초기 버전은 종료 코드 0을 반환하는 유효한 sh 프로그램이기 때문에 true에 대한 빈 파일을 가지고 있습니다. 빈 파일에서 실제 유틸리티의 기록에 이르기까지 몇 년 전에 읽은 기사를 찾을 수 있기를 바랍니다. 그것이 오늘의 괴물이지만, 내가 찾을 수있는 것은 이것 뿐입니다
Philip

답변:


117

과거 /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
$  

요즘, 최소한의 bashtruefalse명령은 쉘 내장 명령으로 구현됩니다. 따라서 명령 행과 쉘 스크립트 에서 falseand true지시문을 사용할 때 기본적으로 실행 가능한 2 진 파일이 호출되지 않습니다 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

그래서는 확실성의 높은 수준이라고 할 수 truefalse실행 파일이 주로 존재하는 다른 프로그램에서 호출되는 .

이제부터는 데비안 9/64 비트 패키지 의 /bin/true바이너리에 초점을 맞출 것 coreutils입니다. ( /usr/bin/trueRedHat을 실행 중입니다. RedHat과 Debian은 두 coreutils패키지를 모두 사용하고, 패키지 패키지를 더 많이 사용하고 있습니다.

이 소스 파일에서 볼 수 있듯이 false.c, /bin/false같은 (거의) 동일한 소스 코드로 컴파일 /bin/true단지 (1) 대신에, 그래서이 답변이 모두 바이너리 적용 할 수 있습니다 EXIT_FAILURE를 반환.

#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.

그러나 질문의 ​​실제 정신에 초점을 맞추면 왜 그렇게 단순하고 작아야하는 것이 그렇게 커 집니까?

섹션의 실제 분포는 /bin/true이 차트가 보여주는 것과 같습니다. 주요 코드 + 데이터는 26KB 바이너리 중 약 3KB에 해당하며 크기는 12 %입니다 /bin/true.

true유틸리티는 수년 동안 더 많은 비밀 코드, 특히 --versionand에 대한 표준 지원을 받았습니다 --help.

그러나 그것이 공유 라이브러리를 사용하여 동적으로 링크되는 동안 coreutils정적 라이브러리로 연결된 바이너리에 의해 일반적으로 사용되는 일반 라이브러리의 일부를 갖는 것은 그것이 유일한 큰 정당화가 아니라 오히려 그렇습니다 . elf실행 파일을 만들기위한 메타 데이터 는 바이너리의 상당 부분을 차지하며 오늘날 표준에 따라 상대적으로 작은 파일입니다.

나머지 대답은 /bin/true실행 가능한 이진 파일 의 구성을 자세히 설명하는 다음 차트를 작성하는 방법과 그 결론에 도달 한 방법을 설명하기위한 것입니다.

bintrue bintrue2

@Maks가 말했듯이 바이너리는 C에서 컴파일되었습니다. 내 의견에 따르면 coreutils에서도 확인되었습니다. 우리는 @Maks (같은 소스, 다른 저장소-이 저장소와 같은 gnu git 대신 저자 git https://github.com/wertarbyte/coreutils/blob/master/src/true.c를 직접 가리키고 있습니다. coreutils라이브러리 의 전체 소스가 있으므로 선택되었습니다 )

/bin/true여기서 바이너리 의 다양한 빌딩 블록을 볼 수 있습니다 (에서 데비안 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()엘프에서 볼 수있는 외부 함수에는없는 함수를 호출 할 수있다 objdump -T true. 존재하는 기능 :

외부 적으로 연결되지 않은 추가 기능 main()은 다음 과 같습니다.

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

그래서 내 첫 번째 의혹은 라이브러리는 동적 라이브러리를 사용하고있는 동안은, 부분적으로 정확했다 /bin/true가 있기 때문에 바이너리 * 큰 일부 정적 라이브러리는 *이 포함 (하지만 그 유일한 원인이 아니다).

C 코드를 컴파일하는 것은 일반적으로 이러한 공간을 고려할 비효율적 이지 않으므로 초기 의심이 잘못되었습니다.

이진 크기의 거의 90 % 인 추가 공간은 실제로 추가 라이브러리 / 엘프 메타 데이터입니다.

바이너리를 디스 어셈블 / 디 컴파일하기 위해 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);
}

이진의 다양한 섹션의 십진 크기 :

$ 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

5
64KB의 +를 2kB 마이크로 컨트롤러와 함께 최근 일부 프로그래밍을 수행하는 데, 28 KB는 모든 작은 ..하지 않는 것
Barleyman

1
@Barleyman에는 OpenWRT, yocto, uClinux, uclib, busybox, microcoreutils 및 해당 환경에 대한 기타 솔루션이 있습니다. 우려 사항으로 게시물을 수정했습니다.
Rui F Ribeiro

4
@Barleyman : 이진 실행 파일 크기를 최적화 하는 경우 45 바이트 x86 ELF 실행 파일을 구현 true하거나 false사용 하여 ELF 프로그램 헤더 내에 실행 코드 (4 x86 명령)를 압축 하여 명령 줄 옵션을 지원하지 않습니다! . 리눅스 정말 얼른 ELF 실행 파일을 만들기에 회오리 바람 튜토리얼 . (Linux ELF 로더 구현 세부 사항에 따라 피하고 싶다면 약간 더 큼 : P)
Peter Cordes

3
아니, 아니 예를 들어 Yocto는 64kB를 초과하는 힙 및 바운드 인 메가 바이트 미만으로 채워질 수 있습니다. 이러한 종류의 장치에서는 기본적인 프로세스 / 메모리 관리와 함께 어떤 종류의 RTOS를 사용할 수 있지만 너무 무거워 질 수도 있습니다. 간단한 협업 멀티 스레딩 시스템을 작성하고 내장 메모리 보호 기능을 사용하여 코드 덮어 쓰기를 방지했습니다. 모두 펌웨어가 현재 약 55kB를 소비하므로 추가 오버 헤드를위한 공간이 너무 많지 않다고 말했습니다. 그
거대한

2
@PeterCordes는 확실하지만 Linux가 실행되기 전에 몇 가지 더 많은 리소스가 필요합니다. 가치있는 점에서 C ++은 해당 환경에서도 실제로 작동하지 않습니다. 어쨌든 표준 라이브러리는 아닙니다. Iostream은 약 200kB 정도입니다.
보리 맨

34

구현은 아마도 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.

바이너리에는 문자열 데이터 외에도 명령 행 플래그 등을 구문 분석하는 로직이 포함되어 있습니다. 이는 약 20KB의 코드를 추가하는 것 같습니다.

참고로 여기에서 소스 코드를 찾을 수 있습니다 : http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/true.c


2
참고로 나는 그들의 버그 추적기에 이들로 coreutils 구현에 대해 불평했지만, 기회가 고정되지 얻을 lists.gnu.org/archive/html/bug-coreutils/2016-03/msg00040.html
rudimeier

7
인수의 논리는 아니며, C는 비효율적이지 않습니다 ... 인라인 라이브러리 / 하우스 유지 관리 작업입니다. 까다로운 세부 사항에 대한 내 대답을 살펴보십시오.
Rui F Ribeiro

8
컴파일 된 기계 코드 (C 또는 기타)가 엄청난 양의 공간을 차지한다는 것을 암시하기 때문에 오해의 소지가 있습니다. 실제 크기 오버 헤드는 컴파일러에서 인라인 된 대량의 표준 C 라이브러리 / 런타임 상용구와 관련이 있습니다. C 라이브러리와 상호 운용하기 위해 (glibc, 아마도 시스템이 다른 것을 사용한다고 들지 않는 한, glibc), ELF 헤더 / 메타 데이터 (많은 것이 반드시 필요하지는 않지만 충분히 가치있는 것으로 간주 됨) 기본 빌드에 포함).
mtraceur

2
두 함수의 실제 main () + usage () + string은 20KB가 아닌 약 2KB입니다.
Rui F Ribeiro

2
@JdeBP --version / 버전 funtions의 1킬로바이트 대한 논리 / 사용법 - 주 () 225 바이트의 이진의 전체 정적 데이터를 833 바이트 도움 1킬로바이트이다
루이 F 베

25

핵심 기능으로 분리하고 어셈블러로 작성하면 훨씬 작은 바이너리가 만들어집니다.

원래의 true / false 바이너리는 C로 작성되며, 그 특성상 다양한 라이브러리 + 심볼 참조를 가져옵니다. 당신 readelf -a /bin/true이 이것을 실행 하면 상당히 눈에.니다.

스트립 된 ELF 정적 실행 파일의 경우 352 바이트 (코드 크기에 맞게 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
$

2
의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
terdon

2
이 훌륭한 글을 참고하십시오 : muppetlabs.com/~breadbox/software/tiny/teensy.html
mic_e

3
int 0x8064 비트 실행 파일에서 32 비트 ABI를 사용하고 있습니다. 이는 비정상적이지만 지원됩니다 . 사용 syscall하면 아무것도 절약되지 않습니다. 의 높은 바이트 ebx는 무시되므로 2 바이트를 사용할 수 있습니다 mov bl,1. 또는 물론 xor ebx,ebx0 입니다. 리눅스는 정수 레지스터를 0으로 초기화하므로 1 = __NR_exit (i386 ABI)를 얻을 있습니다 inc eax.
Peter Cordes

1
64 비트 ABI를 사용하도록 골프 예제의 코드를 업데이트하고에 대해 127 바이트로 골프를 쳤습니다 true. (나는 미만 128 바이트 관리 할 수있는 쉬운 방법이 표시되지 않는 false32 비트 ABI를 사용하거나 리눅스는 프로세스 시작시 레지스터를 0이 때문에 사실을 활용 이외하지만, mov al,252(2 바이트) 작동합니다. push imm8/ pop rdilea설정 대신에 작동 edi=1하지만 여전히 mov bl,1REX 접두어없이 32 비트 ABI를 이길 수는 없습니다
Peter Cordes

2
l $(which true false)
-rwxr-xr-x 1 root root 27280 Mär  2  2017 /bin/false
-rwxr-xr-x 1 root root 27280 Mär  2  2017 /bin/true

내 우분투 16.04에서도 꽤 큽니다. 정확히 같은 크기? 왜 그렇게 큰가요?

strings $(which true)

(발췌 :)

Usage: %s [ignored command line arguments]
  or:  %s OPTION
Exit with a status code indicating success.
      --help     display this help and exit
      --version  output version information and exit
NOTE: your shell may have its own version of %s, which usually supersedes
the version described here.  Please refer to your shell's documentation
for details about the options it supports.
http://www.gnu.org/software/coreutils/
Report %s translation bugs to <http://translationproject.org/team/>
Full documentation at: <%s%s>
or available locally via: info '(coreutils) %s%s'

아, 참과 거짓에 대한 도움이 있으므로 시도해보십시오.

true --help 
true --version
#

아무것도. 아,이 다른 줄이 있었다 :

NOTE: your shell may have its own version of %s, which usually supersedes
    the version described here.

따라서 내 시스템에서는 / usr / bin / true가 아닌 / bin / true입니다.

/bin/true --version
true (GNU coreutils) 8.25
Copyright © 2016 Free Software Foundation, Inc.
Lizenz GPLv3+: GNU GPL Version 3 oder höher <http://gnu.org/licenses/gpl.html>
Dies ist freie Software: Sie können sie ändern und weitergeben.
Es gibt keinerlei Garantien, soweit wie es das Gesetz erlaubt.

Geschrieben von Jim Meyering.

LANG=C /bin/true --version
true (GNU coreutils) 8.25
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Jim Meyering.

따라서 국제화를 위해 라이브러리에 바인딩 된 버전 정보가 있습니다. 이것은 크기의 많은 부분을 설명하며, 쉘은 어쨌든 대부분의 경우 최적화 된 명령을 사용합니다.


정적 라이브러리와 엘프 메타 다에 대한 이진 크기의 절반을 포함합니다. 내 대답을 참조하십시오.
Rui F Ribeiro
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.