Tại sao đúng và sai quá lớn?


80

Sau khi phát hiện ra rằng một số lệnh phổ biến (chẳng hạn như read) thực sự là các nội trang của Bash (và khi chạy chúng tại dấu nhắc, tôi thực sự đang chạy một tập lệnh shell hai dòng chỉ chuyển tiếp đến phần dựng sẵn), tôi đã tìm xem có giống như vậy không là đúng cho truefalse.

Vâng, họ chắc chắn là nhị phân.

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$

Tuy nhiên, điều tôi thấy ngạc nhiên nhất là kích thước của chúng. Tôi dự kiến ​​chúng chỉ có một vài byte mỗi cái, truevề cơ bản là vừa exit 0falseexit 1.

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

Tuy nhiên tôi thấy ngạc nhiên khi cả hai tập tin đều có kích thước trên 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$

Vì vậy, câu hỏi của tôi là: Tại sao chúng rất lớn? Những gì trong thực thi khác với mã trả về?

Tái bút: Tôi đang sử dụng RHEL 7.4


9
Bạn nên sử dụng command -V truekhông which. Nó sẽ xuất ra: true is a shell builtincho bash.
meuh

32
truefalse được tích hợp trong mọi shell hiện đại, nhưng các hệ thống cũng bao gồm các phiên bản chương trình bên ngoài của chúng vì nó là một phần của hệ thống tiêu chuẩn để các chương trình gọi lệnh trực tiếp (bỏ qua shell) có thể sử dụng chúng. whichbỏ qua các nội trang và chỉ tìm kiếm các lệnh bên ngoài, đó là lý do tại sao nó chỉ hiển thị cho bạn các lệnh bên ngoài. Hãy thử type -a truetype -a falsethay vào đó.
mtraceur

74
Thật trớ trêu khi bạn viết một câu hỏi dài như vậy để nói "Tại sao truefalse29kb mỗi câu? Cái gì trong thực thi không phải là mã trả về?"
David Richerby

7
Một số phiên bản đầu tiên của unix chỉ có một tệp trống vì đó là chương trình sh hợp lệ sẽ trả về mã thoát 0. Tôi thực sự ước mình có thể tìm thấy một bài viết mà tôi đã đọc cách đây nhiều năm về lịch sử của tiện ích thực sự từ một tệp trống sự quái dị ngày nay, nhưng tất cả những gì tôi có thể tìm thấy là đây: trillian.mit.edu/~jc/humor/ATT_Copyright_true.html
Philip

9
Bắt buộc - triển khai nhỏ nhất của false: muppetlabs.com/~breadbox/software/tiny/teensy.html
d33tah

Câu trả lời:


117

Trong quá khứ, /bin/true/bin/falsetrong vỏ thực sự là các kịch bản.

Chẳng hạn, trong 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
$  

Ngày nay, ít nhất là trong bash, các lệnh truefalselệnh được triển khai như các lệnh tích hợp shell. Do đó không có file thực thi nhị phân được gọi theo mặc định, cả khi sử dụng falsetruechỉ thị trong bashdòng lệnh và bên trong kịch bản shell.

Từ bashnguồn , builtins/mkbuiltins.c:

char * posix_builtins [] =
    {
      "bí danh", "bg", "cd", "lệnh", "** false **", "fc", "fg", "getopts", "jobs",
      "Giết", "newgrp", "pwd", "đọc", "** đúng **", "umask", "unalias", "chờ",
      (char *) NULL
    };

Ngoài ra mỗi bình luận @meuh:

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

Vì vậy, có thể nói với mức độ chắc chắn cao, các tệp thực thi truefalsetồn tại chủ yếu được gọi từ các chương trình khác .

Từ giờ trở đi, câu trả lời sẽ tập trung vào /bin/truenhị phân từ coreutilsgói trong Debian 9/64 bit. ( /usr/bin/truechạy RedHat. RedHat và Debian sử dụng cả coreutilsgói, đã phân tích phiên bản được biên dịch của phiên bản sau có sẵn trong tay).

Như có thể thấy trong tệp nguồn false.c, /bin/falseđược biên dịch với (gần như) mã nguồn giống như /bin/true, chỉ trả về EXIT_FAILURE (1), vì vậy câu trả lời này có thể được áp dụng cho cả hai nhị phân.

#define EXIT_STATUS EXIT_FAILURE
#include "true.c"

Vì nó cũng có thể được xác nhận bởi cả hai tệp thực thi có cùng kích thướ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

