Mengapa benar dan salah begitu besar?


80

Setelah mengetahui bahwa beberapa perintah umum (seperti read) sebenarnya Bash builtins (dan ketika menjalankannya di prompt saya benar-benar menjalankan skrip shell dua baris yang hanya meneruskan ke builtin), saya mencari untuk melihat apakah sama berlaku untuk truedan false.

Ya, mereka pasti binari.

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$

Namun, yang paling mengejutkan bagi saya adalah ukurannya. Saya harapkan mereka untuk menjadi hanya beberapa byte masing-masing, seperti truepada dasarnya hanya exit 0dan falseadalah exit 1.

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

Namun saya terkejut bahwa kedua file berukuran lebih dari 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$

Jadi pertanyaan saya adalah: Mengapa mereka begitu besar? Apa yang ada dalam executable selain dari kode pengembalian?

PS: Saya menggunakan RHEL 7.4


9
Anda sebaiknya command -V truetidak menggunakannya which. Ini akan menampilkan: true is a shell builtinuntuk bash.
meuh

32
truedan false yang builtin di setiap shell modern, tetapi sistem juga termasuk versi program eksternal dari mereka karena itu adalah bagian dari sistem standar sehingga program memohon perintah langsung (melewati shell) dapat menggunakannya. whichmengabaikan builtin, dan mencari perintah eksternal saja, itulah sebabnya hanya menunjukkan kepada Anda perintah eksternal. Coba type -a truedan type -a falsesebagai gantinya.
mtraceur

74
Sungguh ironis bahwa Anda menulis pertanyaan yang begitu panjang untuk mengatakan "Mengapa masing-masing truedan false29kb? Apa yang ada di executable selain kode kembali?"
David Richerby

7
Beberapa versi awal unix hanya memiliki file kosong untuk true karena itu adalah program sh yang valid yang akan mengembalikan kode keluar 0. Saya benar-benar berharap saya bisa menemukan artikel yang saya baca bertahun-tahun lalu tentang sejarah utilitas yang sebenarnya dari file kosong ke monstrositas seperti sekarang ini, tetapi yang bisa saya temukan adalah ini: trillian.mit.edu/~jc/humor/ATT_Copyright_true.html
Philip

9
Wajib - implementasi terkecil dari false: muppetlabs.com/~breadbox/software/tiny/teensy.html
d33tah

Jawaban:


117

Di masa lalu, /bin/truedan /bin/falsedi shell sebenarnya skrip.

Misalnya, dalam Sistem Unix 7 PDP / 11:

$ 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
$  

Saat ini, setidaknya di bash, truedan falseperintah diimplementasikan sebagai perintah built-in shell. Dengan demikian tidak ada file biner yang dapat dieksekusi dipanggil secara default, baik saat menggunakan falsedan truearahan dalam bashbaris perintah dan skrip shell di dalam.

Dari bashsumbernya builtins/mkbuiltins.c:

char * posix_builtins [] =
    {
      "alias", "bg", "cd", "command", "** false **", "fc", "fg", "getopts", "jobs",
      "bunuh", "newgrp", "pwd", "baca", "** benar **", "umask", "unalias", "tunggu",
      (char *) NULL
    };

Juga per @ saya komentar:

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

Jadi dapat dikatakan dengan tingkat kepastian yang tinggi truedan falsefile yang dapat dieksekusi ada terutama untuk dipanggil dari program lain .

Mulai sekarang, jawabannya akan fokus pada /bin/truebiner dari coreutilspaket di Debian 9/64 bit. ( /usr/bin/trueMenjalankan RedHat. RedHat dan Debian menggunakan kedua coreutilspaket, menganalisis versi terkompilasi dari yang terakhir memiliki lebih di tangan).

Seperti yang dapat dilihat pada file sumber false.c, /bin/falsedikompilasi dengan (hampir) kode sumber yang sama dengan /bin/true, hanya mengembalikan EXIT_FAILURE (1) sebagai gantinya, jadi jawaban ini dapat diterapkan untuk kedua binari.

#define EXIT_STATUS EXIT_FAILURE
#include "true.c"

Karena juga dapat dikonfirmasi oleh kedua executable yang memiliki ukuran yang sama:

$ 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

