Syscalls, NIDs, Imports??
Syscalls, NIDs, Imports?? If you know what HBL is, you have at least heard one of those three terms, especially “syscall”. Most time spent on developing HBL was trying to improve syscall estimation (even if now it’s broken again). Syscall estimation is one of the most advanced and important parts of HBL, without it you wouldn’t able to run so many awesome homebrews on your PSV or PSP! Most of the times, people talk about this and have no idea what they’re talking about, so here’s a brief explanation.
(I will not explain how HBL estimates syscalls or how the PSP kernel handles syscalls, I will only explain the core concepts, so you will be able to understand better how HBL works)
— Interrupts —
Syscalls are probably the most difficult concept to understand, so let’s start off with them.
First of all, what is a syscall? A syscall is a software interrupt. I don’t expect you to know what an interrupt is, so I’m going to give you a quick introduction. There are two types of interrupts: hardware and software.
Hardware interrupts are “events” triggered by peripheral devices, like the HDD in your computer or the UMD drive in your PSP.
Software interrupts, on the other hand, are “events” triggered by software itself, syscalls are software interrupts. At boot, the system assigns a handler to each interrupt, so when an interrupt is triggered, it knows what to do; even syscalls have their own handler.
— Syscalls —
So, syscalls are software interrupts, now, what are syscalls for?
Well, you may know there are different levels of permission in a system: user and kernel. Code running in user mode can’t access kernel mode functions just by jumping to them, like it would do with user mode functions, but some functions require kernel mode, so what’s the solution? Syscalls. Syscalls have “numbers”, every number corresponds to a certain function: when some usermode software needs to call a kernelmode function, it triggers the syscall with the number for that function.
For example: There are a bunch of kernel functions we can call:
- 1 – kernel_doesthis
- 2 – kernel_doesthat
- 10 – kernel_hello
and we want to call kernel_hello; of course, since it’s a kernel function, we can’t just jump to its address, we have to trigger a syscall. Since kernel_hello corresponds to syscall number 10, we can call it like this: syscall 10 (this is just pseudo-code, this is not a programming tutorial.)
This is just an example, on PSP, things are most difficult: back in first firmware versions, syscalls were “hardcoded” (means a function had always the same syscall number), but nowadays, syscalls are random. To know what function to call when a certain syscall is triggered, the system keeps a list in kernel memory (so we can’t access it), when an executable is launched that list is filled, but “randomly” (it’s not completely random, there are many factors involved and we know how they are calculated, but I don’t want to go too technical here).
— NIDs —
This is the easiest part of the explanation: a NID (Name Identifier) is an identifier for an exported function (an exported function is a function that can be called outside of the module, as long as the module that contains it is running).
A NID is just the first 32 bits (little-endian) of the SHA1 of the function’s name, this explanation is actually taken from SilverSpring, who actually cracked a lot of NIDs (yes, they can be bruteforced), I just resumed it as his blog is now offline.
NIDs of kernel functions change after some firmware revisions, usermode NIDs should never change (otherwise old games wouldn’t work). NIDs are used by the system to understand what function to link to a certain import.
— Imports —
PSP system provides an API for many tasks like graphics or threading. Applications can call any function they need from that API, but first, they have to declare what functions they need.
When the PSP system executes an application, it parses “imports”, a list of libraries and NIDs, and links every import to the corresponding function: imports of kernel functions will become syscalls and user imports will become jumps. HBL does exactly the same: when you launch an homebrew with HBL, it can’t ask the system to resolve the imports of that homebrew as it would require kernel access (a kernel exploit, basically), instead, it resolves imports for you. The problem is it can’t access the syscall list, so it has to guess syscalls starting from the syscalls it already knows (the ones imported by the game used to run it)
I hope this helps you understanding why HBL isn’t “perfect” and why developing it is so hard 😉