Than ôi, câu hỏi trực tiếp cho câu trả lời why are true and false so large?có thể là bởi vì không còn lý do cấp bách nào để quan tâm đến hiệu suất hàng đầu của họ. Chúng không cần thiết cho bashhiệu suất, không được sử dụng nữa bởi bash(kịch bản).

Nhận xét tương tự áp dụng cho kích thước của chúng, 26KB cho loại phần cứng chúng ta hiện nay là không đáng kể. Không gian không còn cao cấp cho máy chủ / máy tính để bàn thông thường nữa và họ thậm chí không bận tâm sử dụng cùng một nhị phân cho falsetruevì nó chỉ được triển khai hai lần trong các bản phân phối sử dụng coreutils.

Tuy nhiên, tập trung vào tinh thần thực sự của câu hỏi, tại sao một cái gì đó đơn giản và nhỏ bé lại trở nên lớn đến vậy?

Phân phối thực sự của các phần /bin/truelà như những biểu đồ này cho thấy; mã chính + dữ liệu lên tới khoảng 3KB trong số nhị phân 26KB, chiếm tới 12% kích thước của /bin/true.

Các truetiện ích có mã thực sự cruft hơn trong những năm qua, đặc biệt là sự hỗ trợ tiêu chuẩn cho --version--help.

Tuy nhiên, đó không phải là lý do chính (duy nhất) cho nó quá lớn, mà là, trong khi được liên kết động (sử dụng libs chung), cũng có một phần của thư viện chung thường được sử dụng bởi các coreutilsnhị phân được liên kết như một thư viện tĩnh. Metada để xây dựng một elftệp thực thi cũng chiếm một phần đáng kể của nhị phân, vì nó là một tệp tương đối nhỏ theo tiêu chuẩn của hôm nay.

Phần còn lại của câu trả lời là để giải thích cách chúng tôi xây dựng các biểu đồ sau đây chi tiết về thành phần của /bin/truetệp nhị phân thực thi và cách chúng tôi đi đến kết luận đó.

bintrue bintrue2

Như @Maks nói, nhị phân được biên dịch từ C; theo nhận xét của tôi, nó cũng được xác nhận là từ coreutils. Chúng tôi đang chỉ trực tiếp đến tác giả git https://github.com/wertarbyte/coreutils/blob/master/src/true.c , thay vì gnu git như @Maks (cùng nguồn, kho lưu trữ khác nhau - kho lưu trữ này đã được chọn vì nó có nguồn đầy đủ của các coreutilsthư viện)

Chúng ta có thể thấy các khối xây dựng khác nhau của /bin/truenhị phân ở đây (Debian 9 - 64 bit từ 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

Những:

  • văn bản (thường là mã) khoảng 24KB
  • dữ liệu (biến khởi tạo, chủ yếu là chuỗi) khoảng 1KB
  • bss (dữ liệu chưa được khởi tạo) 0,5KB

Trong số 24KB, khoảng 1KB là để sửa 58 chức năng bên ngoài.

Điều đó vẫn còn khoảng 23KB cho phần còn lại của mã. Chúng tôi sẽ chỉ ra dưới đây rằng tệp chính thực tế - mã chính () + cách sử dụng () được biên dịch khoảng 1KB và giải thích 22KB khác được sử dụng để làm gì.

Đi sâu hơn vào nhị phân với readelf -S true, chúng ta có thể thấy rằng trong khi nhị phân là 26159 byte, mã được biên dịch thực tế là 13017 byte và phần còn lại là các loại dữ liệu / mã khởi tạo.

Tuy nhiên, true.ckhông phải là toàn bộ câu chuyện và 13KB dường như quá nhiều nếu chỉ là tập tin đó; chúng ta có thể thấy các hàm được gọi trong main()đó không được liệt kê trong các hàm bên ngoài nhìn thấy trong elf với objdump -T true; các chức năng có mặt tại:

Các chức năng bổ sung không được liên kết bên ngoài main()là:

  • set_program_name ()
  • close_stdout ()
  • phiên bản_etc ()

Vì vậy, sự nghi ngờ đầu tiên của tôi là một phần chính xác, trong khi thư viện đang sử dụng các thư viện động, /bin/truenhị phân là lớn * bởi vì nó có một số thư viện tĩnh đi kèm với nó * (nhưng đó không phải là nguyên nhân duy nhất).

Biên dịch mã C không phải là thường không hiệu quả vì có không gian như vậy mất tích, vì thế ban đầu nghi ngờ một cái gì đó của tôi là không ổn.

Không gian thêm, gần 90% kích thước của nhị phân, thực sự là siêu thư viện / elf siêu dữ liệu.

Trong khi sử dụng Hopper để phân tách / dịch ngược tệp nhị phân để hiểu vị trí của các hàm, có thể thấy mã nhị phân được biên dịch của hàm true.c / used () thực sự là 833 byte và của hàm true.c / main () là 225 byte, gần như ít hơn 1KB. Logic cho các chức năng phiên bản, được chôn trong các thư viện tĩnh, là khoảng 1KB.

Chuỗi chính () + cách sử dụng () + phiên bản () + chuỗi + vars được biên dịch thực tế chỉ sử dụng tối đa khoảng 3KB đến 3,5KB.

Thật là mỉa mai, những tiện ích nhỏ và khiêm tốn như vậy đã trở nên lớn hơn về kích thước vì những lý do đã giải thích ở trên.

Câu hỏi liên quan: Hiểu những gì một nhị phân Linux đang làm

true.c main () với các lệnh gọi hàm vi phạm:

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

Kích thước thập phân của các phần khác nhau của nhị phân:

$ 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

Đầu ra của 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)

Đầu ra của objdump -T true(các chức năng bên ngoài được liên kết động theo thời gian chạy)

$ 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
Gần đây đã thực hiện một số chương trình với bộ vi điều khiển 64kB + 2kB, 28kB dường như không quá nhỏ ..
Barleyman

1
@Barleyman bạn có OpenWRT, yocto, uClinux, uclib, busybox, microcoreutils và các giải pháp khác cho loại môi trường đó. Chỉnh sửa bài viết với mối quan tâm của bạn.
Rui F Ribeiro

4
@Barleyman: Nếu bạn được tối ưu hóa cho kích thước thực thi nhị phân, bạn có thể thực hiện truehoặc falsevới 45 byte x86 ELF thực thi, đóng gói mã thực thi (4 lệnh x86) bên trong (! Mà không cần sự hỗ trợ cho bất kỳ tùy chọn dòng lệnh) tiêu đề chương trình ELF . Hướng dẫn về cơn lốc trong việc tạo các tệp thực thi ELF thực sự dành cho Linux . (Hoặc lớn hơn một chút nếu bạn muốn tránh tùy thuộc vào chi tiết triển khai trình tải ELF của Linux: P)
Peter Cordes

3
Không thực sự, không. Ví dụ, Yocto có thể được nhồi nhét vào dưới một megabyte, chất đống trên 64kB .. Trong loại thiết bị này, bạn có thể sử dụng RTOS một số loại với quản lý bộ nhớ / quy trình thô sơ nhưng ngay cả những thiết bị đó có thể dễ dàng trở nên quá nặng. Tôi đã viết một hệ thống đa luồng hợp tác đơn giản và sử dụng bảo vệ bộ nhớ tích hợp để bảo vệ mã khỏi bị ghi đè. Tất cả nói với phần sụn tiêu thụ khoảng 55kB ngay bây giờ vì vậy không có quá nhiều chỗ để có thêm chi phí. Những người
khổng lồ

2
@PeterCordes chắc chắn nhưng bạn cần một số lượng lớn tài nguyên hơn trước khi Linux trở nên khả thi. Đối với những gì nó có giá trị, C ++ cũng không thực sự hoạt động trong môi trường đó. Vâng, không phải thư viện tiêu chuẩn nào. Iostream có giá khoảng 200kB, v.v.
Barleyman

34

Việc thực hiện có lẽ đến từ coreutils GNU. Các nhị phân này được tổng hợp từ C; không có nỗ lực cụ thể nào được thực hiện để làm cho chúng nhỏ hơn so với chúng theo mặc định.

Bạn có thể cố gắng biên dịch việc triển khai tầm thường của truechính mình và bạn sẽ nhận thấy nó có kích thước vài KB. Ví dụ: trên hệ thống của tôi:

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

Tất nhiên, nhị phân của bạn thậm chí còn lớn hơn. Đó là bởi vì họ cũng hỗ trợ các đối số dòng lệnh. Hãy thử chạy /usr/bin/true --helphoặc /usr/bin/true --version.

Ngoài dữ liệu chuỗi, nhị phân bao gồm logic để phân tích các cờ dòng lệnh, v.v. Điều đó làm tăng thêm khoảng 20 KB mã, rõ ràng.

Để tham khảo, bạn có thể tìm mã nguồn tại đây: http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/true.c


2
FYI Tôi đã phàn nàn về những triển khai coreutils này trên trình theo dõi lỗi của họ, nhưng không có cơ hội để sửa nó trong danh sách.gnu.org/archive/html/orms
coreutils/2016-03/msg00040.html

