Buffered I/O and synchronization primitives to avoid corrupting the buffer. The underlying vector implementation and memory allocator. Panic support and backtrace printer, which includes a Rust-specific name demangler and a not-so-small DWARF parser in Linux. Path support because backtraces would include source file paths. Zlib-compatible decompressor because ELF allows compressed sections (!). Then you have several formatters that are often pretty large (e.g. f32 and f64 would add at least 30 KB of binary, and some depend on Unicode grapheme clusters). They are all essential for edge cases that can happen even for such a simple program.
> several formatters that are often pretty large (e.g. f32 and f64 would add at least 30 KB of binary …)
I know floats are full of scary corner cases, but… Assuming tens of bytes per an if statement, hundreds of corner cases just in formatting? Is it really that bad?
A naive implementation would be small, but a correct implementation (with some concrete definition of "correct") will need a sizable data table. I know this because I wrote that part of code [1] and somehow it is still in the std even though other algorithms now exist [2].
The stack frame is a list of return addresses which have to be translated to a file name and line number. Such debug information is within a specific ELF section in the predefined format. In this simple case you may be able to hard-code the offset to the section (and guarantee that it was never compressed), but any additional C or Rust library will break this assumption, so a general parser has to be included.
Sorry if this is dumb but if the idea is stripping debug stuff, why would a parser for translating return addresses into file names and line numbers be useful?
EDIT: oh, ok, so I guess it's because strip is "debuginfo" here, rather than "true".