Skip to content

Commit

Permalink
Implement loading of linked ELF executables.
Browse files Browse the repository at this point in the history
This took me a couple hours. :^)

The ELF loading code now allocates a single region for the entire
file and creates virtual memory mappings for the sections as needed.

Very nice!
  • Loading branch information
awesomekling committed Oct 27, 2018
1 parent 99ee6ac commit 9a71c77
Show file tree
Hide file tree
Showing 16 changed files with 258 additions and 57 deletions.
12 changes: 12 additions & 0 deletions ELFLoader/ELFImage.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class ELFImage {
unsigned size() const { return m_sectionHeader.sh_size; }
unsigned entrySize() const { return m_sectionHeader.sh_entsize; }
unsigned entryCount() const { return size() / entrySize(); }
dword address() const { return m_sectionHeader.sh_addr; }
const char* rawData() const { return m_image.rawData(m_sectionHeader.sh_offset); }
bool isUndefined() const { return m_sectionIndex == SHN_UNDEF; }
const RelocationSection relocations() const;
Expand Down Expand Up @@ -115,13 +116,17 @@ class ELFImage {
const Symbol symbol(unsigned) const;
const Section section(unsigned) const;

template<typename F> void forEachSection(F) const;
template<typename F> void forEachSectionOfType(unsigned, F) const;
template<typename F> void forEachSymbol(F) const;

// NOTE: Returns section(0) if section with name is not found.
// FIXME: I don't love this API.
const Section lookupSection(const char* name) const;

bool isExecutable() const { return header().e_type == ET_EXEC; }
bool isRelocatable() const { return header().e_type == ET_REL; }

private:
bool parseHeader();
const char* rawData(unsigned offset) const;
Expand All @@ -142,6 +147,13 @@ class ELFImage {
unsigned m_stringTableSectionIndex { 0 };
};

template<typename F>
inline void ELFImage::forEachSection(F func) const
{
for (unsigned i = 0; i < sectionCount(); ++i)
func(section(i));
}

template<typename F>
inline void ELFImage::forEachSectionOfType(unsigned type, F func) const
{
Expand Down
29 changes: 25 additions & 4 deletions ELFLoader/ELFLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,26 @@ bool ELFLoader::layout()
kprintf("[ELFLoader] Layout\n");
#endif
bool failed = false;
dword highestOffset = 0;
dword sizeNeeded = 0;
m_image->forEachSection([&] (auto& section) {
if (section.offset() > highestOffset) {
highestOffset = section.offset();
sizeNeeded = highestOffset + section.size();
}
});
#ifdef ELFLOADER_DEBUG
kprintf("[ELFLoader] Highest section offset: %u, Size needed: %u\n", highestOffset, sizeNeeded);
#endif
m_execSpace.allocateUniverse(sizeNeeded);

m_image->forEachSectionOfType(SHT_PROGBITS, [this, &failed] (const ELFImage::Section& section) {
#ifdef ELFLOADER_DEBUG
kprintf("[ELFLoader] Allocating progbits section: %s\n", section.name());
#endif
if (!section.size())
return true;
char* ptr = m_execSpace.allocateArea(section.name(), section.size());
char* ptr = m_execSpace.allocateArea(section.name(), section.size(), section.offset(), LinearAddress(section.address()));
if (!ptr) {
kprintf("ELFLoader: failed to allocate section '%s'\n", section.name());
failed = true;
Expand All @@ -62,7 +75,7 @@ bool ELFLoader::layout()
#endif
if (!section.size())
return true;
char* ptr = m_execSpace.allocateArea(section.name(), section.size());
char* ptr = m_execSpace.allocateArea(section.name(), section.size(), section.offset(), LinearAddress(section.address()));
if (!ptr) {
kprintf("ELFLoader: failed to allocate section '%s'\n", section.name());
failed = true;
Expand Down Expand Up @@ -163,8 +176,16 @@ void ELFLoader::exportSymbols()
#ifdef ELFLOADER_DEBUG
kprintf("symbol: %u, type=%u, name=%s, section=%u\n", symbol.index(), symbol.type(), symbol.name(), symbol.sectionIndex());
#endif
if (symbol.type() == STT_FUNC)
m_execSpace.addSymbol(symbol.name(), areaForSection(symbol.section()) + symbol.value(), symbol.size());
if (symbol.type() == STT_FUNC) {
char* ptr;
if (m_image->isExecutable())
ptr = (char*)symbol.value();
else if (m_image->isRelocatable())
ptr = areaForSection(symbol.section()) + symbol.value();
else
ASSERT_NOT_REACHED();
m_execSpace.addSymbol(symbol.name(), ptr, symbol.size());
}
// FIXME: What about other symbol types?
return true;
});
Expand Down
26 changes: 19 additions & 7 deletions ELFLoader/ExecSpace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,19 +101,31 @@ char* ExecSpace::symbolPtr(const char* name)
return nullptr;
}

char* ExecSpace::allocateArea(String&& name, unsigned size)
void ExecSpace::allocateUniverse(size_t size)
{
char* ptr;
ASSERT(!m_universe);
if (hookableAlloc)
ptr = static_cast<char*>(hookableAlloc(name, size));
m_universe = static_cast<char*>(hookableAlloc("elf-sec", size));
else
ptr = static_cast<char*>(kmalloc(size));
if (size)
ASSERT(ptr);
m_areas.append(make<Area>(move(name), ptr, size));
m_universe = static_cast<char*>(kmalloc(size));
}

char* ExecSpace::allocateArea(String&& name, unsigned size, dword offset, LinearAddress laddr)
{
ASSERT(m_universe);
char* ptr = m_universe + offset;
m_areas.append(make<Area>(move(name), offset, ptr, size, laddr));
return ptr;
}

void ExecSpace::forEachArea(Function<void(const String& name, dword offset, size_t size, LinearAddress)> callback)
{
for (auto& a : m_areas) {
auto& area = *a;
callback(area.name, area.offset, area.size, area.laddr);
}
}

void ExecSpace::addSymbol(String&& name, char* ptr, unsigned size)
{
m_symbols.set(move(name), { ptr, size });
Expand Down
13 changes: 11 additions & 2 deletions ELFLoader/ExecSpace.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,20 @@ class ELFLoader;
class ExecSpace {
public:
struct Area {
Area(String&& n, char* m, unsigned s)
Area(String&& n, dword o, char* m, unsigned s, LinearAddress l)
: name(move(n))
, offset(o)
, memory(m)
, size(s)
, laddr(l)
{
}

String name;
dword offset { 0 };
char* memory { 0 };
unsigned size { 0 };
LinearAddress laddr;
};

struct PtrAndSize {
Expand Down Expand Up @@ -48,13 +52,18 @@ class ExecSpace {

char* symbolPtr(const char* name);

char* allocateArea(String&& name, unsigned size);
char* allocateArea(String&& name, unsigned size, dword offset, LinearAddress);
void addSymbol(String&& name, char* ptr, unsigned size);

void allocateUniverse(size_t);

void forEachArea(Function<void(const String& name, dword offset, size_t size, LinearAddress)>);

private:
void initializeBuiltins();

Vector<OwnPtr<Area>> m_areas;
HashMap<String, PtrAndSize> m_symbols;
char* m_universe { nullptr };
};

6 changes: 2 additions & 4 deletions Kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ ARCH_FLAGS =
STANDARD_FLAGS = -std=c++17 -nostdinc++ -nostdlib
KERNEL_FLAGS = -ffreestanding -fno-stack-protector -fno-ident
WARNING_FLAGS = -Wextra -Wall -Wundef -Wcast-qual -Wwrite-strings
FLAVOR_FLAGS = -mregparm=3 -march=i386 -m32 -fno-exceptions -fno-rtti -ffunction-sections -fdata-sections -fmerge-all-constants -fno-unroll-loops -falign-functions=1 -falign-jumps=1 -falign-loops=1 -fno-pie -fno-pic
#FLAVOR_FLAGS = -mregparm=3 -march=i386 -m32 -fno-exceptions -fno-rtti -ffunction-sections -fdata-sections
FLAVOR_FLAGS = -mregparm=3 -march=i386 -m32 -fno-exceptions -fno-rtti -fmerge-all-constants -fno-unroll-loops -fno-pie -fno-pic
OPTIMIZATION_FLAGS = -Os -fno-asynchronous-unwind-tables
INCLUDE_FLAGS = -I.. -I.

Expand All @@ -65,11 +64,10 @@ DEFINES = -DSERENITY -DSANITIZE_PTRS
CXXFLAGS = $(WARNING_FLAGS) $(OPTIMIZATION_FLAGS) $(KERNEL_FLAGS) $(FLAVOR_FLAGS) $(ARCH_FLAGS) $(STANDARD_FLAGS) $(INCLUDE_FLAGS) $(DEFINES)
#CXX = /usr/local/gcc-4.8.1-for-linux64/bin/x86_64-pc-linux-g++
#LD = /usr/local/gcc-4.8.1-for-linux64/bin/x86_64-pc-linux-ld
CXX = g++
CXX = g++-8
LD = ld
LDFLAGS = -T linker.ld --strip-debug -melf_i386 --gc-sections --build-id=none -z norelro -z now


all: $(KERNEL) $(IMAGE) kernel.map

kernel.map: kernel
Expand Down
76 changes: 69 additions & 7 deletions Kernel/MemoryManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

static MemoryManager* s_the;

MemoryManager& MemoryManager::the()
MemoryManager& MM
{
return *s_the;
}
Expand Down Expand Up @@ -43,8 +43,8 @@ void MemoryManager::initializePaging()

identityMap(LinearAddress(4096), 4 * MB);

// Put pages between 4MB and 16MB in the page freelist.
for (size_t i = (4 * MB) + 1024; i < (16 * MB); i += PAGE_SIZE) {
// Put pages between 4MB and 8MB in the page freelist.
for (size_t i = (4 * MB) + PAGE_SIZE; i < (8 * MB); i += PAGE_SIZE) {
m_freePages.append(PhysicalAddress(i));
}

Expand All @@ -56,6 +56,14 @@ void MemoryManager::initializePaging()
);
}

void* MemoryManager::allocatePageTable()
{
auto ppages = allocatePhysicalPages(1);
dword address = ppages[0].get();
identityMap(LinearAddress(address), 4096);
return (void*)address;
}

auto MemoryManager::ensurePTE(LinearAddress linearAddress) -> PageTableEntry
{
ASSERT_INTERRUPTS_DISABLED();
Expand All @@ -76,8 +84,13 @@ auto MemoryManager::ensurePTE(LinearAddress linearAddress) -> PageTableEntry
pde.setPresent(true);
pde.setWritable(true);
} else {
// FIXME: We need an allocator!
ASSERT_NOT_REACHED();
auto* pageTable = allocatePageTable();
kprintf("allocated page table %u (for laddr=%p) at %p\n", pageDirectoryIndex, linearAddress.get(), pageTable);
memset(pageTable, 0, 4096);
pde.setPageTableBase((dword)pageTable);
pde.setUserAllowed(true);
pde.setPresent(true);
pde.setWritable(true);
}
}
return PageTableEntry(&pde.pageTableBase()[pageTableIndex]);
Expand Down Expand Up @@ -190,7 +203,27 @@ bool MemoryManager::unmapRegion(Task& task, Task::Region& region)
pte.setWritable(false);
pte.setUserAllowed(false);
flushTLB(laddr);
// kprintf("MM: >> Unmapped L%x => P%x <<\n", laddr, zone.m_pages[i].get());
//kprintf("MM: >> Unmapped L%x => P%x <<\n", laddr, zone.m_pages[i].get());
}
return true;
}

bool MemoryManager::unmapSubregion(Task& task, Task::Subregion& subregion)
{
InterruptDisabler disabler;
auto& region = *subregion.region;
auto& zone = *region.zone;
size_t numPages = subregion.size / 4096;
ASSERT(numPages);
for (size_t i = 0; i < numPages; ++i) {
auto laddr = subregion.linearAddress.offset(i * PAGE_SIZE);
auto pte = ensurePTE(laddr);
pte.setPhysicalPageBase(0);
pte.setPresent(false);
pte.setWritable(false);
pte.setUserAllowed(false);
flushTLB(laddr);
//kprintf("MM: >> Unmapped subregion %s L%x => P%x <<\n", subregion.name.characters(), laddr, zone.m_pages[i].get());
}
return true;
}
Expand All @@ -202,6 +235,31 @@ bool MemoryManager::unmapRegionsForTask(Task& task)
if (!unmapRegion(task, *region))
return false;
}
for (auto& subregion : task.m_subregions) {
if (!unmapSubregion(task, *subregion))
return false;
}
return true;
}

bool MemoryManager::mapSubregion(Task& task, Task::Subregion& subregion)
{
InterruptDisabler disabler;
auto& region = *subregion.region;
auto& zone = *region.zone;
size_t firstPage = subregion.offset / 4096;
size_t numPages = subregion.size / 4096;
ASSERT(numPages);
for (size_t i = 0; i < numPages; ++i) {
auto laddr = subregion.linearAddress.offset(i * PAGE_SIZE);
auto pte = ensurePTE(laddr);
pte.setPhysicalPageBase(zone.m_pages[firstPage + i].get());
pte.setPresent(true);
pte.setWritable(true);
pte.setUserAllowed(!task.isRing0());
flushTLB(laddr);
//kprintf("MM: >> Mapped subregion %s L%x => P%x (%u into region)<<\n", subregion.name.characters(), laddr, zone.m_pages[firstPage + i].get(), subregion.offset);
}
return true;
}

Expand Down Expand Up @@ -229,6 +287,10 @@ bool MemoryManager::mapRegionsForTask(Task& task)
if (!mapRegion(task, *region))
return false;
}
for (auto& subregion : task.m_subregions) {
if (!mapSubregion(task, *subregion))
return false;
}
return true;
}

