#pragma once #include "fs/VFS.h" #include "memory/AddressSpace.h" class BinaryFormatLoader : public Shareable { public: /** * @brief Determine if the given executable file matches this binary format. * * @return Result An error, or whether the file matches the binary format. */ virtual Result sniff() = 0; /** * @brief Load the given executable binary file into an address space. * * Depending on the binary format, this function may load an arbitrary interpreter instead, which will interpret the * file on its own. * * @param space The address space to load the executable into. * @return Result An error, or the entry point of the executable in memory. */ virtual Result load(AddressSpace* space) = 0; /** * @brief Return the short name associated with this format. * * @return StringView The format's name. */ virtual StringView format() const = 0; /** * @brief Transform an interpreted program's command line arguments. * * Example: A script 'foo.sh' with a shebang line '#!/bin/sh' is loaded using a BinaryFormatLoader. * * This function will then be called with path="/path/to/foo.sh" and args={"foo.sh", "--enable-bar"} * * The function should return {"/bin/sh", "/path/to/foo.sh", "--enable-bar"} (prepending the interpreter command * line and substituting args[0] for the full path of the script). * * For native executable formats that do not require an interpreter (e.g. ELF), this function should just ignore * path and return args unmodified. * * @param path The path (absolute or relative to the current process's working directory) of the current program * file. This should be used instead of args[0] as arbitrary values can be passed there, leaving the interpreter * unable to find the target program. * @param args The original command line arguments passed to execve(). * @return Result> An error, or the transformed command line arguments. */ virtual Result> cmdline(const String& path, Vector args) = 0; virtual ~BinaryFormatLoader() = default; /** * @brief Construct a new BinaryFormatLoader. * * This should not be directly used, instead each subclass of BinaryFormatLoader should implement a static create() * method which implements the binfmt_loader_creator_t type and register it using * BinaryFormat::register_binary_format(). * * Then, anyone that needs an appropriate BinaryFormatLoader for an executable file should call * BinaryFormat::create_loader(), which will find an appropriate loader out of all default/registered loaders. * * @param inode The executable program file to load into memory. * @param recursion_level In normal cases, 0. If the BinaryFormatLoader is created inside another loader (for * example, a script loader loading the script's interpreter), the caller shall pass its recursion_level + 1 to * BinaryFormat::create_loader(), which will forward it to this constructor. This avoids infinite recursion. */ BinaryFormatLoader(SharedPtr inode, int recursion_level); protected: SharedPtr m_inode; int m_recursion_level; }; /** * @brief The factory function signature for binary format loaders. */ typedef Result> (*binfmt_loader_creator_t)(SharedPtr, void*, int); namespace BinaryFormat { /** * @brief Register the default binary format loaders. * * @return Result Whether the operation succeeded. */ Result init(); /** * @brief Register a new binary format loader type. * * @param creator A factory function to create said binary format loader. The function shall be passed the inode to * load the program from, the arbitrary argument passed to this function, and a recursion index to avoid infinite * recursion. * @param arg An arbitrary argument that will be passed to the above factory function. * @return Result Whether the operation succeeded. */ Result register_binary_format(binfmt_loader_creator_t creator, void* arg); /** * @brief Create an appropriate loader object for an executable file. * * @param inode The executable file to create the loader for. If no appropriate loader could be found for this file * type, this function shall return ENOEXEC. * @param recursion_level In most cases, 0. If called inside another loader, the loader shall pass its * own recursion_level variable + 1, to avoid infinite recursion. If recursion_level >= 8, this function shall * immediately return ELOOP. * @return Result> An error, or the new loader object created for this file. */ Result> create_loader(SharedPtr inode, int recursion_level = 0); }