Sayangnya, pertanyaan langsung untuk jawabannya why are true and false so large?bisa jadi, karena tidak ada lagi alasan yang begitu mendesak untuk peduli dengan kinerja terbaik mereka. Mereka tidak penting untuk bashkinerja, tidak digunakan lagi oleh bash(scripting).

Komentar serupa berlaku untuk ukurannya, 26KB untuk jenis perangkat keras yang kita miliki saat ini tidak signifikan. Ruang tidak lagi premium untuk server / desktop biasa, dan mereka bahkan tidak repot-repot lagi menggunakan biner yang sama untuk , falsedan truekarena hanya digunakan dua kali dalam distribusi menggunakan coreutils.

Namun, berfokus pada semangat pertanyaan yang sebenarnya, mengapa sesuatu yang seharusnya begitu sederhana dan kecil, menjadi begitu besar?

Distribusi nyata bagian /bin/trueadalah seperti yang ditunjukkan oleh bagan ini; kode utama + data berjumlah sekitar 3KB dari biner 26KB, yang berjumlah 12% dari ukuran /bin/true.

The trueutilitas mendapat kode memang lebih cruft selama bertahun-tahun, terutama dukungan standar untuk --versiondan --help.

Namun, itu bukan pembenaran utama (hanya) untuk itu menjadi begitu besar, tetapi lebih tepatnya, sementara sedang terkait secara dinamis (menggunakan shared libs), juga memiliki bagian dari perpustakaan umum yang biasa digunakan oleh coreutilsbiner yang dihubungkan sebagai perpustakaan statis. Metada untuk membangun elffile yang dapat dieksekusi juga berjumlah sebagian besar dari biner, karena itu file yang relatif kecil menurut standar saat ini.

Sisa jawabannya adalah untuk menjelaskan bagaimana kami harus membuat bagan berikut yang merinci komposisi /bin/truefile biner yang dapat dieksekusi dan bagaimana kami sampai pada kesimpulan itu.

bintrue bintrue2

Seperti yang dikatakan @Maks, biner dikompilasi dari C; sesuai komentar saya juga, juga dikonfirmasi itu dari coreutils. Kami menunjuk langsung ke penulis git https://github.com/wertarbyte/coreutils/blob/master/src/true.c , alih-alih gnu git sebagai @Maks (sumber yang sama, repositori berbeda - repositori ini dipilih karena memiliki sumber coreutilsperpustakaan yang lengkap)

Kita dapat melihat berbagai blok bangunan /bin/truebiner di sini (Debian 9 - 64 bit dari 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

Dari mereka:

  • teks (biasanya kode) sekitar 24KB
  • data (variabel yang diinisialisasi, sebagian besar string) sekitar 1KB
  • bss (data tidak diinisialisasi) 0,5KB

Dari 24KB, sekitar 1KB adalah untuk memperbaiki 58 fungsi eksternal.

Itu masih menyisakan sekitar 23KB untuk sisa kode. Kami akan menunjukkan di bawah ini bahwa file utama sebenarnya - kode utama () + penggunaan () adalah sekitar 1KB yang dikompilasi, dan menjelaskan untuk apa 22KB lainnya digunakan.

Pengeboran lebih jauh ke bawah biner dengan readelf -S true, kita dapat melihat bahwa sementara biner adalah 26159 byte, kode kompilasi yang sebenarnya adalah 13017 byte, dan sisanya adalah berbagai data / kode inisialisasi.

Namun, true.cbukan keseluruhan cerita dan 13KB tampaknya cukup berlebihan jika hanya file itu; kita bisa melihat fungsi-fungsi yang dipanggil main()yang tidak terdaftar dalam fungsi-fungsi eksternal yang terlihat di elf dengan objdump -T true; fungsi yang hadir di:

Fungsi-fungsi ekstra yang tidak ditautkan secara eksternal main()adalah:

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

Jadi kecurigaan pertama saya sebagian benar, sementara perpustakaan menggunakan perpustakaan dinamis, /bin/truebiner besar * karena memiliki beberapa perpustakaan statis yang disertakan dengannya * (tapi itu bukan satu-satunya penyebab).

Kompilasi kode C biasanya tidak yang efisien untuk memiliki ruang seperti belum ditemukan, maka saya awal kecurigaan sesuatu yang salah.

Ruang ekstra, hampir 90% dari ukuran biner, memang merupakan perpustakaan ekstra / metadata elf.

Saat menggunakan Hopper untuk membongkar / mendekompilasi biner untuk memahami di mana fungsi berada, dapat dilihat kode biner yang dikompilasi dari fungsi true.c / use () sebenarnya 833 byte, dan dari fungsi true.c / main () adalah 225 byte, yang kira-kira sedikit kurang dari 1KB. Logika untuk fungsi versi, yang terkubur di perpustakaan statis, sekitar 1KB.

Sebenarnya kompilasi utama () + penggunaan () + versi () + string + vars hanya menggunakan sekitar 3KB hingga 3.5KB.

Sungguh ironis, utilitas kecil dan sederhana telah menjadi lebih besar karena alasan yang dijelaskan di atas.

pertanyaan terkait: Memahami apa yang dilakukan biner Linux

true.c main () dengan panggilan fungsi yang menyinggung:

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);
}