Expand All @@ -243,7 +305,7 @@ bool copyToZone(Zone& zone, const void* data, size_t size)
auto* dataptr = (const byte*)data;
size_t remaining = size;
for (size_t i = 0; i < zone.m_pages.size(); ++i) {
byte* dest = MemoryManager::the().quickMapOnePage(zone.m_pages[i]);
byte* dest = MM.quickMapOnePage(zone.m_pages[i]);
kprintf("memcpy(%p, %p, %u)\n", dest, dataptr, min(PAGE_SIZE, remaining));
memcpy(dest, dataptr, min(PAGE_SIZE, remaining));
dataptr += PAGE_SIZE;
Expand Down
9 changes: 9 additions & 0 deletions Kernel/MemoryManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ struct Zone : public Retainable<Zone> {

bool copyToZone(Zone&, const void* data, size_t);

#define MM MemoryManager::the()

class MemoryManager {
public:
static MemoryManager& the() PURE;
Expand All @@ -50,6 +52,11 @@ class MemoryManager {
// HACK: don't use this jeez :(
byte* quickMapOnePage(PhysicalAddress);

bool mapSubregion(Task&, Task::Subregion&);
bool unmapSubregion(Task&, Task::Subregion&);
bool mapSubregionsForTask(Task&);
bool unmapSubregionsForTask(Task&);

bool mapRegion(Task&, Task::Region&);
bool unmapRegion(Task&, Task::Region&);
bool mapRegionsForTask(Task&);
Expand All @@ -63,6 +70,8 @@ class MemoryManager {
void flushEntireTLB();
void flushTLB(LinearAddress);

void* allocatePageTable();

void protectMap(LinearAddress, size_t length);
void identityMap(LinearAddress, size_t length);

Expand Down
8 changes: 4 additions & 4 deletions Kernel/ProcFileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ ByteBuffer procfs$pid_stack(Task& task)
{
InterruptDisabler disabler;
if (current != &task) {
MemoryManager::the().unmapRegionsForTask(*current);
MemoryManager::the().mapRegionsForTask(task);
MM.unmapRegionsForTask(*current);
MM.mapRegionsForTask(task);
}
struct RecognizedSymbol {
dword address;
Expand Down Expand Up @@ -78,8 +78,8 @@ ByteBuffer procfs$pid_stack(Task& task)
}
buffer.trim(bufptr - (char*)buffer.pointer());
if (current != &task) {
MemoryManager::the().unmapRegionsForTask(task);
MemoryManager::the().mapRegionsForTask(*current);
MM.unmapRegionsForTask(task);
MM.mapRegionsForTask(*current);
}
return buffer;
}
Expand Down
Loading

0 comments on commit 9a71c77

Please sign in to comment.