kernel/binfmt: Add documentation + support script interpreters being scripts themselves
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
d01087362e
commit
4c87d72b44
@ -23,17 +23,20 @@ Result<void> BinaryFormat::register_binary_format(binfmt_loader_creator_t creato
|
||||
return g_binary_formats.try_append({ creator, arg });
|
||||
}
|
||||
|
||||
Result<SharedPtr<BinaryFormatLoader>> BinaryFormat::create_loader(SharedPtr<VFS::Inode> inode)
|
||||
Result<SharedPtr<BinaryFormatLoader>> BinaryFormat::create_loader(SharedPtr<VFS::Inode> inode, int recursion_level)
|
||||
{
|
||||
if (recursion_level >= 8) return err(ELOOP);
|
||||
|
||||
for (const auto& format : g_binary_formats)
|
||||
{
|
||||
auto loader = TRY(format.creator(inode, format.arg));
|
||||
auto loader = TRY(format.creator(inode, format.arg, recursion_level));
|
||||
if (TRY(loader->sniff())) return loader;
|
||||
}
|
||||
|
||||
return err(ENOEXEC);
|
||||
}
|
||||
|
||||
BinaryFormatLoader::BinaryFormatLoader(SharedPtr<VFS::Inode> inode) : m_inode(inode)
|
||||
BinaryFormatLoader::BinaryFormatLoader(SharedPtr<VFS::Inode> inode, int recursion_level)
|
||||
: m_inode(inode), m_recursion_level(recursion_level)
|
||||
{
|
||||
}
|
||||
|
@ -5,28 +5,110 @@
|
||||
class BinaryFormatLoader : public Shareable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Determine if the given executable file matches this binary format.
|
||||
*
|
||||
* @return Result<bool> An error, or whether the file matches the binary format.
|
||||
*/
|
||||
virtual Result<bool> 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<u64> An error, or the entry point of the executable in memory.
|
||||
*/
|
||||
virtual Result<u64> load(AddressSpace* space) = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the short name associated with this format.
|
||||
*
|
||||
* @return StringView The format's name.
|
||||
*/
|
||||
virtual StringView format() const = 0;
|
||||
|
||||
virtual Result<Vector<String>> cmdline(Vector<String> args) = 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<Vector<String>> An error, or the transformed command line arguments.
|
||||
*/
|
||||
virtual Result<Vector<String>> cmdline(const String& path, Vector<String> args) = 0;
|
||||
|
||||
virtual ~BinaryFormatLoader() = default;
|
||||
|
||||
BinaryFormatLoader(SharedPtr<VFS::Inode>);
|
||||
/**
|
||||
* @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<VFS::Inode> inode, int recursion_level);
|
||||
|
||||
protected:
|
||||
SharedPtr<VFS::Inode> m_inode;
|
||||
int m_recursion_level;
|
||||
};
|
||||
|
||||
typedef Result<SharedPtr<BinaryFormatLoader>> (*binfmt_loader_creator_t)(SharedPtr<VFS::Inode>, void*);
|
||||
/**
|
||||
* @brief The factory function signature for binary format loaders.
|
||||
*/
|
||||
typedef Result<SharedPtr<BinaryFormatLoader>> (*binfmt_loader_creator_t)(SharedPtr<VFS::Inode>, void*, int);
|
||||
|
||||
namespace BinaryFormat
|
||||
{
|
||||
/**
|
||||
* @brief Register the default binary format loaders.
|
||||
*
|
||||
* @return Result<void> Whether the operation succeeded.
|
||||
*/
|
||||
Result<void> 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<void> Whether the operation succeeded.
|
||||
*/
|
||||
Result<void> register_binary_format(binfmt_loader_creator_t creator, void* arg);
|
||||
|
||||
Result<SharedPtr<BinaryFormatLoader>> create_loader(SharedPtr<VFS::Inode> inode);
|
||||
/**
|
||||
* @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<SharedPtr<BinaryFormatLoader>> An error, or the new loader object created for this file.
|
||||
*/
|
||||
Result<SharedPtr<BinaryFormatLoader>> create_loader(SharedPtr<VFS::Inode> inode, int recursion_level = 0);
|
||||
}
|
||||
|
@ -132,16 +132,16 @@ Result<u64> ELFLoader::load(AddressSpace* space)
|
||||
return elf_header.e_entry;
|
||||
}
|
||||
|
||||
Result<Vector<String>> ELFLoader::cmdline(Vector<String> args)
|
||||
Result<Vector<String>> ELFLoader::cmdline(const String&, Vector<String> args)
|
||||
{
|
||||
return args;
|
||||
}
|
||||
|
||||
ELFLoader::ELFLoader(SharedPtr<VFS::Inode> inode) : BinaryFormatLoader(inode)
|
||||
ELFLoader::ELFLoader(SharedPtr<VFS::Inode> inode, int recursion_level) : BinaryFormatLoader(inode, recursion_level)
|
||||
{
|
||||
}
|
||||
|
||||
Result<SharedPtr<BinaryFormatLoader>> ELFLoader::create(SharedPtr<VFS::Inode> inode, void*)
|
||||
Result<SharedPtr<BinaryFormatLoader>> ELFLoader::create(SharedPtr<VFS::Inode> inode, void*, int recursion_level)
|
||||
{
|
||||
return (SharedPtr<BinaryFormatLoader>)TRY(make_shared<ELFLoader>(inode));
|
||||
return (SharedPtr<BinaryFormatLoader>)TRY(make_shared<ELFLoader>(inode, recursion_level));
|
||||
}
|
||||
|
@ -55,14 +55,14 @@ class ELFLoader : public BinaryFormatLoader
|
||||
Result<bool> sniff() override;
|
||||
Result<u64> load(AddressSpace* space) override;
|
||||
|
||||
Result<Vector<String>> cmdline(Vector<String> args) override;
|
||||
Result<Vector<String>> cmdline(const String& path, Vector<String> args) override;
|
||||
|
||||
StringView format() const override
|
||||
{
|
||||
return "elf";
|
||||
}
|
||||
|
||||
ELFLoader(SharedPtr<VFS::Inode> inode);
|
||||
ELFLoader(SharedPtr<VFS::Inode> inode, int recursion_level);
|
||||
|
||||
static Result<SharedPtr<BinaryFormatLoader>> create(SharedPtr<VFS::Inode> inode, void*);
|
||||
static Result<SharedPtr<BinaryFormatLoader>> create(SharedPtr<VFS::Inode> inode, void*, int);
|
||||
};
|
||||
|
@ -39,23 +39,29 @@ Result<u64> ScriptLoader::load(AddressSpace* space)
|
||||
TRY(VFS::resolve_path(interpreter_path.chars(), current->auth, current->current_directory, true));
|
||||
if (!VFS::can_execute(interpreter, current->auth)) return err(EACCES);
|
||||
|
||||
auto loader = TRY(ELFLoader::create(interpreter, nullptr));
|
||||
return loader->load(space);
|
||||
auto loader = TRY(BinaryFormat::create_loader(interpreter, m_recursion_level + 1));
|
||||
u64 entry = TRY(loader->load(space));
|
||||
|
||||
m_interpreter_cmdline = TRY(loader->cmdline(interpreter_path, move(m_interpreter_cmdline)));
|
||||
return entry;
|
||||
}
|
||||
|
||||
Result<Vector<String>> ScriptLoader::cmdline(Vector<String> args)
|
||||
Result<Vector<String>> ScriptLoader::cmdline(const String& path, Vector<String> args)
|
||||
{
|
||||
Vector<String> new_args;
|
||||
for (auto& arg : m_interpreter_cmdline) { TRY(new_args.try_append(move(arg))); }
|
||||
for (auto& arg : args) { TRY(new_args.try_append(move(arg))); }
|
||||
auto arg = TRY(path.clone());
|
||||
TRY(new_args.try_append(move(arg)));
|
||||
for (usize i = 1; i < args.size(); i++) { TRY(new_args.try_append(move(args[i]))); }
|
||||
return new_args;
|
||||
}
|
||||
|
||||
ScriptLoader::ScriptLoader(SharedPtr<VFS::Inode> inode) : BinaryFormatLoader(inode)
|
||||
ScriptLoader::ScriptLoader(SharedPtr<VFS::Inode> inode, int recursion_level)
|
||||
: BinaryFormatLoader(inode, recursion_level)
|
||||
{
|
||||
}
|
||||
|
||||
Result<SharedPtr<BinaryFormatLoader>> ScriptLoader::create(SharedPtr<VFS::Inode> inode, void*)
|
||||
Result<SharedPtr<BinaryFormatLoader>> ScriptLoader::create(SharedPtr<VFS::Inode> inode, void*, int recursion_level)
|
||||
{
|
||||
return (SharedPtr<BinaryFormatLoader>)TRY(make_shared<ScriptLoader>(inode));
|
||||
return (SharedPtr<BinaryFormatLoader>)TRY(make_shared<ScriptLoader>(inode, recursion_level));
|
||||
}
|
||||
|
@ -9,16 +9,16 @@ class ScriptLoader : public BinaryFormatLoader
|
||||
Result<bool> sniff() override;
|
||||
Result<u64> load(AddressSpace* space) override;
|
||||
|
||||
Result<Vector<String>> cmdline(Vector<String> args) override;
|
||||
Result<Vector<String>> cmdline(const String& path, Vector<String> args) override;
|
||||
|
||||
StringView format() const override
|
||||
{
|
||||
return "script";
|
||||
}
|
||||
|
||||
ScriptLoader(SharedPtr<VFS::Inode> inode);
|
||||
ScriptLoader(SharedPtr<VFS::Inode> inode, int recursion_level);
|
||||
|
||||
static Result<SharedPtr<BinaryFormatLoader>> create(SharedPtr<VFS::Inode> inode, void*);
|
||||
static Result<SharedPtr<BinaryFormatLoader>> create(SharedPtr<VFS::Inode> inode, void*, int);
|
||||
|
||||
private:
|
||||
Vector<String> m_interpreter_cmdline;
|
||||
|
@ -80,7 +80,7 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
||||
|
||||
auto image = TRY(ThreadImage::try_load_from_binary(loader));
|
||||
|
||||
argv = TRY(loader->cmdline(move(argv)));
|
||||
argv = TRY(loader->cmdline(path, move(argv)));
|
||||
|
||||
u64 user_argv = TRY(image->push_string_vector_on_stack(argv));
|
||||
usize user_argc = argv.size();
|
||||
|
@ -148,7 +148,7 @@ namespace Scheduler
|
||||
auto guard = make_scope_guard([&] { delete thread; });
|
||||
|
||||
// Contrary to other programs, which use BinaryFormat::create_loader(), init must be a native executable.
|
||||
auto loader = TRY(ELFLoader::create(inode, nullptr));
|
||||
auto loader = TRY(ELFLoader::create(inode, nullptr, 0));
|
||||
|
||||
auto image = TRY(ThreadImage::try_load_from_binary(loader));
|
||||
u64 argv = TRY(image->push_string_vector_on_stack(args));
|
||||
|
Loading…
Reference in New Issue
Block a user