Чому правдиві та хибні такі великі?


80

Дізнавшись, що декілька загальних команд (таких як read) насправді є вбудованими Bash (і при запуску їх у відповідь я фактично запускаю дворядковий скрипт оболонки, який просто вперед до вбудованого), я шукав, чи однакові вірно для trueі false.

Ну, вони, безумовно, бінарні файли.

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це в принципі справедливо exit 0і falseє exit 1.

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

Однак я здивував, що обидва файли розміром понад 28 КБ.

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


9
Ви повинні використовувати command -V trueНЕ which. Це виведе: true is a shell builtinдля баш.
meuh

32
trueі false є вбудованими у кожну сучасну оболонку, але до системи також входять зовнішні версії програм, оскільки вона є частиною стандартної системи, щоб програми, що викликають команди безпосередньо (в обхід оболонки), могли їх використовувати. whichігнорує вбудовані файли та шукає лише зовнішні команди, тому він лише показав вам зовнішні. Спробуйте type -a trueі type -a falseзамість цього.
mtraceur

74
Іронічно, що ви пишете таке довге запитання, щоб сказати "Чому trueі по 29 falseКБ кожен? Що в виконуваному файлі, крім коду повернення?"
Девід Річербі

7
У деяких ранніх версіях unix просто був порожній файл для true, оскільки це була дійсна програма sh, яка повертає вихідний код 0. Я дуже хочу, щоб я міг знайти статтю, яку я прочитав роки тому про історію справжньої утиліти з порожнього файлу до жахливість це сьогодні, але все, що я міг знайти, це це: trillian.mit.edu/~jc/humor/ATT_Copyright_true.html
Філіп

9
Обов’язкова - найменша реалізація false: muppetlabs.com/~breadbox/software/tiny/teensy.html
d33tah

Відповіді:


117

У минулому /bin/trueі /bin/falseв оболонці насправді були сценарії.

Наприклад, в PDP / 11 Unix System 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, trueі falseкоманди реалізуються як вбудовані команди оболонки. Таким чином, жодні виконувані бінарні файли не викликаються за замовчуванням, як при використанні, так falseі trueдирективах у bashкомандному рядку та всередині скриптів оболонки.

З bashджерела builtins/mkbuiltins.c:

