Live data from Hacker News

Extending the Linux Kernel with Built-In Kernel Headers

linuxjournal.com

41–50 of 62 posts

Re: Extending the Linux Kernel with Built-In Kernel Headers

#41

Earlier quoted context omitted.

If you just give me a .jar do I get those interfaces in a consumable form? Said differently, can I download your .jar and write my own code to interface with it while on a desert island without any other resources?

Yes. If you have a random JAR file, an IDE can introspect it to see what classes are in it and what methods are in those classes. The only exception is if you run it through some kind of obfuscator first.

Yes but JAR files are basically equivalent to source code parsed and serialised as bytecode with only the most rudimentary optimisations applied. I don't really see a difference between that and carrying gzipped headers with the kernel. Yes, the .class format is cleaner, but it's still very close to what the programmer wrote. Another plus to carrying the header with the kernel is that you can carry the preprocessed header to make sure the user doesn't set the wrong defines, and you can add compiler checks to check that the user isn't using a too old or too new compiler with incompatible ABI.

Re: Extending the Linux Kernel with Built-In Kernel Headers

#42
post #4

It’s a simple and practical solution. It irks me for some reason but I can’t think of something better.

Doesn't necessarily work for the kernel, but GIR and typelibs provide machine-readable descriptions of C APIs: https://github.com/GNOME/gobject-introspection

Re: Extending the Linux Kernel with Built-In Kernel Headers

#43
post #4

It’s a simple and practical solution. It irks me for some reason but I can’t think of something better.

Doesn't necessarily work for the kernel, but GIR and typelibs provide machine-readable descriptions of C APIs: https://github.com/GNOME/gobject-introspection

From what I know about it, gobject-introspection has some nice properties, but one killer drawback: it's incompatible with cross compilation. This is apparently because it requires running binaries compiled for the target system as part of the build process. You actually can cross compile if you have an emulator for that system handy [1], but that's horrible.

Apparently the reason it requires running the compiled binaries is that GObject types are only registered at runtime, within so-called "_get_type" functions. For more typical systems, everything needed can be determined at compile time. Too bad there's no portable way to ask a C compiler to dump what it knows about a source file, but if you just want things like struct sizes and field offsets, you can compile a C file that embeds them as global variables, and then extract the variable values. For more advanced introspection there are many less-portable options including Clang's API, GCC-XML, parsing debug info, or writing your own compiler (easier for C; it seems that parts of gobject-introspection work like this).

Anyway, another interesting comparison is DTrace's CTF (Compact Type Format) [2], a simple binary format that describes the kernel's C struct layouts, function signatures, etc. This information is simply converted from compiler-generated debug info [3], but it's stripped down enough that it can be embedded into every kernel without too much size overhead. When the DTrace compiler is invoked to compile a user hook, it parses the CTF data and exposes the types and functions to the user's code (which is written in a custom C-like language).

Ironically, BPF has BTF, which is a very similar-looking format that encodes very similar kinds of data – but is used for a completely different purpose. Specifically, it's only used to encode types and functions defined by BPF programs, to allow the kernel to pretty-print things. But in theory BTF could be repurposed to work like CTF: you would need to generate BTF information for the kernel itself, and then Clang could be extended to support "including" BTF files in place of C headers. However, this option was apparently discussed and rejected [4]. I haven't read the original threads to find out why, but I suspect it might involve:

- Lack of existing tooling to do the above;

- Lower expressivity compared to C headers, e.g. the inability to encode macros (although this could be fixed);

- Desire to use the information for building not just BPF hooks but also full-fledged kernel modules.

[1] https://maxice8.github.io/8-cross-the-gir/

[2] https://github.com/oracle/libdtrace-ctf/blob/master/include/...

[3] https://www.freebsd.org/cgi/man.cgi?query=ctfconvert&sektion...

[4] https://lwn.net/Articles/783832/

Re: Extending the Linux Kernel with Built-In Kernel Headers

#44
post #13

Earlier quoted context omitted.

File this one under: kludges to get around openly user-hostile userland.

How so? Seems like an elegant enough solution to ensure you always have the right headers to build modules against the currently running kernel.

The article goes into detail as to why this actually can't be used to build modules against the currently running kernel.

Re: Extending the Linux Kernel with Built-In Kernel Headers

#45
post #8

Why /sys and not /proc ? After all, the kernel binary itself is under /proc .

There is a very long thread on LKML discussing this. But I'm pretty sure the primary reason is that procfs has been slowly accumulating more and more random knobs, while sysfs actually has a structure that can be used reasonably by userspace.

Re: Extending the Linux Kernel with Built-In Kernel Headers

#46

Why can't they build the eBPF bytecode offline using the correct kernel API and ship the bytecode to the Android device?

Lol Joel (the author) and I discussed that this week. Sounds like bpftools will need some modifications to reload a precompiled bpf program. We'd like to avoid having another copy of LLVM on device (renderscript has an old version of LLVM).

