Live data from Hacker News

The Journey Before main()

amit.prasad.me

131–140 of 145 posts

Re: The Journey Before main()

#131

Earlier quoted context omitted.

Glibc is half of GNU/Linux. You can of course use another libc, but it will be a different OS.

Yeah, even library loading relies on glibc, so we can't really escape glibc on GNU/Linux.

I wonder if anyone implemented loading shared libraries without glibc? It shouldn't be that hard, just need to implement ELF parser and glibc-compatible relocation mechanism.

Re: The Journey Before main()

#132

Earlier quoted context omitted.

Can you write that so, that people who are dumb and don't know the Windows way also get it?

> Treat static/dynamic linking the same Imagine you have an executable with a random library that has a global variable. Now you have a shared/dynamic library that just so happens to use that library deep in its bowels. It's not in the public API, it's an implementation detail. Is the global variable shared across the exe and shared lib or not? On Linux it's shared, on Windows its not. I think the Windows way is bett…

> On Linux it's shared, on Windows its not.

Yes, the default compiler invocation makes all symbols exported. But leaving it like that is super lazy, it will likely break things (like you wrote). You can change the default with -fvisibility=[default|internal|hidden|protected] and it's kind of expected that you do. Oh, and I just found out that GCC has -fvisibility-ms-compat, to make it work like the MS compiler.

> Instead you link against a thin, small import lib called (ideally) foo.imp.lib.

Interesting. How is that file created? Is it created automatically, when you build foo.dll? How is it shipped? Is it generally distributed with foo.dll, because then I don't really see the benefit of linking against foo2.15.imp.lib compared to foo2.15.dll.

> It should be trivial to link against glibc2.15 even if your system is on glibc2.40.

It don't know if you know that, but on Linux glibc2.40 is not really only version 2.40. It includes all the versions up to 2.40. When you link against a symbol that was last changed in 2.15, you link against glibc2.15, not against glibc2.40. If you only use symbols from glibc2.15, then you have effectively linked the complete program against glibc2.15.

But yes, enforcing this should be trivial. I think this a common complaint.

> The Linux Way is to install shared libraries into the global path.

Only in so far, as on Windows you put the libraries into 'C:\Program Files\PROGRAM\' and on Linux into '/usr/lib/PROGRAM/'. You of course shouldn't dump all your libraries into '/usr/lib'. That's different when you install a library by itself. I don't know how common that is on Windows?

I don't really know what problems you have in mind, but it seems like you think a program would have a dependency on 'libfoo.so', so at runtime it could randomly break by getting linked against another libfoo, that happens to be in the library path. But that is not the case, you link against '/usr/lib/foo.so.6'. Relying on runtime environment paths for linking is as bad as calling execve("bash foo") and this is a security bug. Paths are for the user, so that he doesn't need to specify the full path, not for programs to use for dependency management. Also when you don't want updates to minor versions, then you can link to '/usr/lib/foo.so.6.2'. And when you don't want bugfixes, you can link against '/usr/lib/foo.so.6.2.15', but that would be super dumb in my opinion. On Linux ABIs have there own versions differently from the library versions, I agree that this can be confusing for newcomers.

A fundamentally difference is also that there is a single entity controlling installation on Linux. It is the responsibility of the OS to install programs, bypassing that just creates a huge mess. I think that is the better way and both Apple and Microsoft are moving to that way, but likely for other reasons (corporate control). This doesn't mean, that the user can't install his own programs which aren't included in the OS repository. OS repository != OS package manager. I think when you can bother to create foo-installer.exe, you should also create foo.deb . Extracting foo.zip into C:\ is also a dumb idea, yet some people think it suddenly isn't dumb anymore when doing it on Linux.

PIP and similar projects are a bad idea, in my opinion. When someone wants to create their own package system breaking the OS, they should have at least the decency to roll it in /opt. Actually that is not a problem in Python proper. They have essentially solved that for decades and all that dance with venv, uv and what else is completely unnecessary. You can install different Python installation into the OS path. Python installs into /usr/bin/python3.x and creates /usr/lib/python3.x/ by default. Each python version will only use the appropriate libraries. That's my unpopular opinion. That mess is why Docker was created, but in my opinion that does not come from following the Linux way, but by actively sabotaging it.

> Also, super duper unpopular opinion, but bash sucks and any script longer than 10 lines should be written in a real language with a debugger.

Bash's purpose is to cobble programs together and setup pipes and process hierarchies and job control. It excels at this task. Using it for anything else sucks, but I don't think that is widely disputed.

Re: The Journey Before main()

#133

Earlier quoted context omitted.

Linux does none of those things. That's user space stuff. Linux loads your ELF and jumps to its entry point. That's it. Linux is so great you're actually free to remake the entire user space in your image if you want. It's the only kernel that lets you do it, all the others force you to go through C library nonsense, including Windows. The glibc madness you described is just a convention, kept in place by inertia. Yo…

Yes that’s all filed under blah blah blah. Some people use “Linux” to exclusively refer to the Linux kernel. Most people do not.

Linux by default does mean Linux kernel, but in my reply I didn't cared about that either. When all know what is meant, that is fine in my opinion.

I think it is important to have GNU/Linux in mind, because there are OSs that don't use glibc and work totally different, so none of your complaints apply. But yes, most people think of GNU/Linux, when you tell them about Linux.

It is also relevant to consider that there is no OS called GNU/Linux. The OSs are called Debian, Arch, OpenSuSE, Fedora, ... . It is fine for different OS to have differently working runtime linkers and installation methods, but some people act surprised when they find out ignoring that doesn't work.

Re: The Journey Before main()

#134

Earlier quoted context omitted.

