mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-08 22:00:19 +02:00
Fixes #2136 This is a fairly tricky edge case to support with FEX. If execveat is used with AT_EMPTY_PATH then the application can pass an FD to execve instead of a filename. This includes FDs that have been deleted from the disk so the child process can't open it by filename anymore. To work around this limitation, we need to pass the FD to the new FEX process and open it directly, similar to how binfmt_misc works with FDs. The FD will get passed through environment variables, which the new process will check for and then remove the variable from the environment. Lots of prickly edge cases to support here. Without binfmt_misc: - Passes the FD to FEXLoader directly. - Requires duplicating the FD if it has O_CLOEXEC on the FD. With binfmt_misc: - Shebang file, pass directly to FEXLoader, just like without binfmt. - x86 ELF Files, rely on the kernel's binfmt_misc support here. - Unsupported ELF files, let kernel handle it through binfmt_misc Argument handling: - The application can pass in no arguments. - Means our application configurations were failing to find a config - Also various checks in the frontend were failing. - If opened through an FD, find the symlink for that FD for the application configuration instead. Side note: Fixed a performance issue in execve where when we were checking for file format support. Either ELF or Shebang files, we were reading the /whole/ file upfront. We only need to read a header worth of ELF files, and only 257 bytes if it is potentially a shebang file. Should dramatically reduce some application's execve times.
182 lines
4.4 KiB
C++
182 lines
4.4 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <elf.h>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <stddef.h>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
// Add macros which are missing in some versions of <elf.h>
|
|
#ifndef ELF32_ST_VISIBILITY
|
|
#define ELF32_ST_VISIBILITY(o) ((o) & 0x3)
|
|
#endif
|
|
|
|
#ifndef ELF64_ST_VISIBILITY
|
|
#define ELF64_ST_VISIBILITY(o) ((o) & 0x3)
|
|
#endif
|
|
|
|
namespace ELFLoader {
|
|
struct ELFSymbol {
|
|
uint64_t FileOffset;
|
|
uint64_t Address;
|
|
uint64_t Size;
|
|
uint8_t Type;
|
|
uint8_t Bind;
|
|
uint16_t SectionIndex;
|
|
char const *Name;
|
|
};
|
|
|
|
class ELFContainer {
|
|
public:
|
|
ELFContainer(std::string const &Filename, std::string const &RootFS, bool CustomInterpreter);
|
|
~ELFContainer();
|
|
|
|
uint64_t GetEntryPoint() const {
|
|
if (Mode == MODE_32BIT) {
|
|
return Header._32.e_entry;
|
|
}
|
|
else {
|
|
return Header._64.e_entry;
|
|
}
|
|
}
|
|
|
|
using MemoryLayout = std::tuple<uint64_t, uint64_t, uint64_t>;
|
|
|
|
MemoryLayout GetLayout() const {
|
|
return std::make_tuple(MinPhysicalMemoryLocation, MaxPhysicalMemoryLocation,
|
|
PhysicalMemorySize);
|
|
}
|
|
|
|
struct BRKInfo {
|
|
uint64_t Base;
|
|
uint64_t Size;
|
|
};
|
|
|
|
BRKInfo GetBRKInfo() const {
|
|
return {BRKBase, BRKSize};
|
|
}
|
|
|
|
// Data, Physical, Size
|
|
using MemoryWriter = std::function<void(void *, uint64_t, uint64_t)>;
|
|
void WriteLoadableSections(MemoryWriter Writer, uint64_t Offset = 0);
|
|
|
|
ELFSymbol const *GetSymbol(char const *Name);
|
|
ELFSymbol const *GetSymbol(uint64_t Address);
|
|
|
|
using RangeType = std::pair<uint64_t, uint64_t>;
|
|
ELFSymbol const *GetSymbolInRange(RangeType Address);
|
|
|
|
bool WasDynamic() const { return DynamicProgram; }
|
|
bool HasDynamicLinker() const { return !DynamicLinker.empty(); }
|
|
bool WasLoaded() const { return Loaded; }
|
|
std::string &InterpreterLocation() { return DynamicLinker; }
|
|
|
|
std::vector<char const*> const *GetNecessaryLibs() const { return &NecessaryLibs; }
|
|
|
|
void PrintRelocationTable() const;
|
|
|
|
using SymbolGetter = std::function<ELFSymbol*(char const*, uint8_t)>;
|
|
void FixupRelocations(void *ELFBase, uint64_t GuestELFBase, SymbolGetter Getter);
|
|
|
|
using SymbolAdder = std::function<void(ELFSymbol*)>;
|
|
void AddSymbols(SymbolAdder Adder);
|
|
|
|
using UnwindAdder = std::function<void(uintptr_t)>;
|
|
void AddUnwindEntries(UnwindAdder Adder);
|
|
|
|
void GetInitLocations(uint64_t GuestELFBase, std::vector<uint64_t> *Locations);
|
|
|
|
|
|
bool HasTLS() const { return TLSHeader._64 != nullptr; }
|
|
uint64_t GetTLSBase() const {
|
|
if (GetMode() == ELFMode::MODE_64BIT) {
|
|
return TLSHeader._64->p_vaddr;
|
|
}
|
|
else {
|
|
return TLSHeader._32->p_vaddr;
|
|
}
|
|
}
|
|
|
|
enum ELFMode {
|
|
MODE_32BIT,
|
|
MODE_64BIT,
|
|
};
|
|
|
|
ELFMode GetMode() const { return Mode; }
|
|
size_t GetProgramHeaderCount() const { return ProgramHeaders.size(); }
|
|
|
|
enum ELFType {
|
|
TYPE_NONE,
|
|
TYPE_X86_64,
|
|
TYPE_X86_32,
|
|
TYPE_OTHER_ELF,
|
|
};
|
|
static ELFType GetELFType(std::string const &Filename);
|
|
static ELFType GetELFType(int FD);
|
|
static bool IsSupportedELF(std::string const &Filename) {
|
|
ELFType Type = GetELFType(Filename);
|
|
return Type == TYPE_X86_64 || Type == TYPE_X86_32;
|
|
}
|
|
|
|
private:
|
|
bool LoadELF(std::string const &Filename);
|
|
bool LoadELF_32();
|
|
bool LoadELF_64();
|
|
void CalculateMemoryLayouts();
|
|
void CalculateSymbols();
|
|
void GetDynamicLibs();
|
|
|
|
// Information functions
|
|
void PrintHeader() const;
|
|
void PrintSectionHeaders() const;
|
|
void PrintProgramHeaders() const;
|
|
void PrintSymbolTable() const;
|
|
void PrintInitArray() const;
|
|
void PrintDynamicTable() const;
|
|
|
|
std::vector<char> RawFile;
|
|
union {
|
|
Elf32_Ehdr _32;
|
|
Elf64_Ehdr _64;
|
|
} Header;
|
|
|
|
union SectionHeader {
|
|
Elf32_Shdr *_32;
|
|
Elf64_Shdr *_64;
|
|
};
|
|
|
|
union ProgramHeader {
|
|
Elf32_Phdr *_32;
|
|
Elf64_Phdr *_64;
|
|
};
|
|
|
|
ELFMode Mode;
|
|
std::vector<SectionHeader> SectionHeaders;
|
|
std::vector<ProgramHeader> ProgramHeaders;
|
|
std::vector<ELFSymbol> Symbols;
|
|
std::vector<uintptr_t> UnwindEntries;
|
|
std::unordered_map<std::string, ELFSymbol *> SymbolMap;
|
|
std::map<uint64_t, ELFSymbol *> SymbolMapByAddress;
|
|
|
|
std::vector<char const*> NecessaryLibs;
|
|
|
|
uint64_t MinPhysicalMemoryLocation{0};
|
|
uint64_t MaxPhysicalMemoryLocation{0};
|
|
uint64_t PhysicalMemorySize{0};
|
|
|
|
uint64_t BRKBase{};
|
|
uint64_t BRKSize{};
|
|
ProgramHeader InterpreterHeader{};
|
|
bool DynamicProgram{false};
|
|
std::string DynamicLinker;
|
|
ProgramHeader TLSHeader{};
|
|
bool Loaded {false};
|
|
};
|
|
|
|
} // namespace ELFLoader
|