char * posix_builtins [] =
    {
      "псевдонім", "bg", "cd", "command", "** false **", "fc", "fg", "getopts", "jobs",
      "вбити", "newgrp", "pwd", "прочитати", "** справжній **", "umask", "unalias", "зачекати",
      (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(сценарії).

Подібні коментарі стосуються їх розміру, 26 КБ для того обладнання, яке ми маємо сьогодні, є незначним. Простір не в пошані для типового сервера / робочого столу більше, і вони навіть не потрудилися більше використовувати той же двійковий файл для falseі true, так як він просто розгорнуть двічі в розподілах з використанням coreutils.

Якщо зосередитись у реальному дусі питання, чому щось таке, що повинно бути таким простим і малим, стає таким великим?

Справжній розподіл розділів у /bin/trueтаких графіках; основний код + дані становлять приблизно 3 КБ із двійкового файла 26 КБ, що становить 12% від розміру /bin/true.

trueУтиліта отримала дійсно більш мотлох коду на протязі багатьох років, в першу чергу стандартної підтримки --versionі --help.

Однак, це не єдине (основне) обґрунтування того, що воно настільки велике, а скоріше, будучи динамічно пов'язаним (використовуючи спільні лібри), також має частину родової бібліотеки, яка зазвичай використовується coreutilsбінарними файлами, пов'язаними як статична бібліотека. Метадані для створення elfвиконуваного файлу також складають значну частину двійкового файлу, що є відносно невеликим файлом за сучасними мірками.

Решта відповіді - це пояснення того, як нам вдалося скласти наступні діаграми, що деталізують склад /bin/trueвиконавчого двійкового файлу та як ми дійшли цього висновку.

бінтру бінтруе2

Як говорить @Maks, двійковий файл був складений з C; за моїм коментарем також, він також підтвердив, що це з coreutils. Ми вказуємо безпосередньо на автора (и) git https://github.com/wertarbyte/coreutils/blob/master/src/true.c , а не на gnu git як @Maks (ті самі джерела, різні сховища - це сховище було обрано, оскільки воно має повне джерело 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

З тих:

  • текст (зазвичай код) становить близько 24 КБ
  • Дані (ініціалізовані змінні, переважно рядки) становлять близько 1 КБ
  • bss (неініціалізовані дані) 0,5 КБ

З 24 КБ близько 1 КБ призначено для налаштування 58 зовнішніх функцій.

Це все ще залишає близько 23 КБ для решти коду. Нижче ми покажемо, що фактичний код основного файлу - main () + використання () становить близько 1 КБ, і пояснимо, для чого використовуються інші 22 КБ.

Пробиваючись далі по двійковій readelf -S true, ми можемо бачити, що в той час, як двійковий код становить 26159 байт, фактично складений код становить 13017 байт, а решта - це код даних / ініціалізація.

Однак, true.cце не вся історія, і 13 КБ здається набагато надмірною, якби це був лише той файл; ми можемо бачити функції, що викликаються main(), які не перелічені у зовнішніх функціях, що бачать ельф з objdump -T true; функції, які присутні у:

Ці додаткові функції, не пов'язані зовні main():

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

Отже, моя перша підозра була частково правильною, хоча бібліотека використовує динамічні бібліотеки, /bin/trueдвійкові файли є великими *, оскільки в неї є деякі статичні бібліотеки * (але це не єдина причина).

Компіляція коду С зазвичай не є такою неефективною для того, щоб не враховувати такий простір, отже, моє первісне підозри щось не так.

Додатковий простір, майже 90% розміру двійкових, справді є зайвими бібліотеками / ельфійськими метаданими.

Під час використання Hopper для розбирання / декомпіляції двійкового файлу, щоб зрозуміти, де є функції, можна побачити, що складений двійковий код функції true.c / use () насправді становить 833 байти, а функції true.c / main () - 225 байт, що приблизно трохи менше 1 КБ. Логіка функцій версій, яка похована в статичних бібліотеках, становить близько 1 КБ.

Фактично складений main () + використання () + версія () + рядки + vars використовує лише від 3 КБ до 3,5 КБ.

Це справді іронічно, що такі невеликі та скромні утиліти набули більших розмірів з пояснених вище причин.

пов'язане питання: Розуміння того, що робить бінарний файл 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
Зробивши нещодавно програмування за допомогою мікроконтролера на 64 КБ + 2 КБ, 28 кБ не здається таким маленьким ..
Barleyman

1
@Barleyman у вас є OpenWRT, yocto, uClinux, uclib, busybox, мікрокорегулювання та інші рішення для таких середовищ. Редагував публікацію з вашою турботою.
Rui F Ribeiro

4
@Barleyman: Якщо ви оптимізували розмір бінарного виконуваного файлу, ви можете реалізувати trueабо виконати false45-байтовий виконуваний файл EL86 x86, упакувавши виконуваний код (4 x86 інструкції) всередині заголовка програми ELF (без підтримки жодних параметрів командного рядка!) . Підручник з вихору щодо створення справді виконаних ELF-файлів для підлітків для Linux . (Або трохи більше, якщо ви хочете уникнути, залежно від деталей впровадження завантажувача Linux ELF: P)
Пітер Кордес,

3
Не дуже, ні. Наприклад, Yocto може бути забито менш ніж мегабайт, який є нагромадженням і межею вище 64 кБ. У цьому пристрої ви можете використовувати RTOS якогось типу з рудиментарним процесом / управлінням пам'яттю, але навіть вони можуть легко стати занадто важкими. Я написав просту кооперативну багатопотокову систему і використовував вбудований захист пам’яті для захисту коду від перезапису. Всі сказані, що прошивка споживає зараз близько 55 КБ, тому не надто багато місця для додаткових витрат. Ці
величні 2 кБ

2
@PeterCordes напевно, але вам потрібно кілька величин більше ресурсів, перш ніж Linux стане життєздатним. Для чого це вартий, C ++ теж не працює в цьому середовищі. Ну, не стандартні бібліотеки все одно. Iostream виходить близько 200 кБ тощо
Barleyman

34

Реалізація, ймовірно, походить від GNU coreutils. Ці бінарні файли складаються з C; не робилося особливих зусиль, щоб зробити їх меншими, ніж вони за замовчуванням.

Ви можете спробувати скласти тривіальну реалізацію true, і ви помітите, що вона вже має кілька КБ. Наприклад, у моїй системі:

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

Звичайно, ваші бінарні файли ще більше. Це тому, що вони також підтримують аргументи командного рядка. Спробуйте запустити /usr/bin/true --helpабо /usr/bin/true --version.

На додаток до рядкових даних, двійковий файл включає логіку для розбору прапорів командного рядка і т. Д. Це, мабуть, містить приблизно 20 КБ коду.

Для довідки ви можете знайти вихідний код тут: http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/true.c


2
FYI я скаржуся про цих реалізаціях Coreutils на їх відслідковування помилок, але немає можливості її виправити lists.gnu.org/archive/html/bug-coreutils/2016-03/msg00040.html
rudimeier

7
Це не логіка аргументів, C не є такою неефективною ... це вбудовані бібліотеки / завдання зберігання будинку. Погляньте на мою відповідь щодо деталей горі.
Rui F Ribeiro

8
Це вводить в оману, оскільки це дозволяє припустити, що складений машинний код (з C або іншим чином) займає величезну кількість простору - фактичний накладний розмір має більше спільного з величезною кількістю стандартної C-бібліотеки / часу роботи котла, яка накреслюється компілятором у щоб взаємодіяти з бібліотекою C (glibc, якщо ви, мабуть, не чули, що ваша система використовує щось інше), і, меншою мірою, заголовками / метаданими ELF (багато з яких не є строго необхідними, але вважаються досить вартіми включити до збірок за замовчуванням).
mtraceur

2
Фактична основна () + використання () + рядків для обох функцій становить близько 2 КБ, а не 20 КБ.
Rui F Ribeiro

2
Логіка @JdeBP для функцій --version / version 1KB, --usage / - допомога 833 байт, main () 225 байт, а всі статичні дані двійкових файлів становлять 1 КБ
Rui F Ribeiro

25

Приведення їх до основних функціональних можливостей і запис у асемблері дає набагато менші двійкові файли.

Оригінальні істинні / хибні бінарні файли записуються на С, який за своєю природою тягне за собою різні бібліотеки + посилання символів. Якщо ви запускаєте 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
$

Або, з трохи неприємним / геніальним підходом (kudos to stalkr ), створіть власні заголовки ELF, зменшивши їх до 132 127 байт. Ми входимо сюди на територію Code Golf .

$ 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
Також дивіться цю чудову підписку
mic_e

3
Ви використовуєте int 0x8032-розрядний ABI в 64-бітному виконуваному файлі, що незвично, але підтримується . Використання syscallнічого не заощадить. Високі байти ebxігноруються, тому ви можете використовувати 2-байт mov bl,1. Або звичайно xor ebx,ebxза нуль . Linux призначає цілі регістри до нуля, так що ви могли просто inc eaxотримати 1 = __NR_exit (i386 ABI).
Пітер Кордес

1
Я оновив код на прикладі вашого гольфу, щоб використовувати 64-розрядний ABI, і обернув його до 127 байт true. (Я не бачу легкий спосіб керувати менш ніж 128 байт для false, хоча, інші , ніж при використанні 32-бітного ABI або скориставшись тим , що Linux нулі регістрів при запуску процесу, так mov al,252(2 байта) працює. push imm8/ pop rdiБуде також працюємо замість leaналаштування edi=1, але ми все ще не можемо обіграти 32-розрядний ABI, де ми могли б mov bl,1без префікса REX.
Пітер Кордес,

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

Досить великий і на моєму Ubuntu 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.

Тож у моїй системі це / bin / true, а не / usr / 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.