Re: Extending the Linux Kernel with Built-In Kernel Headers

#47
post #38

Shipping the kernel headers is complicated, but somehow a whole C compiler is not? Or doesnt BPF compilation need a C compiler?

BPF is compiled in user-space and the bytecode is provided to the kernel -- the compiler definitely isn't shipped as part of the kernel.

Re: Extending the Linux Kernel with Built-In Kernel Headers

#48
post #41

Earlier quoted context omitted.

Yes. If you have a random JAR file, an IDE can introspect it to see what classes are in it and what methods are in those classes. The only exception is if you run it through some kind of obfuscator first.

Yes but JAR files are basically equivalent to source code parsed and serialised as bytecode with only the most rudimentary optimisations applied. I don't really see a difference between that and carrying gzipped headers with the kernel. Yes, the .class format is cleaner, but it's still very close to what the programmer wrote. Another plus to carrying the header with the kernel is that you can carry the preprocessed h…

I suppose the real difference is that with java platform, you get

1. 1/10 or 1/100 compile time

2. full introspection in regards to the interfaces (the topic of this discussion). the implementation classes could be obfuscated, but the point of an interface (and an API in general) that it is not.

3. one could possibly allow multiple versions of the interface be present on the same machine at the same time. for example, when SomeInterfaceV1 is replaced (or extended) by SomeInterfaceV2, it might be possible to provide an adapter that publishes the older interface for backward compatibility.

these are just random thoughts, because the difference between current systems written in c/c++ and a new architecture that uses interface-based approach is too great, I don't think I can name a real system that uses it in practice.

done the API versioning myself several times with complex applications though.

Re: Extending the Linux Kernel with Built-In Kernel Headers

#49
post #14

Earlier quoted context omitted.

eBPF is not Turing-complete, but static analysis can absolutely prove things about individual programs in Turing-complete languages.

You can prove some code in Turing complete languages. You can construct non-turing-complete languages which all code can be proven, and that is the point the parent is making.

Forbidding loops in a language does make all code written in that language provably terminating. But there’s not much else it lets you prove for all code, at least not if you want to get results before the heat death of the universe. For example, you can’t determine what inputs to a program will yield a given output: even classic BPF is probably expressive enough to implement a cryptographic hash, and eBPF definitely is. You can’t enumerate all possible paths through the program: the number of paths is exponential in the size of the program.

On the other hand, forbidding loops does make some properties easier to prove for restricted classes of programs. For instance, Linux’s BPF verifier tracks, for each instruction, the minimum and maximum value of each register at that point in the program. It uses that to determine whether array accesses in the program are bounds checked, and complain if not: that way, it doesn’t have to insert its own bounds check, which might be redundant. Doing the same in the presence of loops would require a more expensive algorithm, so forbidding them is a benefit. Yet... Linux’s verifier is sound: it will forbid all programs that could possibly index out of bounds, at least barring bugs. But it is not fully precise: it does not pass all programs that have the property of never indexing out of bounds for any input. For example, you could have a program that takes a 256-bit input and indexes out of bounds only if its SHA-256 hash is a specific value. That program is safe iff there happen to be no 256-bit strings that hash to that value, something that you could theoretically verify – but only by going through all 2^256 possible strings and hashing each of them. Linux does not.

Nor would it be reasonable to, why does that hypothetical matter? Because the ability to prove arbitrary properties about all programs, at the cost of arbitrarily long analysis time, is sort of the main mathematical benefit to non-Turing-completeness. But from a practical standpoint that’s useless. And if you don’t need that – if you only care about the ability to prove things about restricted classes of programs – well, you can achieve that even with loops. After all, that’s what a type system does, and there are plenty of Turing-complete languages with type systems. As I said, disallowing loops makes the job easier in some cases. But that’s more of a matter of degree, not the kind of hard theoretical barrier that “non-Turing-complete = analyzable” makes it sound like. That makes it a less convincing rationale for disallowing them.

Re: Extending the Linux Kernel with Built-In Kernel Headers

#50
post #40
post #37

Earlier quoted context omitted.

squashfs has horrible performance. All requests to the block layer are 512 Bytes. Other filesystems like ext4 make much bigger requests and perform much better in the end despite the compression of squashfs leading to lower overall data volume. Disclaimer: Measured 2 years ago on ARM32, emmc, with a 4.1(?) kernel.

A big part of the problem might be xz decompression, that's been my discovery anyway. https://bugzilla.redhat.com/show_bug.cgi?id=1717728

I tried various compression algorithms. I don't think there was a CPU bottleneck, at least not with the less aggressive compressions. blktrace showed the difference compared to ext4, squashfs does all reads one by one block.

It was in a previous job. I don't have access to the details anymore. And the kernel was not the newest. But squashfs looked unmaintained already then and that's what they're saying elsewhere in the discussion. So I fear nothing has changed.

Post reply on HN