$ORIGIN was only supported when the loader was looking for other dependencies. The loader itself (field PT_INTERP) is loaded by the kernel. So prior to this change, every program must hardcode the absolute path to ld.so. With support for an $ORIGIN-relative loader, each program could use its own copy of ld.so.
HN user
dgrunwald
Furthermore parsing JSON or YAML gives you the basic data types like lists and dictionaries. Parsing XML gives you an AST that requires a lot more effort to turn into data in your domain.
More precisely: in XML, elements (nodes) are named/labeled. ("node-labeled graph") In JSON, keys (edges) are named. ("edge-labeled graph")
In programming, we need names for the fields in our structures (edges between objects), so JSON is a much better match than XML (which needs contortions to handle this use case -- e.g. by having nesting levels alternate between element=node and element=edge). Only in some object-oriented cases (which derived class should the deserializer construct?) do you care about node labels -- but usually that's in addition to edge labels, so a "_type" key in JSON is still easier than XML.
`rmdir /s /q` in a command prompt is significantly faster than Windows Explorer.
Yes C: is slow due to filters and Dev Drive is faster; but this difference can only be felt when using the command line; Windows Explorer has so much additional overhead that the overhead from file filters is insignificant in comparison.
In our compiler (in a code analysis tool), we have
#pragma immutable_macro __attribute__
After this pragma, any attempt to #define/#undef the macro "__attribute__" will be silently ignored. This lets us (or our customers) bypass such stupidity in library headers. It's also often useful to replace broken macros with working versions.It's a normal command called "View: Reopen Closed Editor".
Yes `int` acts as if it was a subtype of `float`: https://typing.python.org/en/latest/spec/special-types.html#...
But in Python the type checker does not complain about `x: float = 0`, because for the purpose of type checking (but not at runtime), `int` is considered a subtype of `float`: https://typing.python.org/en/latest/spec/special-types.html#...
In most languages, `x: float = 0` involves an implicit conversion from int to float. In Python, type annotations have no impact on runtime behavior, so even though the type checker accepts this code, `type(x)` will be `int` -- python acts as if `int` was a subtype of `float`.
It would be weird if the behavior of `1 / x` was different depending on whether `0` or `0.0` was passed to a `x: float` parameter -- if `int` is a subtype of `float`, then any operation allowed on `float` (e.g. division) should have the same behavior on both types.
This means Python had to choose at least one:
1. division violates the liskov substitution principle
2. division by zero involving only integer inputs returns NaN
3. division by zero involving only float inputs throws exception
4. It's a type error to pass an int where a float is expected.
They went with option 3, and I think I agree that this is the least harmful/surprising choice. Proper statically typed languages don't have to make this unfortunate tradeoff.
Don't forget the compatibility issues: Fil-C isn't really usable if you mix C with other languages in the process.
It's especially problematic to have multiple different garbage collectors; so given the desire to reuse libraries across language boundaries, there's still a strong demand for Yolo-C, C++ or Rust.
When accessing individual elements, 0-based and 1-based indexing are basically equally usable (up to personal preference). But this changes for other operations! For example, consider how to specify the index of where to insert in a string. With 0-based indexing, appending is str.insert(str.length(), ...). With 1-based indexing, appending is str.insert(str.length() + 1, ...). Similarly, when it comes to substr()-like operations, 0-based indexing with ranges specified by inclusive start and exclusive end works very nicely, without needing any +1/-1 adjustments. Languages with 1-based indexing tend to use inclusive-end for substr()-like operations instead, but that means empty substrings now are odd special cases. When writing something like a text editor where such operations happen frequently, it's the 1-based indexing that ends up with many more +1/-1 in the codebase than an editor written with 0-based indexing.
make sure not to sign into your Microsoft account or link it to Windows again
That's not so easy. Microsoft tries really hard to get you to use a Microsoft account. For example, logging into MS Teams will automatically link your local account with the Microsoft account, thus starting the automatic upload of all kinds of stuff unrelated to MS Teams.
In the past I also had Edge importing Firefox data (including stored passwords) without me agreeing to do so, and then uploading those into the Cloud.
Nowadays you just need to assume that all data on Windows computers is available to Microsoft; even if you temporarily find a way to keep your data out of their hands, an update will certainly change that.
That loop isn't N²: if there are long sequences of dashes, every iteration will cut the lengths of those sequences in half. So the loop has at most lg(N) iterations, for a O(N*lg(N)) total runtime.
cygwin is a POSIX-emulating library intended for porting POSIX-only programs to Windows. That is: when compiling for cygwin, you'd use the cygwin POSIX APIs instead of the Windows APIs. So anything compiled with cygwin won't be a normal Windows program.
There's no reason to use cygwin with Rust, since Rust has native Windows support. The only reason to use x86_64-pc-cygwin is if you would need your program to use a C library that is not available for Windows, but is available for cygwin.
If you don't want to/can't use the MSVC linker, the usual alternative is Rust's `x86_64-pc-windows-gnu` toolchain.
If you need `0..=n`, you can't write `0..(n+1)` because that addition might overflow.
But the source character set remains implementation-defined, so compilers do not have to directly support unicode names, only the escape notation.
Definitely a questionable choice to throw off readers with unicode weirdness in the very first code example.
The article is using the Python C API directly.
pybind11 is a C++ wrapper that makes the Python API more friendly to use from C++ (e.g. smart pointers instead of manual reference counting)
But if i is indeed put immediately after, then the function should indeed return 5 for n=3.
That's not how compilers work. The optimization changing `return i;` into `return 0;` happens long before the compiler determines the stack layout.
In this case, because `return i;` was the only use of `i`, the optimization allows deleting the variable `i` altogether, so it doesn't end up anywhere on the stack. This creates a situation where the optimization only looks valid in the simple "flat memory model" because it was performed; if the variable `i` hadn't been optimized out, it would have been placed directly after `arr` (at least in this case: https://godbolt.org/z/df4dhzT5a), so the optimization would have been invalid.
There's no infrastructure in any compiler that I know of that would track "an optimization assumed arr[3] does not alias i, so a later stage must take care not to place i at that specific point on the stack". Indeed, if array index was a runtime value, the compiler would be prevented from ever spilling to the stack any variable that was involved in any optimizations.
So I think your general idea "the allowable behaviors of an out-of-bounds write is specified by the possible actual behaviors in a simple flat memory model for various different stack layouts" could work as a mathematical model as an alternative to UB-based specifications, but it would end up not being workable for actual optimizing compiler implementations -- unless the compiler could guarantee that a variable can always stay in a register and will never be spilled (how would the compiler do that for functions calls?), it'd have to essentially treat all variables as potentially-modified by basically any store-via-pointer, which would essentially disable all optimizations.
The problem with `setenv` is that people expect one process to have one set of environment variables, which is shared across multiple languages running in that process. This implies every language must let its environment variables be managed by a central language-independent library -- and on POSIX systems, that's libc. So if libc refuses to provide thread-safety, that impacts not just C, but all possible languages (except for those that cannot call into C-libraries; as those don't need to bother synchronizing the environment with libc).
How exactly would that help in this situation?
If both Rust and C have independent standard libraries loaded into the same process, each would have an independent set of environment variables. So setting a variable from Rust wouldn't make it visible to the C code, which would break the article's usecase of configuring OpenSSL.
The only real solution is to have the operating system provide a thread-safe way of managing environment variables. Windows does so; but in Linux that's the job of libc, which refuses to provide thread-safety.
Yes, that is typically the way to go.
Collecting a call stack only requires unwinding information (which is usually already present for C++ exceptions / Rust panics), not full debug symbols. This gives you a list of instruction pointers. (on Linux, the glibc `backtrace` function can help with this)
Print those instruction pointers in a relative form (e.g. "my_binary+0x1234") so that the output is independent of ASLR.
The above is all that needs to happen on the production/customer machines, so you don't need to ship debug symbols -- you can ship `strip`ped binaries.
On your own infrastructure, keep the original un-stripped binaries around. We use a script involving elfutil's eu-addr2line with those original binaries to turn the module+relative_address stack trace into a readable symbolized stack trace. I wasn't aware of llvm-symbolizer yet, seems like that can do the same job as eu-addr2line. (There's also binutil's addr2line but in my experience that didn't work as well as eu-addr2line)
Caution with these functions: in most cases you need to check not only the error code, but also the `ptr` in the result. Otherwise you end up with `to_int("0x1234") == 0` instead of the expected `std::nullopt`, because these functions return success if they matched a number at the beginning of the string.
All noexcept does is catch any exception and immediately std::terminate.
While that's a possible implementation; the standard is a bit more relaxed: `noexcept` may also call `std::terminate` immediately when an exception is thrown, without calling destructors in the usual way a catch block would do.
https://godbolt.org/z/YTe84M5vq test1 has a ~S() destructor call if maybe_throw() throws; test2 never calls ~S().
MSVC does not appear to support this optimization, so using `noexcept` with MSVC involves overhead similar to the catch-block.
The GC does matter.
Most applications are completely fine with having a garbage collector, but they are not fine with having multiple garbage collectors! Mixing multiple garbage collected languages is a recipe for complex bugs. At a minimum, reference cycles across the languages will result in memory leaks.
Thus every garbage collected language tends to grow its own ecosystem of libraries, with the whole world having to be re-implemented in that language. The only exception are the C/C++/Rust libraries, as these can be used (through C FFI interface) by almost every other language. This is precisely because these languages do not introduce their own heavy-weight runtime (especially: no GC), allowing them to be combined with another language's runtime.
Thus widely used libraries must be written in one of the non-GC languages, even if 100% of the applications using them are fine with having a garage collector.
There are some static analysis tools that can check this.
Cert's SIG30 rule page has a list: https://wiki.sei.cmu.edu/confluence/display/c/SIG30-C.+Call+...
Also there's https://clang.llvm.org/extra/clang-tidy/checks/bugprone/sign...
If a compiler is certain there's UB should it format the developer's hard drive immediately, or does it have to wait until the program is run before formatting to be standards compliant?
The standard has enough variants of UB that both options are possible!
For normal runtime UB (e.g. division by zero), it needs to wait until it is certain that the undefined code will actually be executed. So compilation/linking must not fail, the behavior is only undefined if the program is actually run.
IFNDR (ill-formed, no diagnostic required) is a different kind of UB; here it is expected that compilation/linking may already fail.
Then there's also the infamous "preprocessor UB" (e.g. https://wg21.link/p2621), which reuses the "undefined behavior" terminology despite not having anything to do with runtime semantics. I guess this is where the compiler might format your hard drive (old gcc versions actually launched nethack on undefined pragmas!).
You don't need a distro with ancient glibc; you can instead create a cross-compilation toolchain.
We used https://crosstool-ng.github.io/docs/ to create a cross-compilation toolchain that runs on modern debian, but produces executables that only need glibc 2.17. The only downside is that you no longer can simply apt-get a library you need -- all dependencies shipped along with your application also need to be built with this cross-compiler.
You're confused there. The xz backdoor made use of a Debian OpenSSH patch, but it wasn't "caused" by it. Without the patch, the malicious xz maintainer could have written a different backdoor without making use of the OpenSSH patch -- for example, since debian packages are compressed with xz, the backdoor could have modified the sshd binary while unpacking the next OpenSSH security update. That would have been slower (attacker might have needed to wait a long time for a security update), and more discoverable since the modified file would be persisted to disk; but it also wouldn't have caused the performance issues that ended up in the discovery of the backdoor.
Your "third party libs" includes system libraries like libdl.
We had a Python process using both threads (for stuff like background downloads, where the GIL doesn't hurt) and multiprocessing (for CPU-intensive work), and found that on Linux, the child process sometimes deadlocks in libdl (which Python uses to import extension modules).
The fix was to use `multiprocessing.set_start_method('spawn')` so that Python doesn't use fork().
UNC paths like //server/share/ are perfectly fine.
What's being removed in C23 is the ancient K&R syntax for function definitions:
int max(a, b)
int a, b;
{
return a>b?a:b;
}
My understanding is that C23 does not change the meaning of function declarations. So "void foo();" remains a declaration of a function accepting an unknown number of parameters.