Ukuran desimal dari berbagai bagian biner:

$ 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

Output dari 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)

Output of objdump -T true(fungsi eksternal terhubung secara dinamis pada saat run-time)

$ 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
Setelah melakukan beberapa pemrograman baru-baru ini dengan mikrokontroler 64kB + 2kB, 28kB tampaknya tidak terlalu kecil ..
Barleyman

1
@Barleyman Anda memiliki OpenWRT, yocto, uClinux, uclib, busybox, microcoreutils, dan solusi lain untuk lingkungan semacam itu. Mengedit pos dengan perhatian Anda.
Rui F Ribeiro

4
@Barleyman: Jika Anda mengoptimalkan untuk ukuran biner yang dapat dieksekusi, Anda dapat mengimplementasikan trueatau falsedengan executable ELF x86 45-byte, mengemas kode yang dapat dieksekusi (instruksi 4 x86) di dalam header program ELF (tanpa dukungan untuk opsi baris perintah!) . Tutorial Whirlwind tentang Membuat Executables ELF yang Sangat Berlebihan untuk Linux . (Atau sedikit lebih besar jika Anda ingin menghindari tergantung pada rincian implementasi loader ELF Linux: P)
Peter Cordes

3
Tidak juga, tidak. Yocto misalnya dapat dijejalkan menjadi kurang dari satu megabyte yang bertumpukan dan batas di atas 64kB .. Dalam perangkat jenis ini Anda dapat menggunakan RTOS dari beberapa jenis dengan proses dasar / manajemen memori tetapi bahkan mereka dapat dengan mudah menjadi terlalu berat. Saya menulis sistem multithreading koperasi sederhana dan menggunakan perlindungan memori built in untuk melindungi kode dari ditimpa. Semua mengatakan bahwa firmware mengkonsumsi 55kB saat ini sehingga tidak terlalu banyak ruang untuk overhead tambahan. 2kB ginormous itu mencari di atas meja ..
Barleyman

2
@PeterCordes pasti tetapi Anda perlu beberapa sumber daya lebih besar sebelum Linux menjadi layak. Untuk apa nilainya, C ++ juga tidak benar-benar berfungsi di lingkungan itu. Bagaimanapun, bukan perpustakaan standar. Iostream keluar sekitar 200kB dll.
Barleyman

34

Implementasinya mungkin berasal dari GNU coreutils. Binari ini dikompilasi dari C; tidak ada upaya khusus yang dilakukan untuk membuatnya lebih kecil dari yang ada secara default.

Anda dapat mencoba mengkompilasi implementasi sepele dari truediri Anda, dan Anda akan melihat itu sudah beberapa KB. Misalnya, di sistem saya:

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

Tentu saja, binari Anda bahkan lebih besar. Itu karena mereka juga mendukung argumen baris perintah. Coba jalankan /usr/bin/true --helpatau /usr/bin/true --version.

Selain data string, biner menyertakan logika untuk mengurai bendera baris perintah, dll. Itu menambahkan hingga sekitar 20 KB kode, tampaknya.

Untuk referensi, Anda dapat menemukan kode sumber di sini: http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/true.c


