The commentary section starts with "The retail PlayStation BIOS code is a constellation of bugs and bad design." and it gets worse after that. There are hidden gems inside the source code comments of that simili-reconstruction.
2,060 karma · joined May 21, 2020
https://boricj.net/
The commentary section starts with "The retail PlayStation BIOS code is a constellation of bugs and bad design." and it gets worse after that. There are hidden gems inside the source code comments of that simili-reconstruction.
GIAT was privatized, then renamed to Nexter, then merged with German KMW to form European KNDS and now it's about to do an IPO.
All of the French aerospace industry (including missiles) bar Dassault and Thales is inside Airbus.
Arquus was bought by Belgian John Cockerill.
I could probably cite more if I dug deeper, but while we still have French defense companies like Dassault, Naval Group and Thales, a fair amount of our defense industry is no longer exclusively French owned.
And if the French government or owner starts getting uppity, you could always take a page from the Swedes and how they Kockums the shit out of ThyssenKrupp.
For the non-government/private business however, it is indeed a matter of privacy. France rolled out a while ago the requirement to establish the user's age when accessing porn sites. I refuse to do that.
I do kinda get the China customs system example though, only because if corruption is bad enough that it's a greater concern than opsec, then you're kinda hosed anyways.
As for gameplay, that console has a massive library, with thousands of commercially released games (and a lot of hidden gems). I'd be surprised if any gamer wouldn't find at least one to their taste in that catalog.
The PS1 also happens to have RAM aliasing, because there's not enough RAM to cover the entire decoding window for the RAM. I don't know the details, but I've seen PS1 executables setting their stack pointer to the end of the devkit's 8 MiB of RAM and yet they work on retail units, because it ends up at the end of the retail's 2 MiB of RAM. So theoretically, you could stuff bits in there too (and without messing with different memory regions with different cache behaviors).
There are also practical concerns. An invite link that needs to be deleted (for example due to spam abuse) means that it will no longer work. Updating all the places where that old link appeared to the new one can be impractical or impossible.
Second, because while the biggest decompilation Discord servers have effective moderation team and processes, the vast majority are just a server for a project from one or a couple of persons with <100 members joined. Such small servers don't have round-the-clock moderation or customized settings.
Third, because I regularly see phishing crypto spam posted in the smaller servers. Spreading invite links publicly carelessly increases the odds that these scammers find them and spam them.
The decomp.me Discord server invite link can be readily found on that website and in the README of its GitHub repository. It's the closest thing to a central hub of the decompilation community. You can find some invite links in its chat history by searching for "discord.gg" (including the servers listed below).
Some of these Discord servers have a #other-servers or #related-servers channel with tons of invite links to other similar Discord servers. In particular, these servers have those channels:
- PS1/PS2 Decompilation
- GC/Wii Decompilation
That way, you should be able to find dozens and dozens of Discord servers on that topic. There are still many more out there (I've joined at least six others that aren't directly reachable from the invite links inside the servers I've mentioned).
There are also other Discord servers about reverse-engineering that can contain discussions about decompilation techniques or projects.
- Moon Lights 2: https://github.com/Armonte/ml2decomp
- F-15 Strike Eagle II: https://github.com/neuviemeporte/f15se2-re
I've also been in discussion with people working on decompilation projects which are private. I won't share details, but it includes both well-known games and recent games (as in, built with link time optimizations).
The decompilation community is quite decentralized, with lots of Discord servers specific to one platform or a series of games. In the case of Windows it's also heavily fragmented, as there is no equivalent to community-standard tooling like splat or dtk-decomp for that platform, although my Ghidra extension has carved itself a niche in it.
I happen to be guilty of a variant of this, where I don't bother emitting a 16-bit floating point number instead of a 32-bit one in my CBOR encoder even if it can be represented exactly. That one is laziness.
This looks neat, but if encoding/decoding performance is important, payload size isn't and the integer is bounded, I would just put a fixed-size integer into the payload as-is.
LEB128 (and JSON for that matter) can encode integer values of arbitrary length. This doesn't, which may or may not be important but it's different.
I'll admit that I do not do any cryptographic work with my library and therefore canonical representations aren't a huge concern in my use-cases. I merely provide various configurable limits (max value length, max depth, max items per collection) in an effort to prevent infinitely long documents from hogging my tokenizers indefinitely.
If we concatenate the raw strings together without the null terminator, either all string references will require a length on top of the offset (25% size penalty for a Elf32_Sym), or we'll need a separate descriptor table that stores string offsets and lengths to index into.
If we prepend strings with a length (let's say LEB128), we'll be at best tied with null-terminated strings because we'd have a byte for the length vs. a byte for the terminator. At worst, we'll have a longer string table because we'd need more than one byte to encode a long string length and we would lose the ability to share string suffixes.
Out of all the jank from a.out and COFF that was eliminated with ELF, that representation for the string table was kept (in fact, the only change was mandating a null byte at the beginning to have the offset 0 indicate a null string). It works fine since the 1970s and doesn't cause undue problems, as nothing prevents a parser to spit out std::string_view instead of const char* for the application code.
String tables in most object file formats work like that, a concatenated series of ASCIIZ strings. One byte of overhead (NUL), requires only an offset into one to address a string and you can share strings with common suffixes. It's a very compact layout.
Not trusting the Americans was a French thing ever since De Gaulle. It just took the rest of the Europeans 50 years after his death to pick up on it.
Using the RSP makes this possible at a reasonable framerate, but it requires writing custom microcode for it and Nintendo didn't provide the necessary tools or documentation back in the day.
My read is that this makes pseudo-HDR rendering techniques possible on the N64.
I'm also thinking about writing the Necronomicon of delinking at some point. The extension keeps spreading by word of mouth and there's only so much UX improvements I can do, for something that requires throwing everything you've learned in CS 101 into the trashcan before you can "get" it.
- Volgin is not an agent
- Snake is an agent (USA spy)
- The Boss is a double agent (Soviet/USA spy)
- Eva is a triple agent (USA/Soviet/Chinese spy)
- Ocelot is a quadruple agent (GRU/KGB/CIA/Philosophers spy)
And of course, after that Volgin then kills Granin thinking he was the spy, but he wasn't.
It was a project that started innocently enough, but its domain is unbelievably complex. Recovering MIPS relocation spots from a Ghidra database sounds like an easy enough task, until you're confronted with behemoth functions that span thousands of instructions and undocumented psABI extensions that produces edge cases from Hell.
But then, someone contributed a PoC COFF exporter to go along with the PoC x86 ISA analyzer and after that the Windows video game decompilation picked it up, spreading by word of mouth. I've spent a ridiculously long time fixing bugs and learning about MSVC on-the-fly (quipping "there are lies, damned lies and the Microsoft Portable Executable and Common Object File Format Specification." on the decomp.me Discord server at one point). Then other architectures started creeping up in PRs, first x86_64 and later PowerPC. It's a bottomless pit of toolchains and platforms minutiae that demand perfection to pull off and would drive anyone stark raving mad.
It was bad enough that I let it sit for months at a time, only for someone to message me and fall back into it, then discover it got even more popular while I was away. I also somehow got invited to present a poster about it at ACM CCS 2025 in Taiwan, an absolutely insane story (how many hobbyists are invited to present something at a world-class academic conference on cyber-security?) that while very enlightening also physically wiped me out.
Copilot saved this project and I really mean it. Preparing artifacts, writing tests, performing investigations and large-scale refactorings: hours of grueling, soul-crushing menial work that I no longer have to subject myself to. Features that looked impossible like generating debugging symbols became within reach. The ironclad regression test suite happened to provide the perfect feedback loop. I still review the code and design, but I no longer burn myself out on this madness.
But ghidra-delinker-extension's domain is unusually exacting. Mistakes or shortcuts there will cause undefined behavior of the exotic kind that can be exceedingly difficult to troubleshoot. The only way I've found to keep things from imploding is through unusually exacting quality standards that a drive-by contributor (who usually only cares about a particular ISA/object file combination on a given platform) can't be expected to match.
To be clear, I don't go silent when I receive a PR for this project. In those cases, I do engage with the contributor and give high-level feedback. But my spare time is finite and so is the other person's willingness to jump through all of my hoops.
But our modern embedded firmware projects all use Zephyr and west, so I just created a west manifest, stole parts of the scripts/ folder from the Zephyr repository to have a working "west patch" command and went to town. If I had more time to work on it, I'd have gotten "west build", "west flash" and "west debug" working too (probably with bespoke implementations) and removed the cargo cult shell scripts.
You can use west without Zephyr, it's just that by itself it only provides "west init" and "west update".
By the end, the project manager begged me to turn off my flamethrower, as I was ripping it all out for a clean west manifest to tagged versions and stacks of patches. "Take it home OSS" is like take-out food: if you don't do your chores and leave it out for months or years on the kitchen counter, the next person to enter the apartment is going to puke.
As the maintainer of ghidra-delinker-extension, whenever I get a non-trivial PR (like adding an object file format or ISA analyzer) I'm happy that it happens. It also means that I get to install a toolchain, maybe learn how to use it (MSVC...), figure out all of the nonsense and undocumented bullshit in it (COFF...), write byte-perfect roundtrip parser/serializer plus tests inside binary-file-toolkit if necessary, prepare golden Ghidra databases, write the unit tests for them, make sure that the delinked stuff when relinked actually works, have it pass my standards quality plus the linter and have a clean Git history.
I usually find it easier to take their branch, do all of that work myself (attributing authorship to commits whenever appropriate), push it to the master branch and close the PR than puppeteering someone halfway across the globe through GitHub comments into doing all of that for me.
Conversely, at work I implemented support for PKCS#7 certificate chains inside of Mbed-TLS and diligently submitted PRs upstream. They were correct, commented, documented, tested, everything was spotless to the implicit admission of one of the developers. It's still open today (with merge conflicts naturally) and there are like five open PRs for the exact same feature.
When I see this, I'm not going to insist, I'll move on to my next Jira task.
It uses register dependency graph traversal and code block flow analysis to try and find which two instructions are the likely targets of a HI16/LO16 for a given reference. It also uses an unhealthy amount of recursion and has been rewritten multiple times in order to deal with all these edge cases.
Unfortunately, my extension is only set up for one HI16 relocation having one or many LO16 child relocations. There's an undocumented GNU extension that allows multiple HI16 relocations to be shared by a single LO16 relocation, and I have a really ugly hack that shifts branch targets to "normalize" this into the standard pattern. I don't know why this hack works (I didn't even know about the GNU extension, I thought it was an assembler peephole optimization), but with it my extension can delink the whole of Tenchu: Stealth Assassins without any manual annotations, just the regular references from the cleaned-up Ghidra database.
Lately at work I've done C++ optimization tricks like inplace_map, inplace_string, placement new to inline map-like iterators inside a view adapter's iterators and putting that byte buffer as the first member of the class to not incur std::max_align_t padding with the other members. At a higher architecture level, I wrote a data model binding library that can serialize JSON, YAML and CBOR documents to an output iterator one byte at a time without incurring heap allocation in most cases.
This is because I work on an embedded system with 640 KiB of SRAM and given the sheer amount of run-time data it will have to handle and produce, I'm wary not only about heap usage, but also heap fragmentation.
AI will readily identify such tricks, it can even help implement them, but unless constrained otherwise AI will pick the most expedient solution that answers the question (note that I didn't say answers the problem).
I've implemented a bunch of serialization visitors. For the structured formats, most (JSON, YAML, CBOR with indefinite lengths) use an output iterator and can stream out one character/byte at a time, which is useful when your target is a MCU with 640 KiB of SRAM and you need to reply large REST API responses.
And there's the BSON serializer, which writes to a byte buffer because it uses tag-length-value and I need to backtrack in order to patch in the lengths after serializing the values. This means that the entire document needs to be written upfront before I can do something with it. It also has some annoying quirks, like array indices being strings in base 10.
There are also other trade-offs when dealing with JSON vs. its binary encodings. Strings in JSON may have escape characters that require parsing, if it has them then you can't return a view into the document, you need to allocate a string to hold the decoded value. Whereas in BSON or CBOR (excluding indefinite-length strings) the strings are not escaped and you can return a std::string_view straight from the document (and even a const char* for BSON, as it embeds a NUL character).
Some encodings like CBOR are also more expressive than JSON, allowing for example any value type to be used for map keys and not just strings.