Files
FEX-Emu--FEX/Source/Linux/Utils/ELFContainer.h
T
Ryan Houdek 472675d471 FEXLoader: Adds support for execveat with AT_EMPTY_PATH
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.
2023-01-23 02:06:50 -08:00

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