2
FYI Saya mengeluh tentang implementasi coreutils ini pada pelacak bug mereka, tetapi tidak ada kesempatan untuk memperbaikinya lists.gnu.org/archive/html/bug-coreutils/2016-03/msg00040.html
rudimeier

7
Ini bukan logika untuk argumen, C tidak efisien ... adalah inline library / tugas pemeliharaan rumah. Lihatlah jawaban saya untuk detail berdarah.
Rui F Ribeiro

8
Ini menyesatkan karena menunjukkan bahwa kode mesin yang dikompilasi (dari C atau sebaliknya) adalah apa yang mengambil jumlah besar ruang - overhead ukuran sebenarnya lebih berkaitan dengan sejumlah besar library C standar / boilerplate runtime yang digarisbawahi oleh kompiler di memesan untuk beroperasi dengan pustaka C (glibc, kecuali Anda pernah mendengar bahwa sistem Anda menggunakan sesuatu yang lain, mungkin), dan, pada tingkat lebih rendah, header / metadata ELF (banyak yang tidak benar-benar diperlukan, tetapi dianggap cukup berharga) untuk disertakan dalam build default).
mtraceur

2
Main () + penggunaan () + string pada kedua fungsi sebenarnya sekitar 2KB, bukan 20KB.
Rui F Ribeiro

2
@JdeBP logika untuk --versi / versi funtions 1KB, --usup / - help 833 byte, main () 225 byte dan seluruh data statis dari biner adalah 1KB
Rui F Ribeiro

25

Melucuti mereka ke fungsionalitas inti dan menulis di assembler menghasilkan biner yang jauh lebih kecil.

Binari benar / salah asli ditulis dalam C, yang pada dasarnya menarik berbagai referensi pustaka + simbol. Jika Anda menjalankan readelf -a /bin/trueini cukup terlihat.

352 byte untuk executable statis ELF yang dilucuti (dengan ruang untuk menyimpan beberapa byte dengan mengoptimalkan asm untuk ukuran kode).

$ 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
$

Atau, dengan sedikit pendekatan jahat / cerdik (pujian untuk stalkr ), buat header ELF Anda sendiri, turunkan ke 132 127 byte. Kami memasuki wilayah Code Golf di sini.

$ 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
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
terdon


3
Anda menggunakan int 0x80ABI 32-bit dalam eksekusi 64-bit, yang tidak biasa tetapi didukung . Menggunakan syscalltidak akan menyelamatkan Anda apa pun. Bytes tinggi ebxdiabaikan, jadi Anda bisa menggunakan 2 byte mov bl,1. Atau tentu saja xor ebx,ebxnol . Linux inits register integer ke nol, sehingga Anda bisa hanya inc eaxuntuk mendapatkan 1 = __NR_exit (i386 ABI).
Peter Cordes

1
Saya memperbarui kode pada contoh golf Anda untuk menggunakan ABI 64-bit, dan golf turun menjadi 127 byte true. (Saya tidak melihat cara mudah untuk mengelola kurang dari 128 byte untuk false, meskipun, selain menggunakan 32-bit ABI atau mengambil keuntungan dari fakta bahwa Linux angka nol register pada proses startup, sehingga mov al,252(2 bytes) bekerja. push imm8/ pop rdiAkan juga bekerja alih-alih leauntuk pengaturan edi=1, tapi kami masih tidak bisa mengalahkan ABI 32-bit di mana kami bisa mov bl,1tanpa awalan REX
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

Cukup besar di Ubuntu 16.04 saya juga. ukuran persis sama? Apa yang membuat mereka begitu besar?

strings $(which true)

(kutipan:)

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'

Ah, ada bantuan untuk benar dan salah, jadi mari kita coba:

true --help 
true --version
#

Tidak ada. Ah, ada baris lain ini:

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

Jadi pada sistem saya, ini / bin / true, bukan / 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.

Jadi ada bantuan, ada informasi versi, mengikat ke perpustakaan untuk internasionalisasi. Ini menjelaskan banyak ukuran, dan shell tetap menggunakan perintah yang dioptimalkan dan sebagian besar waktu.


Termasuk perpustakaan statis, dan setengah dari ukuran biner untuk meta elf. Lihat jawaban saya.
Rui F Ribeiro
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.