Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
May 2, 2021 03:50 pm GMT

Why is /usr/bin/test 4Kb smaller than /usr/bin/[?

A Reddit user named mathisweirdaf shared some interesting observations:

 $ ls -lh /usr/bin/{test,[}-rwxr-xr-x 1 root root 59K  Sep  5  2019 '/usr/bin/['-rwxr-xr-x 1 root root 55K  Sep  5  2019  /usr/bin/test

[ and test should be aliases of each other, and yet there is a 4Kb difference between the GNU coreutils that execute them. Why?

First of all, for all those who were wondering: yes, there is /usr/bin/[. On this topic I have a separate article, but I will explain briefly:
When you write if [ -e /etc/passwd ]; then . this parenthesis does not act as a shell syntax, but just a standard command with a fancy name. It is usually built into the shell, but can sometimes be implemented through /usr/bin/[. This explains much of its mysterious behavior, such as why it is sensitive to spaces: [1=2] turns out to be no more valid than ls-l/tmp.

Nevertheless, where does the difference in size come from? You can compare the objdump output to see where the data fits. Here is an excerpt from objdump -h /usr/bin/[:

              size                                          offset15 .text         00006e82  0000000000002640  0000000000002640  00002640  2**416 .fini         0000000d  00000000000094c4  00000000000094c4  000094c4  2**217 .rodata       00001e4c  000000000000a000  000000000000a000  0000a000  2**5

But objdump -h /usr/bin/test:

15 .text         000068a2  0000000000002640  0000000000002640  00002640  2**416 .fini         0000000d  0000000000008ee4  0000000000008ee4  00008ee4  2**217 .rodata       00001aec  0000000000009000  0000000000009000  00009000  2**5

Here we see that the .text segment (the compiled executable code) is 1504 bytes larger, while the .rodata (constant values and strings) is 864 bytes larger.

The bottom line is that the increased size of the .text segment forces it to move from 8000 to 9000, crossing the page size boundary of 0x1000 (4096) and therefore shifting all other segments by 4096 bytes. It is this difference in size that we observe.

The only nominal difference between [ and test is that [ requires ] as a final argument. Testing this would require a minimal amount of code, so why are those ~1500 bytes used after all?

Since it's hard to review compiled binaries, I put together a copy of coreutils and compared the list of functions in each:

$ diff -u <(nm -S --defined-only src/[ | cut -d ' ' -f 2-) <(nm -S --defined-only src/test | cut -d ' ' -f 2-)--- /dev/fd/63      2021-02-02 20:21:35.337942508 -0800+++ /dev/fd/62      2021-02-02 20:21:35.341942491 -0800@@ -37,7 +37,6 @@ D __dso_handle d _DYNAMIC D _edata-0000000000000099 T emit_bug_reporting_address B _end 0000000000000004 D exit_failure 0000000000000008 b file_name@@ -63,7 +62,7 @@ 0000000000000022 T locale_charset 0000000000000014 T __lstat 0000000000000014 t lstat-0000000000000188 T main+00000000000000d1 T main 000000000000000b T make_timespec 0000000000000004 d nslots 0000000000000022 t one_argument@@ -142,16 +141,10 @@ 0000000000000032 T umaxtostr 0000000000000013 t unary_advance 00000000000004e5 t unary_operator-00000000000003d2 T usage+0000000000000428 T usage 0000000000000d2d T vasnprintf 0000000000000013 T verror 00000000000000ae T verror_at_line-0000000000000008 D Version-00000000000000ab T version_etc-0000000000000018 T version_etc_ar-000000000000042b T version_etc_arn-000000000000002f R version_etc_copyright-000000000000007a T version_etc_va 000000000000001c r wide_null_string.2840 0000000000000078 T x2nrealloc 000000000000000e T x2realloc

The main contributors are the version_etc* functions. What do they do?
Let's take a look:

/* The three functions below display the --version information the   standard way [...]

These are the 260 lines of expanded, internalized, conditional data formatting methods that make up the --version output. All together they take up about bc << "ibase=16; 7A+2F+42B+18+AB+8+99" = 1592 bytes.

What does it mean? It's simple. The extra 4Kb goes to this:

$ /usr/bin/[ --version[ (GNU coreutils) 8.30Copyright (C) 2018 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <https://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 Kevin Braunsdorf and Matthew Bradburn.

[ --version is missing the final ], so the call is invalid and the result is determined by the implementation. GNU quietly allows version information to be output.

Meanwhile, /usr/bin/test --version turns out to be a valid call and POSIX dictates that it should return success when the first parameter (--version) is a nonempty string.

This difference is even mentioned in the documentation:

Note: [ is responsible for --help and --version options, while test is not.test treats each of them simply as a non-empty string.

The puzzle is solved!


Original Link: https://dev.to/magsguru/why-is-usr-bin-test-4kb-smaller-than-usr-bin-491g

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To