7
Nó không phải là logic cho các đối số, C không phảikhông hiệu quả ... là các thư viện nội tuyến / nhiệm vụ giữ nhà. Có một cái nhìn vào câu trả lời của tôi cho các chi tiết gory.
Rui F Ribeiro

8
Điều này gây hiểu lầm bởi vì nó gợi ý rằng mã máy được biên dịch (từ C hoặc nói cách khác) là thứ chiếm dung lượng lớn - chi phí kích thước thực tế có liên quan nhiều hơn với số lượng lớn thư viện C / thư viện thời gian chạy chuẩn được trình biên dịch nhập vào để tương tác với thư viện C (glibc, trừ khi bạn nghe nói rằng hệ thống của bạn sử dụng một cái gì đó khác, có lẽ), và ở mức độ thấp hơn, các tiêu đề / siêu dữ liệu ELF (rất nhiều trong số đó không thực sự cần thiết, nhưng được coi là đủ giá trị để bao gồm trong các bản dựng mặc định).
mtraceur

2
Chuỗi chính () + cách sử dụng () + trên cả hai hàm là khoảng 2KB, không phải 20KB.
Rui F Ribeiro

2
Logic @JdeBP cho các chức năng --version / phiên bản 1KB, --usage / - trợ giúp 833 byte, main () 225 byte và toàn bộ dữ liệu tĩnh của nhị phân là 1KB
Rui F Ribeiro

25

Tước chúng xuống chức năng cốt lõi và viết bằng trình biên dịch chương trình mang lại các nhị phân nhỏ hơn nhiều.

Các nhị phân đúng / sai gốc được viết bằng C, do bản chất của nó kéo theo các tham chiếu biểu tượng + thư viện khác nhau. Nếu bạn chạy readelf -a /bin/truenày là khá đáng chú ý.

352 byte cho một tệp thực thi tĩnh ELF bị tước (có chỗ để lưu một vài byte bằng cách tối ưu hóa mã asm cho kích thước mã).

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

Hoặc, với một chút cách tiếp cận khó chịu / khéo léo (kudos to stalkr ), hãy tạo các tiêu đề ELF của riêng bạn, giảm xuống còn 132 127 byte. Chúng tôi đang vào lãnh thổ Code Golf ở đây.

$ 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
Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
terdon

2
Ngoài ra, hãy xem bài viết tuyệt vời này: muppetlabs.com/~breadbox/software/tiny/teensy.html
mic_e

3
Bạn đang sử dụng int 0x80ABI 32 bit trong tệp thực thi 64 bit, điều này không bình thường nhưng được hỗ trợ . Sử dụng syscallsẽ không giúp bạn tiết kiệm bất cứ điều gì. Các byte cao ebxđược bỏ qua, vì vậy bạn có thể sử dụng 2 byte mov bl,1. Hoặc tất nhiên là xor ebx,ebxkhông . Linux trong tệp thanh ghi số nguyên bằng không, vì vậy bạn có thể chỉ inc eaxđể có được 1 = __NR_exit (i386 ABI).
Peter Cordes

1
Tôi đã cập nhật mã trên ví dụ đánh gôn của bạn để sử dụng ABI 64 bit và đánh mã xuống còn 127 byte cho true. (Tôi không thấy một cách dễ dàng để quản lý ít hơn 128 byte cho false, tuy nhiên, khác hơn bằng cách sử dụng 32-bit ABI hoặc lợi dụng thực tế là Linux Zeros thanh ghi vào quá trình khởi động, vì vậy mov al,252(2 byte) hoạt động. push imm8/ pop rdiSẽ cũng hoạt động thay vì leacài đặt edi=1, nhưng chúng tôi vẫn không thể đánh bại ABI 32 bit, nơi chúng tôi có thể mov bl,1không có tiền tố 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

Khá lớn trên Ubuntu 16.04 của tôi. chính xác cùng kích thước? Điều gì làm cho chúng lớn như vậy?

strings $(which true)

(trích :)

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'

À, có sự giúp đỡ cho đúng và sai, vì vậy hãy thử nó:

true --help 
true --version
#

Không có gì. À, có dòng khác:

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

Vì vậy, trên hệ thống của tôi, nó / bin / true, không phải / 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.

Vì vậy, có sự giúp đỡ, có thông tin phiên bản, ràng buộc với một thư viện để quốc tế hóa. Điều này giải thích phần lớn kích thước và shell sử dụng lệnh được tối ưu hóa của nó bằng mọi cách và hầu hết thời gian.


Bao gồm các thư viện tĩnh và một nửa kích thước của nhị phân cho elf metada. Xem câu trả lời của tôi.
Rui F Ribeiro
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.