AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 419697
Accepted
Kidburla
Kidburla
Asked: 2018-01-26 12:14:47 +0800 CST2018-01-26 12:14:47 +0800 CST 2018-01-26 12:14:47 +0800 CST

为什么真假这么大?

  • 772

在发现几个常用命令(read如对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$

然而,令我惊讶的是,这两个文件的大小都超过了 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$

所以我的问题是:它们为什么这么大?除了返回码之外,可执行文件中还有什么?

PS:我使用的是 RHEL 7.4

linux reverse-engineering
  • 5 5 个回答
  • 25560 Views

5 个回答

  • Voted
  1. Best Answer
    Rui F Ribeiro
    2018-01-26T12:50:28+08:002018-01-26T12:50:28+08:00

    过去,/bin/true在/bin/falseshell 中实际上是脚本。

    例如,在 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命令被实现为 shell 内置命令。因此,在命令行和 shell 脚本中使用falseandtrue指令时,默认情况下不会调用可执行二进制文件。bash

    从bash源头来看,builtins/mkbuiltins.c:

    字符 *posix_builtins[] =
        {
          “别名”、“bg”、“cd”、“command”、“**false**”、“fc”、“fg”、“getopts”、“jobs”、
          “kill”、“newgrp”、“pwd”、“read”、“**true**”、“umask”、“unalias”、“wait”、
          (char *)NULL
        };
    

    同样根据@meuh 评论:

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

    所以可以高度肯定地说true,false可执行文件的存在主要是为了被其他程序调用。

    从现在开始,答案将集中在 Debian 9 / 64 位软件包中的/bin/true二进制文件上。coreutils(/usr/bin/true运行 RedHat。RedHat 和 Debian 都使用了该 coreutils软件包,分析了后者的编译版本,更容易掌握)。

    正如在源文件中可以看到的false.c,/bin/false使用(几乎)相同的源代码编译/bin/true,只是返回 EXIT_FAILURE (1),所以这个答案可以应用于两个二进制文件。

    #define EXIT_STATUS EXIT_FAILURE
    #include "true.c"
    

    因为它也可以通过具有相同大小的两个可执行文件来确认:

    $ ls -l /bin/true /bin/false
    -rwxr-xr-x 1 root root 31464 Feb 22  2017 /bin/false
    -rwxr-xr-x 1 root root 31464 Feb 22  2017 /bin/true
    

    唉,答案的直接问题why are true and false so large?可能是,因为不再有如此紧迫的理由来关心他们的最佳表现。它们对bash性能不是必需的,不再被bash(脚本)使用。

    类似的评论也适用于它们的大小,对于我们现在拥有的那种硬件来说,26KB 是微不足道的。对于典型的服务器/桌面来说,空间不再是宝贵的,他们甚至不再费心为falseand使用相同的二进制文件true,因为它只是在使用coreutils.

    然而,本着这个问题的真正精神,为什么应该如此简单和小的东西会变得如此之大?

    各部分的实际分布/bin/true如下图所示;主要代码+数据在 26KB 二进制文件中大约占 3KB,占/bin/true.

    true多年来,该实用程序确实获得了更多杂乱无章的代码,最引人注目的是对--version和的标准支持--help。

    然而,它不是(唯一)它如此大的主要理由,而是在动态链接(使用共享库)的同时,还具有coreutils作为静态库链接的二进制文件常用的通用库的一部分。用于构建elf可执行文件的元数据也占二进制文件的很大一部分,按照今天的标准,它是一个相对较小的文件。

    其余的答案是解释我们如何构建以下图表,详细说明/bin/true可执行二进制文件的组成以及我们如何得出该结论。

    宾真 bintrue2

    正如@Maks 所说,二进制文件是从 C 编译的;根据我的评论,也证实它来自 coreutils。我们直接指向作者 git https://github.com/wertarbyte/coreutils/blob/master/src/true.c,而不是像 @Maks 那样的 gnu git(相同的来源,不同的存储库 - 这个存储库被选中是因为它具有coreutils库的完整来源)

    我们可以在这里看到二进制文件的各种构建块/bin/true(Debian 9 - 64 bits from 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;存在于:

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

    那些未在外部链接的额外功能main()是:

    • set_program_name()
    • close_stdout()
    • 版本等()

    所以我的第一个怀疑是部分正确的,当库使用动态库时,/bin/true二进制文件很大*因为它包含一些静态库*(但这不是唯一的原因)。

    编译 C 代码通常不会因为这样的空间下落不明而效率低下,因此我最初怀疑有些不对劲。

    额外的空间,几乎是二进制文件大小的 90%,确实是额外的库/elf 元数据。

    在使用 Hopper 反汇编/反编译二进制以了解函数在哪里时,可以看到 true.c/usage() 函数的编译二进制代码实际上是 833 字节,而 true.c/main() 函数的编译二进制代码是 225字节,大约略小于 1KB。隐藏在静态库中的版本函数的逻辑大约为 1KB。

    实际编译的 main()+usage()+version()+strings+vars 只用了大约 3KB 到 3.5KB。

    确实具有讽刺意味的是,由于上述原因,如此小而不起眼的公用事业变得更大了。

    相关问题:了解 Linux 二进制文件在做什么

    true.cmain() 与有问题的函数调用:

    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
    
    • 153
  2. Maks Verver
    2018-01-26T12:29:47+08:002018-01-26T12:29:47+08:00

    该实现可能来自 GNU coreutils。这些二进制文件是从 C 编译的;没有特别努力使它们小于默认值。

    您可以尝试编译true自己的简单实现,您会注意到它的大小已经只有几 KB。例如,在我的系统上:

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

    当然,您的二进制文件更大。那是因为它们还支持命令行参数。尝试运行/usr/bin/true --help或/usr/bin/true --version.

    除了字符串数据外,二进制文件还包括解析命令行标志等的逻辑。显然,这加起来大约有 20 KB 的代码。

    作为参考,您可以在此处找到源代码:http: //git.savannah.gnu.org/cgit/coreutils.git/tree/src/true.c

    • 37
  3. steve
    2018-01-26T13:05:30+08:002018-01-26T13:05:30+08:00

    Stripping them down to core functionality and writing in assembler yields far smaller binaries.

    Original true/false binaries are written in C, which by its nature pulls in various library + symbol references. If you run readelf -a /bin/true this is quite noticeable.

    352 bytes for a stripped ELF static executable (with room to save a couple bytes by optimizing the asm for code-size).

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

    Or, with a bit of a nasty/ingenious approach (kudos to stalkr), create your own ELF headers, getting it down to 132 127 bytes. We're entering Code Golf territory here.

    $ 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
    $
    
    • 26
  4. user unknown
    2018-02-15T05:22:56+08:002018-02-15T05:22:56+08:00
    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
    

    Pretty big on my Ubuntu 16.04 too. exactly the same size? What makes them so big?

    strings $(which true)
    

    (excerpt:)

    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, there is help for true and false, so let's try it:

    true --help 
    true --version
    #
    

    Nothing. Ah, there was this other line:

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

    So on my system, it's /bin/true, not /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.
    

    So there is help, there is version information, binding to a library for internationalization. This explains much of the size, and the shell uses its optimized command anyway and most of the time.

    • 2
  5. R.. GitHub STOP HELPING ICE
    2020-01-14T18:50:36+08:002020-01-14T18:50:36+08:00

    The accepted answer by Rui F Ribeiro gives a lot of good information, but it's missing some and I feel like it leaves the reader with a misleading impression that the code size is small relative to "ELF overhead" and similar.

    So my first suspicion was partly correct, whilst the library is using dynamic libraries, the /bin/true binary is big *because it has some static libraries included with it* (but that is not the only cause).

    Static linking is at object file granularity (or even finer with --gc-sections), so it doesn't make sense to talk about a static library being "linked in"; the only part linked in is what's used, and the code size here is code that's actually (gratuitously) used by the coreutils version of true. It does not make sense to count it separately from what appears in true.c.

    The extra space, almost 90% of the size of the binary, is indeed extra libraries/elf metadata.

    The ELF metadata is pretty much entirely tables necessary for dynamic linking, and is nowhere near 90% of the size. These are the lines from size -A output relevant to it:

    section               size      addr
    .interp                 28       568
    .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
    .plt                   752      4800
    .plt.got                 8      5552
    .dynamic               480   2125272
    .got                    48   2125752
    .got.plt               392   2125824
    

    for a total of around 5.5k, or an average of about 100 bytes per dynamic symbol (not quite fair because some aren't on a per-symbol basis, but it's a somewhat meaningful figure).

    One large contributor to size that Rui's answer didn't cover is:

    .eh_frame_hdr          572     21728
    .eh_frame             2908     22304
    

    This 3.5k is DWARF unwind tables for C++ exception handling, introspective backtrace, etc. They're completely useless for true, but included by GCC policy in all output unless you explicitly omit them with a very verbosely-named option -fno-asynchronous-unwind-tables. The motivations behind this are explained in an answer by me on Stack Overflow.

    So I would describe the final breakdown as:

    • 16k of program code and read-only (shareable) data
    • 5.5k of dynamic linking glue
    • 3.5k of unwind tables
    • <1k misc

    特别值得注意的是,如果动态链接库所需的代码量足够少,静态链接可能比动态链接少。

    • 2

相关问题

  • 有没有办法让 ls 只显示某些目录的隐藏文件?

  • 使用键盘快捷键启动/停止 systemd 服务 [关闭]

  • 需要一些系统调用

  • astyle 不会更改源文件格式

  • 通过标签将根文件系统传递给linux内核

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    ssh 无法协商:“找不到匹配的密码”,正在拒绝 cbc

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    如何卸载内核模块“nvidia-drm”?

    • 13 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Wong Jia Hau ssh-add 返回:“连接代理时出错:没有这样的文件或目录” 2018-08-24 23:28:13 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Bagas Sanjaya 为什么 Linux 使用 LF 作为换行符? 2017-12-20 05:48:21 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve