[HN Gopher] How is a binary executable organized? Let's explore ...
       ___________________________________________________________________
        
       How is a binary executable organized? Let's explore it (2014)
        
       Author : tripdout
       Score  : 460 points
       Date   : 2024-02-02 17:43 UTC (1 days ago)
        
 (HTM) web link (jvns.ca)
 (TXT) w3m dump (jvns.ca)
        
       | jstrieb wrote:
       | As I've said in other threads
       | https://news.ycombinator.com/item?id=38847750#38862450, I highly
       | recommend writing an ELF by hand at least once. It's a great
       | exercise to understand the basic parts of an executable. It's
       | also helpful if you want to go the opposite direction of this
       | article - bottom-up instead of top-down.
       | 
       | Lots of other great discussion in various threads on that other
       | HN post.
        
         | Retr0id wrote:
         | Similarly, I'd recommend writing a simple ELF loader. There's a
         | fair bit of implementation complexity in dynamic linking, but
         | if you only support static ELFs then it's straight-forward.
        
           | mkoubaa wrote:
           | I've seriously considered writing an ELF loader that uses a
           | special symbol (like _resolve) where dynamic library
           | resolving is done imperatively. The flexibility from libdl
           | always feel underwhelming.
        
           | Isamu wrote:
           | Yes, likewise I wrote a reader that simply tried to parse
           | every bit of a complex ELF binary to report its structure and
           | quickly found myself in poorly documented territory. It's an
           | education if you want it.
        
             | eichin wrote:
             | I assume there's a better modern source (assuming for some
             | reason you don't want to reference libbfd &c, but really,
             | you at least want to cross-check with it) but BITD there
             | was an AT&T System V book - I _think_ it was
             | https://books.google.com/books?id=mrImAAAAMAAJ but only at
             | about 70% confidence, it's part of a series and might have
             | been one of the others - that was "arcane but true" for ELF
             | on existing platforms (at the time, which was the mid
             | 1990s, which is why I _hope_ there 's a better starting
             | point now...)
        
           | matheusmoreira wrote:
           | Any good resources on the matter? I'm gonna need to write a
           | fully featured ELF loader for my language soon. I need to
           | prepare.
        
           | toast0 wrote:
           | I had written a static ELF loader for reasons, but when I was
           | no longer to compile a static version of the binary I wanted
           | to load, I found it wasn't too hard to load the system's
           | dynamic loader instead. That's kind of the best of both
           | worlds --- I can run a dynamicly linked binary, and I didn't
           | have to do the linking and relocations.
        
         | akdas wrote:
         | Writing an ELF file by hand is something I did recently:
         | https://github.com/avik-das/garlic/blob/master/recursive/elf...
         | 
         | To explain the format to myself and others, I also created an
         | interactive visualization for the bytes in the file. It helps
         | me to click on a byte, see an explanation for it and see
         | related bytes elsewhere in the file highlighted.
         | https://scratchpad.avikdas.com/elf-explanation/elf-explanati...
        
           | bruce343434 wrote:
           | That's an awesome interactive page! Did you write it by hand,
           | or did you use some sort of generator/tool?
        
             | sunk1st wrote:
             | I agree it's very nice. I'd also like to know how it was
             | done.
             | 
             | Also if you click two bytes that are in the same caption
             | group one after another then it bugs out.
        
               | akdas wrote:
               | Thanks for the feedback. I replied in a sibling comment
               | about how I made it.
               | 
               | For the bug, feel free to email me at avik at avikdas dot
               | com if you'd like. The behavior I verified just now (for
               | me) is that if you click one byte to highlight it, then
               | clicking any other byte in the same group will remove the
               | highlighting.
        
             | akdas wrote:
             | I wrote it all by hand :)
             | 
             | Lately, I've been using Svelte for interactive
             | visualizations (see my post on using a tool called Astro
             | with Svelte: https://avikdas.com/2023/12/30/interactive-
             | demos-using-astro... ). But this one is all hand-written
             | JS!
        
               | matheusmoreira wrote:
               | You and your web site are huge inspirations for me.
        
               | akdas wrote:
               | Thank you! That means a lot to me
        
           | ddingus wrote:
           | Wow! Very nice work. This is super educational resource.
           | Again nice work.
        
           | cqqxo4zV46cp wrote:
           | I've had such fun making interactive educational
           | visualisations like this. My life's work is going into an
           | interactive simulation of the USB protocol. Unfortunately I'm
           | yet to bang it out over a weekend.
        
           | raarts wrote:
           | Wow. Cool! I'm a CS teacher and definitely going to use this.
           | Thanks for your work! (Anyone aware of a Windows or Mac
           | equivalent?)
        
             | akdas wrote:
             | Thank you! My passion is teaching, and I created
             | interactive visualizations for a CS theory class I taught:
             | https://cstheory.avikdas.com/
             | 
             | If you're interested in collaborating for your classes,
             | reach out to avik at avikdas dot com
        
         | mito88 wrote:
         | is ELF unique to x86/x64?
        
           | inkyoto wrote:
           | It is not, and it has never been.
           | 
           | ELF is platform agnostic, and has been used in operating
           | systems on nearly every existing CPU platform since mid 90's
           | (with a few notable exceptions being OS X, AIX, the embedded
           | world and Windows).
        
         | ddingus wrote:
         | Seconded.
         | 
         | Doing it is basically a hand assembly. One reads the
         | documentation, selects the bytes needed using a processor data
         | sheet, orders them into the various sections, populates the ELF
         | fields and then it really does boil down to typing them all in.
         | 
         | Pre-ELF times, on say an 8 bit Apple 2, the machine code
         | monitor, allowed input of the program bytes directly. Those are
         | then executed.
         | 
         | Storing to disk is only a bit more involved, and there is
         | another opportunity! Disk sector editors allow one to create a
         | file...
         | 
         | ...and so it goes!
        
         | matheusmoreira wrote:
         | Modifying existing ELFs can also be extremely educational and
         | fun. It's a bit frustrating at first because it's more or less
         | impossible to debug this stuff when it doesn't work but when
         | things finally start working it's awesome. Turns out it's
         | possible to patch ELFs in all sorts of interesting ways. With
         | the auxiliary vector you can even have introspection at
         | runtime: Linux gives us the address of the program header table
         | and from there you can get to anywhere. Just gotta extend a
         | LOAD segment to cover the whole binary.
         | 
         | For example I wrote tools to embed lisp modules and code right
         | into my lisp's interpreter executable. The embedded segment is
         | loaded from the ELF automatically, the interpreter just finds
         | it and runs the code. I'm so proud of this little feature I
         | wrote an article about it.
         | 
         | https://www.matheusmoreira.com/articles/self-contained-lone-...
         | 
         | Would be cool if mainstream languages adopted this method.
        
           | kazinator wrote:
           | These tricks can be easily done simply and portably, without
           | caring about the underlying executable format.
           | 
           | One trick is that you can reserve some global array in the
           | executable, which is prefixed by a byte sequence that doesn't
           | occur anywhere. A small utility can find that byte sequence
           | and write custom data after it to create a customized
           | executable.
           | 
           | I think that, also, many executable formats don't mind if
           | something is appended to the executable. If the executable
           | somehow knows its original size (you can write that size
           | somewhere using the previous trick: no grotty executable
           | format parsing required), it can open itself, seek to that
           | offset and read the data.
           | 
           | I think this might be how CLISP creates an .exe file on
           | Windows; I think it takes the base clisp.exe and combines it
           | with the lispinit.mem image into one file.
        
             | matheusmoreira wrote:
             | > reserve some global array in the executable
             | 
             | This is neat but has a limitation in that it cannot be
             | expanded after the program is compiled and linked. Resizing
             | the array would invalidate all pointers that follow as well
             | as render incorrect any code that takes its size.
             | 
             | This can be solved with a layer of indirection: just append
             | the data to the executable and write its size and file
             | offset in the array. That way the data block can be freely
             | resized. That's the solution people told me to use and
             | indeed the one that I usually see in existing repositories.
             | The problem is you run into some additional complexity
             | later which results in the loss of portability and thus the
             | main reason to choose this method.
             | 
             | > it can open itself
             | 
             | That's the crux of the issue. How does an executable open
             | itself? That's where portability goes out the window. I've
             | seen source code that opens argv[0] which is under the
             | control of the parent program and therefore unreliable.
             | I've seen code which opens /proc/self/exe which is Linux
             | specific. I've seen code that calls Win32 API functions to
             | get the path to the executable. All this just so it can
             | open and load into memory a file which the kernel has
             | already loaded, just so it can read some additional data
             | off of it.
             | 
             | My solution sidesteps that question entirely. It just adds
             | a LOAD segment for the embedded data which instructs the
             | kernel to map it in automatically before the program even
             | runs. There's no need to open, seek or read anything, it
             | will already be there by the time execution begins.
             | 
             | The auxiliary vector contains a pointer to the program's
             | segments table so it can reach the data from there. Then
             | it's just a matter of walking this table looking for the
             | custom descriptor segment. It's all done in a structured
             | way, using the standard magic number locations and ranges.
             | There's no chance of a magic number being recognized by
             | mistake.
             | 
             | The only possible portability issue is the availability of
             | the AT_PHDR, AT_PHENT and AT_PHNUM entries in the auxiliary
             | vector. I'm not sure if they're standard. I know Linux has
             | them and it's all I personally care about but if these
             | entries do turn out to be standard then I can confidently
             | say that my method is portable to any ELF-based operating
             | system.
        
               | kazinator wrote:
               | > _How does an executable open itself? That 's where
               | portability goes out the window._
               | 
               | It's a way easier nut to crack than parsing executable
               | formats. Various OSs have a method for this:
               | 
               | Linux: /proc/self/exe
               | 
               | Windows: GetModuleFileNameW(NULL, buf, MAX_PATH)
               | 
               | MacOS: _NSGetExecutablePath(buf, &size)
               | 
               | Solaris: getexecname()
               | 
               | BSDs: ...?
               | 
               | These are just "ask and you shall receive" APIs; nothing
               | to analyze.
               | 
               | You may want to solve that problem _anyway_ for other
               | reasons: for instance, if you want your programming
               | language to have a relocatable installation which finds
               | related files using its own path.
               | 
               | On a system where you cannot solve this problem at all,
               | you can use trick 1. Have an array somewhere in the
               | program where you write the installation path. Your
               | installer has to do this, and if the program is moved
               | after installation, some utility program has to fix that
               | up. (I am not crazy about the idea because it changes the
               | checksum of the file at the installation site. You can no
               | longer use an off-the-shelf program like sha256sum to
               | check the integrity of the executable against the
               | provisioned materials upstream.)
        
             | versteegen wrote:
             | Put the offset at the end! Famously, the table of contents
             | of a .zip file are the end rather than the beginning, which
             | has many useful properties (such as being able to patch the
             | contents by only appending to an existing file). And you
             | can concatenate an executable and a .zip and get a file
             | which is both.
        
               | matheusmoreira wrote:
               | Yes. Cosmopolitan libc has support for exactly this. It
               | contains a lot of platform-specific hacks in order to
               | open the executable though. I went through the
               | implementation.
               | 
               | I think the problem is this notion of a "memory image".
               | It would be so much easier if the kernel just copied the
               | entire file into memory and called it a day.
        
         | cylinder714 wrote:
         | Chris Wellons' "A Magnetized Needle and a Steady Hand," a piece
         | on building an ELF executable from scratch:
         | 
         | https://nullprogram.com/blog/2016/11/17/
        
       | sva_ wrote:
       | > When the program starts running, you might think it starts at
       | main. It doesn't! It actually goes to _start. This does a bunch
       | of Very Important Things that I don't understand very well,
       | including calling main. So I won't explain them.
       | 
       | The way I understand it, the symbol main is a C-specific thing.
       | The symbol _start is a language-agnostic entry point for the
       | binary that will in this case call main.
       | 
       | A convention of i.e. calling the entry point _start with main's
       | argc/argv would make the format a lot less flexible.
        
         | a-priori wrote:
         | Technically the name _start is not special either. The binary
         | lists its entry point address in a header and that's where the
         | OS starts execution from. That symbol is just called _start by
         | convention by C and other languages, which is what the linker
         | uses to set the entry point when writing the ELF headers, but
         | if you're writing your own linker scripts you could call the
         | entry point whatever you want.
        
           | Keyframe wrote:
           | to extend on it, _start is where .text begins and address of
           | that is set by linker
        
             | a-priori wrote:
             | The entry point can be anywhere in the .text section, and
             | often won't be at the beginning of the section.
        
               | Retr0id wrote:
               | Technically it doesn't even need to be in the .text
               | section, it could be anywhere in the address space.
               | You'll get a segfault if it's not somewhere executable
               | though (assuming you're on a system with an appropriately
               | configured MMU)
        
               | Keyframe wrote:
               | yes and then you'll have a bad time, but at the same time
               | per convention _start is where .text begins. You can see
               | where it starts with readelf --file-header <executable>
               | and look at Entry point address field. You can change it,
               | yes.
        
               | logdahl wrote:
               | A common hack to reduce ELF size is actually to start the
               | first section (possibly the .text) right on the elf
               | header, as this circumvents the alignment requirements.
        
               | Keyframe wrote:
               | probably not even mandatory.. lots of /usr/bin stuff on
               | my ubuntu machine have __libc_start_main only
        
               | Retr0id wrote:
               | No, it's not even a convention, _start is most commonly
               | _not_ where .text begins.
               | 
               | Compiling a static hello world binary on my system
               | (aarch64 fedora 39, gcc -static hello.c -o hello), .text
               | starts at 0x410080, e_entry is at 0x4103c0, and the
               | _start symbol is also at 0x4103c0. This is not unusual at
               | all.
        
         | vlovich123 wrote:
         | > Things that I don't understand very well, including calling
         | main. So I won't explain them.
         | 
         | It depends on the language runtime, but a common task will be
         | initializing global non-0 statics. For languages like
         | Rust/C/C++ you can also inject variables to be initialized via
         | linker flags. Before start if the program is dynamically linked
         | then I believe the linker runtime is run to resolve the links
         | and then transfer control to _start.
         | 
         | Basically hacks on hacks on hacks added organically to offer
         | extensibility and the hacks have enough social adoption and are
         | good enough that we stick with them.
        
           | seanw444 wrote:
           | > Basically hacks on hacks on hacks added organically to
           | offer extensibility and the hacks have enough social adoption
           | and are good enough that we stick with them.
           | 
           | The more I learn about the deep depths of modern computing,
           | the more I realize that they're actually _full_ of inelegant
           | legacy cruft.
        
           | kccqzy wrote:
           | The style guide at both my previous and current employers
           | explicitly forbids having global non-0 statics for this exact
           | reason: code that runs before main() is very unusual. Many
           | assumptions do not hold.
           | 
           | A far better way is to use function-local statics. A static
           | variable inside a function is initialized when execution
           | reaches that point when the function is being called.
           | Furthermore, such initialization is thread safe so that one
           | initialization happens despite multiple concurrent calls of
           | the function.
           | 
           | The only exception to that style guide rule is the new
           | constinit in C++20. It is sometimes called linker-initialized
           | to make it even clearer that the program didn't do anything
           | to initialize it, the linker did.
        
             | cesarb wrote:
             | > Furthermore, such initialization is thread safe so that
             | one initialization happens despite multiple concurrent
             | calls of the function.
             | 
             | IIRC, there are some popular compilers in which
             | initialization of static variables inside functions is not
             | thread safe (even though AFAIK the C++ standard said they
             | should be).
        
               | vlovich123 wrote:
               | I'm not aware of this problem in MSVC, clang and gcc and
               | those are the most popular afaik.
        
               | cesarb wrote:
               | > I'm not aware of this problem in MSVC
               | 
               | The compiler I was thinking of was indeed MSVC. From a
               | quick web search, it seems that more recent versions of
               | MSVC have changed them to be thread safe by default, so
               | if you can make sure that your code will never be
               | compiled on older MSVC versions (and that nobody will
               | ever use the compiler option which disables the thread-
               | safe initialization), it might be fine to depend on it.
        
               | vlovich123 wrote:
               | True as of msvc 2015 which is 8.5 years old at this
               | point. I agree it's weird that you can disable it but
               | libraries retaining correctness in the face of random
               | compiler flags is hard (eg ffast-math is a common one
               | that can break your floating point library)
        
             | secondcoming wrote:
             | The issue with Meyers Singletons is that every time they're
             | accessed a flag must be checked first. This is bad in hop
             | loops.
        
               | vlovich123 wrote:
               | It's not as bad as you might think because the CPU should
               | speculate through it pretty easily.
        
           | matheusmoreira wrote:
           | > initializing global non-0 statics
           | 
           | Does that mean this doesn't work in a freestanding
           | environment? Yet another reason to assiduously avoid global
           | variables. I suppose that's why I never ran into this issue.
           | 
           | > Basically hacks on hacks on hacks
           | 
           | And then there's _this_ insanity right here:
           | 
           | https://blogs.oracle.com/solaris/post/init-and-fini-
           | processi...
           | 
           | I like to imagine that I won't have to actually implement
           | this when I write my ELF loader. Someone please tell me no
           | modern software uses this.
        
             | neerajsi wrote:
             | Wow, great read. I worked on the Windows DLL loader and we
             | had to implement similar mechanics for similar reasons. The
             | PE image format makes some part of this a little easier,
             | but the complexity is essentially the same.
        
             | vlovich123 wrote:
             | > Does that mean this doesn't work in a freestanding
             | environment? Yet another reason to assiduously avoid global
             | variables. I suppose that's why I never ran into this
             | issue.
             | 
             | Why do you say that? A freestanding environment will still
             | enter through _start and execute the compiler generated
             | code to initialize globals before main. What I can't recall
             | is how compile time non-0 values are initialized - I think
             | it could be part of the bss and initialized by the loader
             | instead (but freestanding environments would implement that
             | too as part of being a target for a language) but both them
             | and runtime initialized globals initialized between _start
             | and main would work.
             | 
             | Basically freestanding targets might not give you access to
             | runtime APIs (eg POSIX) but the language is still the
             | language and all features defined as language features
             | should work and it's the responsibility of the compiler and
             | target environment to provide that guarantee.
        
               | matheusmoreira wrote:
               | > Why do you say that?
               | 
               | Your post gave me the impression the compiler generates
               | initializers for static globals and calls them during
               | program initialization. Is this not correct?
               | 
               | > A freestanding environment will still enter through
               | _start and execute the compiler generated code to
               | initialize globals before main.
               | 
               | Freestanding C generally implies not linking in any
               | standard library code though. It doesn't make any sense
               | to choose the freestanding C dialect only to link in the
               | standard hosted C library.
               | 
               | The _start symbol is provided by the C library's so
               | called standard system startup files. The standard _start
               | calls all the initialization functions that the C program
               | expects. If you override it with your own _start, then
               | none of these functions will be called. I thought you
               | meant the static data initialization was handled in one
               | of these functions.
        
         | matheusmoreira wrote:
         | > the symbol main is a C-specific thing
         | 
         | Absolutely. And only available on hosted C. Freestanding C lets
         | you have any entry point you want.
         | 
         | > The symbol _start is a language-agnostic entry point for the
         | binary that will in this case call main.
         | 
         | That's just the linker's default. You can set it to a nicer
         | symbol with -Wl,--entry="${symbol}" and GCC even supports
         | setting it directly with no need for the unsightly -Wl.
         | 
         | Also, the entry point is actually a pointer, not a symbol. The
         | linker just takes the address of the symbol you specify and
         | sets the ELF entry point to that.
         | 
         | > calling the entry point _start with main's argc/argv
         | 
         | In addition to argument count and argument vector, the stack
         | also contains the environment vector and the auxiliary vector.
         | The process startup code can be as simple as popping all that
         | stuff off the stack and into the appropriate registers and then
         | calling a C function of your choosing. Note that the entry
         | point is not itself a function: there's nothing to return to.
         | The entry point code finishes with an exit system call to
         | ensure clean process termination when main returns a status
         | code. This is how things work on Linux at least.
        
       | zerotolerance wrote:
       | Julia's articles are always excellent. I've always had great
       | results teaching people that compiled code doesn't keep secrets
       | by demoing `strings`.
        
         | actionfromafar wrote:
         | Can you elaborate?
        
           | shzhdbi09gv8ioi wrote:
           | man strings
        
           | dilyevsky wrote:
           | If you put something like                 if mySecretPassword
           | == "Qwerty123" {          ...
           | 
           | then "Qwerty123" will be easily seen by strings utility.
           | Which is pretty obvious but I'm guessing some junior folks
           | will be surprised.
        
           | xutopia wrote:
           | You can run the `strings` command on most executables (or
           | PDFs) and get an output of the strings represented in the
           | file. Of course you can obfuscate some of those strings if
           | you do things right but a lot of people who don't know about
           | `strings` could write a password protected feature in a
           | compile bit of code and be embarrassed to see how easy it is
           | to find out what the password is.
        
           | zerotolerance wrote:
           | The other replies are pretty good. You can find all sorts of
           | goodies in string data inside a binary: hostnames, URL
           | fragments, error messages or templates, credentials. Pretty
           | much any string constants that a program might use.
        
         | sva_ wrote:
         | Explain that to the German judges that fined some poor fella
         | for finding passwords in a binary by [doing the equivalent of]
         | running strings on it. They claim he 'circumvented' the
         | software's 'security measures'.
         | 
         | https://www.theregister.com/2024/01/19/germany_fine_security...
        
       | actionfromafar wrote:
       | Not a criticism, not even a nit-pick, but a reflection
       | 
       |  _" (binaries are kind of the definition of platform-specific, so
       | this is all platform-specific)_ (this is true!)
       | 
       | When "Actually Portable Executable" took the (geek) proved that
       | the same binary could run on a bunch of platforms, that was a
       | surreal moment I still haven't mentally recovered from.
       | 
       | Here we spent decades trying to solve the cross-platform problem,
       | in so many fractals of ways (Java, cross-platform libraries, etc
       | etc) and the solution was right under our noses all this time.
        
         | norir wrote:
         | I personally am not convinced that portable binaries are a net
         | positive. I believe in the era of fast computers that source
         | distribution and local compilation is superior to binary
         | distribution. Unfortunately, much of the software we rely on is
         | so large, and compilers so relatively slow, that binary
         | distribution is something of a necessary evil. I'd rather see
         | more effort towards simpler software components (that naturally
         | compile fast) and faster compilers than portable binaries.
        
           | csdvrx wrote:
           | You can have both. APE are generally faster and smaller.
           | 
           | Fat APEs (aarch64 + x86_64) are larger, but interesting in
           | their own way.
        
             | saagarjha wrote:
             | Not in general.
        
         | vitiral wrote:
         | I may have misunderstood, but I'm pretty sure APE is not a
         | "binary format" per se.
         | 
         | It is a script that can be executed on any system. That script
         | can then LOAD a binary. IIRC the original needed to decode it
         | from base64 before it could be loaded.
         | 
         | So... it's an executable binary loader
        
           | actionfromafar wrote:
           | Same same, but different...
           | 
           | How can the syscalls work the same in linux, windows and n my
           | macos?!
           | 
           | It's bananas
        
           | rep_lodsb wrote:
           | It's a script that starts with an EXE header ("MZ"). Having
           | both EXE and ELF headers at the same location is obviously
           | impossible, since they start with different bytes.
           | 
           | A possible, although very limited way to have an actual
           | binary program execute on different platforms would be to
           | create a DOS .COM file (which has no header, just the raw
           | machine code) with a valid ELF header. It would then also
           | work on 32-bit Windows via its built-in DOS emulator, and
           | presumably on 64-bit Windows with WSL2.
           | 
           | The start bytes for a 32-bit ELF header decode as 16-bit x86
           | into:                   7F 45    JG    +45h         4C
           | DEC   SP         46       INC   SI         01 01    ADD
           | [BX+DI],AX         01 xx    ADD   ...
           | 
           | The first instruction is a jump past the end of the ELF
           | header, unfortunately it's conditional. But we have 9
           | reserved bytes to continue this code, which is enough to undo
           | the effects of the DEC and ADD instructions and then jump to
           | the same address. I've written a 138 byte "Hello world" that
           | works on Linux, DOS and also CP/M-80 that way.
           | 
           | It's possible to have the code that executes under Linux be a
           | small (less than 2K bytes) loader program that creates 16-bit
           | code and data segments and installs a handler for SIGSEGV. It
           | can then jump into _the same code_ that would run under
           | 16-bit DOS, trapping every INT 21h and translating the most
           | important syscalls into their Linux equivalents, kind of like
           | a minimal version of Wine.
           | 
           | I have a proof of concept for that, it only handles the
           | "read", "write" and "exit" syscalls, which is enough to write
           | something like rot13 or hexdump. With a lot more work, it
           | could be possible to produce really non-trivial software that
           | runs in such a restricted environment...
        
       | lisper wrote:
       | > Executables aren't magic.
       | 
       | Nothing in a computer is magic. It was all designed by humans,
       | every single one of which was once a clueless noob. No one is
       | born understanding this stuff.
        
         | JoshuaRogers wrote:
         | > This does a bunch of Very Important Things that I don't
         | understand very well, including calling main. So I won't
         | explain them.
         | 
         | Honestly, this line was the best in the whole article. It felt
         | like at that moment I knew the person talking to me wasn't
         | trying to prove that they were some sage (personally guilty
         | here) but instead of was someone who wanted to show me
         | something cool that we could both enjoy.
         | 
         | Wonderful write up.
        
         | infinite8s wrote:
         | "It is no exaggeration to regard this as the most fundamental
         | idea in programming: The evaluator, which determines the
         | meaning of expressions on a piece of paper, is just another
         | piece of paper." --SICP
        
         | latexr wrote:
         | > Nothing in a computer is magic.
         | 
         | I think that's covered by the text, in the sentence right after
         | that one (emphasis mine):
         | 
         | > ELF is a file format _like any other_!
        
         | olsher wrote:
         | The actual /behavior/ of computers, though, tends to emerge
         | from the confluence of complex processes that humans /can't/
         | understand...our AGI leverages this emergence to enable problem
         | solving in domains where complexity exceeds human capabilities.
        
       | fragmede wrote:
       | cat-ing a binary to the terminal is a recipe for sadness. I like
       | | hd, which is hexdump -C, though that's just as impenetrable to
       | the naked eye.
        
       | heinrichhartman wrote:
       | I started my blog in 2012, when I shifted my academic career from
       | Mathematics to Computer Science. This topic was literally the
       | first thing that I studied:
       | 
       | https://heinrichhartmann.com/archive/Dissecting-Hello-World....
       | 
       | Never regretted going down this deep rabbit hole. IIRC, Julia
       | also has a math background. Maybe it's the desire for bottom-up
       | reasoning that leads math folks towards experiments like this.
       | Great to see her making this approachable for a large audience.
        
       | sergejf wrote:
       | The format of executable files fascinated me back in the early
       | 90s, to the point that I spent weeks writing (in Modula 2) a DOS
       | and Windows executable file viewer that I named VEXE, releasing
       | it as shareware in 1991.
       | 
       | It found a niche following among crackers, even deserving a
       | mention in a +ORC tutorial,
       | https://gist.github.com/callowaysutton/48bdf0245e17e72d41a15...,
       | probably because it could detect various encryption and
       | compression methods used to prevent the reverse engineering of
       | those programs.
        
       | randall wrote:
       | Amazingly helpful!
        
       | adolph wrote:
       | For folks interested in this topic who have not seen Cosmopolitan
       | and RedBean, actually pdrtable executable (2020) is a great read
       | too: https://justine.lol/ape.html
       | 
       | https://redbean.dev/
        
       | as1009 wrote:
       | Great thread
        
       | as1009 wrote:
       | great thread, thank you!
        
       | dishsoap wrote:
       | I think ELF should absolutely be mentioned in the title.
        
       | setheron wrote:
       | (Self promotion) Check out my tool which let's you explore ELF
       | using SQL
       | 
       | https://github.com/fzakaria/sqlelf
        
       | alkh wrote:
       | For a person with a heavy Python background, can anyone suggest a
       | resource/book that would be a good applied intro to practical
       | low-level programming? I've recently started learning Rust and
       | realized I need to catch up on many things. I haven't taken any
       | compiler course, so maybe that's the reason I am missing so much
       | information. For example, I had no idea that symbols in a binary
       | were a thing or what the difference between ELF/MACH-O was
        
       | EmilioPeJu wrote:
       | A book about this topic which I enjoyed is "Learning Linux Binary
       | Analysis" by Ryan O'neil.
        
       | EmilioPeJu wrote:
       | If you are curious about how small a ELF binary file can be, you
       | might like the following amusing article:
       | https://www.muppetlabs.com/~breadbox/software/tiny/teensy.ht...
        
       ___________________________________________________________________
       (page generated 2024-02-03 23:01 UTC)