I found MinGW to be quite nice, but ymmv. > to try and do things TheLinuxWay It's not really about TheLinuxWay. It's more that Microsoft completely lacks POSIX tools at all and the compiler needs to have a complete IDE installed, which I would need a license for, and the compiler invocation also doesn't really correspond to any other compiler.

> Microsoft completely lacks POSIX tools True! > compiler needs to have a complete IDE installed Not true. You can download just MSVC the toolchain sans IDE. Works great. https://stackoverflow.com/questions/76792904/how-to-install-... > compiler invocation also doesn't really correspond to any other compiler True. But you don’t have to use MSVC. You can just use Clang for everything. Clang on Windows does typically u…

But then I don't understand your complaints against MSYS2/MinGW. MSYS2 UCRT (the default environment) is a collection of POSIX tools and GCC to compile against the Microsoft C++ standard library. The only difference to what you tell me is completely fine is, that it uses GCC instead of Clang. Other MSYS2 environments are Clang instead of GCC.

MinGW is the open-source implementation of the Windows API, so that you can use the Microsoft C++ standard library, without needing to use the MS toolchain.

Re: The Journey Before main()

#135

Earlier quoted context omitted.

Glibc is half of GNU/Linux. You can of course use another libc, but it will be a different OS.

Yeah, even library loading relies on glibc, so we can't really escape glibc on GNU/Linux.

I don't really know why people expect to be able to bypass the OS and not have problems. It seems to come from people who think a "Linux OS" only consists of the Linux kernel.

Re: The Journey Before main()

#136

Earlier quoted context omitted.

Yeah, even library loading relies on glibc, so we can't really escape glibc on GNU/Linux.

I wonder if anyone implemented loading shared libraries without glibc? It shouldn't be that hard, just need to implement ELF parser and glibc-compatible relocation mechanism.

I don't think nobody has done, that. It is just that vendoring your own OS comes with a lot of work.

Re: The Journey Before main()

#137

Earlier quoted context omitted.

I found MinGW to be quite nice, but ymmv. > to try and do things TheLinuxWay It's not really about TheLinuxWay. It's more that Microsoft completely lacks POSIX tools at all and the compiler needs to have a complete IDE installed, which I would need a license for, and the compiler invocation also doesn't really correspond to any other compiler.

> Microsoft completely lacks POSIX tools True! > compiler needs to have a complete IDE installed Not true. You can download just MSVC the toolchain sans IDE. Works great. https://stackoverflow.com/questions/76792904/how-to-install-... > compiler invocation also doesn't really correspond to any other compiler True. But you don’t have to use MSVC. You can just use Clang for everything. Clang on Windows does typically u…

> Not true. You can download just MSVC the toolchain sans IDE. Works great.

How is the standalone MS build system called?

Re: The Journey Before main()

#138

Earlier quoted context omitted.

I am not sure you understand my point. Inlining libc definitions for syscalls is fine when you only care about Debian 12 commit hash ####. It will break as soon as you think your machine is running Debian 12 and you updated it, so surely it includes the latest userspace-patches. It will also break when a user uses the OS configuration to change the behaviour of some OS functionality, but your code is oblivious to tha…

> It will break If it ever breaks, it's a bug in the Linux kernel. > It will also break when a user uses the OS configuration to change the behaviour of some OS functionality Can you give concrete examples of this? > There is syscall(2) in the libc, if you want to do this. I know. I've written my own syscall(), as well. The idea is to put it in the compiler as a builtin so there's no need to even write it.

> If it ever breaks, it's a bug in the Linux kernel.

No, your program will still instruct the kernel to do the same. It will just cause conflicts with the other OS internals.

> Can you give concrete examples of this?

Adding another encoding as a gconv module. The DNS issues everyone is talking about.

I don't know what that gets you compared to using syscall(2) and -static. When you want your program to depend on the kernel API instead of the OS API, then you should really link libc statically.

Re: The Journey Before main()

#139
post #113

Earlier quoted context omitted.

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.

Kernel32.dll is loaded into all Windows processes by default, so you actually can have a valid, working Windows binary with 0 entries in the import table. See here[1] for a "Hello world" program written as such. [1]: https://gist.github.com/rfl890/195307136c7216cf243f7594832f4...

That's interesting. How does it work?

  PEB *peb = (PEB *)__readgsqword(0x60);
    
  LIST_ENTRY *current_entry = peb->Ldr->InMemoryOrderModuleList.Flink->Flink;
It just obtains a pointer to the loader's data structures out of nowhere?

Is this actually supported by Microsoft or are people going to end up in a Raymond Chen article if they use this?

Re: The Journey Before main()

#140

Earlier quoted context omitted.

> It will break If it ever breaks, it's a bug in the Linux kernel. > It will also break when a user uses the OS configuration to change the behaviour of some OS functionality Can you give concrete examples of this? > There is syscall(2) in the libc, if you want to do this. I know. I've written my own syscall(), as well. The idea is to put it in the compiler as a builtin so there's no need to even write it.

> If it ever breaks, it's a bug in the Linux kernel. No, your program will still instruct the kernel to do the same. It will just cause conflicts with the other OS internals. > Can you give concrete examples of this? Adding another encoding as a gconv module. The DNS issues everyone is talking about. I don't know what that gets you compared to using syscall(2) and -static. When you want your program to depend on the…

> It will just cause conflicts with the other OS internals.

But not with the kernel.

"Other OS internals" are just replaceable components. The idea is to depend on Linux only, not on Linux+glibc.

> Adding another encoding as a gconv module. The DNS issues everyone is talking about.

Those are glibc problems, not Linux problems. Linux does not perform name resolution or character encoding conversion.

Post reply on HN