Live data from Hacker News

The Journey Before main()

amit.prasad.me

81–90 of 145 posts

Re: The Journey Before main()

#81
post #46
post #22

> The ELF file contains a dynamic section which tells the kernel which shared libraries to load, and another section which tells the kernel to dynamically “relocate” pointers to those functions, so everything checks out. This is not how dynamic linking works on GNU/Linux. The kernel processes the program headers for the main program (mapping the PT_LOAD segments, without relocating them) and notices the PT_INTERP pro…

I've always wondered why there weren't more popular loaders to choose from given that on Linux loaders are user-space

With containers, you usually get incompatible dynamic loaders in the containers (see mananaysiempre' comment; the glibc dynamic linker sees rather active development in some LTS distributions). This wouldn't be possible if the loader were part of the kernel.

Non-ELF loaders are fairly common, too. It's how Wine works, and how Microsoft reuses PE/COFF SQL Server binaries on Linux.

Re: The Journey Before main()

#82

Earlier quoted context omitted.

Yeah, but does that really help? The phrases "growing down/up" still exist and now you defined them to mean the opposite. This issue still didn't go away, since heap and stack still grow in different directions. Can't you just start drawing from the bottom of the blackboard, and it will be obvious? Coordinate systems also typically work that way.

Starting at the bottom of the blackboard would be backwards from how it prints in the terminal when you cat /proc/ /maps.

The way it's printed in the terminal is honestly backwards to me. This seams to only come from the scroll direction of the terminal, and because this is not a drawing, but a simple list. Every other tool like a debugger show it in the opposite direction and in all illustrations I have read it's that way too.

Re: The Journey Before main()

#83

Earlier quoted context omitted.

> malloc, realloc, free Wrapper around sbrk, mmap, etc. whatever the modern variant is. > fopen, FILE Wrapper around open, write, read, close. > stdatomic implementations You can argue, these are wrappers around thread syscalls. > math functions like cos, sin tan, some string functions are all defined in C library True for these, but they are so small, they could just be inlined directly, on their own they wouldn't n…

> malloc, realloc, free > Wrapper around sbrk, mmap, etc. whatever the modern variant is. I don't think that's correct. While `malloc` uses `brk` syscall to allocate large memory areas, it uses non-trivial algorithms and data-structures to further divide that areas into smaller chunks which actually returned. Using syscall for every `malloc`/`free` is quite an overhead. > fopen, FILE > Wrapper around open, write, rea…

Yeah sure they do more things than only doing the syscall, that's the point of an abstraction. But they still provide the functionality of the syscalls, just in the abstraction that you want it to be exposed as in the programming language. That's what I would consider a wrapper.

> C is used a lot in embedded projects.

Sure that's a freestanding implementation, which primary distinction is that it doesn't rely on the libc. The notion of the libc being part of the OS in the wider sense, still holds water here, since here no OS corresponds to no libc.

Re: The Journey Before main()

#84

Earlier quoted context omitted.

> malloc, realloc, free > Wrapper around sbrk, mmap, etc. whatever the modern variant is. I don't think that's correct. While `malloc` uses `brk` syscall to allocate large memory areas, it uses non-trivial algorithms and data-structures to further divide that areas into smaller chunks which actually returned. Using syscall for every `malloc`/`free` is quite an overhead. > fopen, FILE > Wrapper around open, write, rea…

Yeah sure they do more things than only doing the syscall, that's the point of an abstraction. But they still provide the functionality of the syscalls, just in the abstraction that you want it to be exposed as in the programming language. That's what I would consider a wrapper. > C is used a lot in embedded projects. Sure that's a freestanding implementation, which primary distinction is that it doesn't rely on the…

mmap and sbrk would be very poor implementations of malloc.

Re: The Journey Before main()

#85
post #37

Earlier quoted context omitted.

Not exactly the same, but on Windows if you use entirely Win32 calls you can avoid linking any C runtime library. Win32 is below the C standard library on Windows and the C runtime is optional.

This is one of the cornerstones that guarantee Windows can easily upgrade the C runtime and make performance and security upgrades. Win32 APIs have a different function calling ABI too. So only part of that gets "bloated" is Win32 API itself (which is spread across multiple DLLs and don't actually bloat RAM usage). Most of the time even those functions and structures are carefully designed to have some future-proofne…

> If "the year of Linux desktop" would ever happen, they need to either do an Android and change the definition of what a software package is, or split Glibc into 3 parts: syscalls, dynamic loader and the actual C library.

The dynamic loader used to be its own library, FWIW. It got merged into the main one recently.

Re: The Journey Before main()

#86

Earlier quoted context omitted.

Starting at the bottom of the blackboard would be backwards from how it prints in the terminal when you cat /proc/ /maps.

The way it's printed in the terminal is honestly backwards to me. This seams to only come from the scroll direction of the terminal, and because this is not a drawing, but a simple list. Every other tool like a debugger show it in the opposite direction and in all illustrations I have read it's that way too.

So you use debuggers. Good. Then you can confirm that the program counter is incremented after each instruction, and that you read assembly from top to bottom. That means smaller addresses are at the top and larger addresses are at the bottom. This matches my layout, and it also matches what you see in the terminal in /proc//maps.

Re: The Journey Before main()

#87

Earlier quoted context omitted.

Yeah sure they do more things than only doing the syscall, that's the point of an abstraction. But they still provide the functionality of the syscalls, just in the abstraction that you want it to be exposed as in the programming language. That's what I would consider a wrapper. > C is used a lot in embedded projects. Sure that's a freestanding implementation, which primary distinction is that it doesn't rely on the…

mmap and sbrk would be very poor implementations of malloc.

We are talking of wrappers on top of mmap and sbrk. Of course you wouldn't use mmap and sbrk instead of the abstraction. It's really the same as the difference between fread and read.

Re: The Journey Before main()

#88
post #5

It's also possible to pack a whole codebase into "before main()" - or with no main() at all. I was recently experimenting doing this, as well as a whole codebase that only uses main() and calls itself over and over. Good fun: https://joshua.hu/packing-codebase-into-single-function-disr...

Just wondering, how did you get that domain name? I’ve been looking for registrars offering .hu

Re: The Journey Before main()

#89

Earlier quoted context omitted.

The way it's printed in the terminal is honestly backwards to me. This seams to only come from the scroll direction of the terminal, and because this is not a drawing, but a simple list. Every other tool like a debugger show it in the opposite direction and in all illustrations I have read it's that way too.

So you use debuggers. Good. Then you can confirm that the program counter is incremented after each instruction, and that you read assembly from top to bottom. That means smaller addresses are at the top and larger addresses are at the bottom. This matches my layout, and it also matches what you see in the terminal in /proc/ /maps.

Incrementation direction is orthogonal to up/down. When I disassemble a program, the addresses are all over the place and not ordered.

> That means smaller addresses are at the top and larger addresses are at the bottom.

No, my mental model is the exact opposite and this matches the jargon out there.

> also matches what you see in the terminal in /proc//maps

I think of this as a sorted list, not as a display or description of a model.

When I drive on a road, I think of think of things on the road near me to have lower addresses in my coordinate system and things further away as having higher addresses. When I write a list of things I see, this will be from left-to-right then from top-to-bottom on a sheet of paper, because it is a list that follows the writing directions of my language/script. When I look at a traffic sign things nearer to me will be at the bottom and things far away at the top, because that's the agreed-upon mental model of a road. When I look at my navi, things near me will be in the center and things far away from me at the edge of the display.

When I write down points in the first sector of the coordinate system, I might order things according to the x-coordinate ascending top-to-bottom. That doesn't mean I would draw the axis inverted.

The correspondence of physical addresses to position is entirely non-linear and also three dimensional so there is no natural top and bottom especially when we are talking about virtual addresses.

When I get taught a new concept I want to get to know the model everyone uses. I will not like a teacher, that tells me a different ordering which is different from how everyone else does it, because this the output of some random command on some random OS, which actually shows a list, not a graph of a memory model. (Sorry that's harsh, of course I still appreciate didactic simplification.)

Maybe the issue is that you consider the stack to be so important to determine the model of the whole process space. When I would draw a stack on it's own, I DO draw it from top-to-bottom. But when I draw a whole process space, I do not, because everything else is mapped/allocated from bottom-to-top. When you invert the direction of the mental model, yes the stack now grows from bottom-to-top. But no, the other things are now allocated from top-to-bottom instead. This are more things: the text, libraries, mmap'd files, and most-used thing: the heap are all allocated inverted now. And the most important thing, the index for all that: the addresses now are allocated from top-to-bottom.

Re: The Journey Before main()

#90
post #23

Earlier quoted context omitted.

You can make CRT-free Win32 programs, read this guide[1] and you're all set. I've written a couple CLI utilities which are completely CRT-free and weigh just under a few kilobytes. [1]: https://nullprogram.com/blog/2023/02/15/

Almost freestanding. It still requires you to link against kernel32 and use the functions it provides. This is because issuing system calls directly to the Windows kernel is not supported. The kernel developers reserve the right to change things like system call numbers, so they can't be hardcoded into the application.

> Almost freestanding. It still requires you to link against kernel32

Nitpick: the phrase “link against kernel32” feels like a Linux-ism. If you’re only calling a few function you need to load kernel32.dll and call some functions in it. But that’s a slightly different operation than linking against it. At least how I’ve always used the term link.

You’re not wrong in principle. But Linux and Windows do a lot of things differently wrt linking and loading libs. (I think Windows does it waaay better but ymmv)

Post reply on HN