aboutsummaryrefslogtreecommitdiff
path: root/backends/plugins/elf
diff options
context:
space:
mode:
authorBastien Bouclet2019-12-01 17:19:50 +0100
committerBastien Bouclet2019-12-01 17:19:50 +0100
commit34e835a20ca648b0fd2e67cefc84511c1dab6217 (patch)
tree97ba9b239a8fd9c9d8bba925b21262314de2f2c2 /backends/plugins/elf
parenta51c23abd3a667811b01e90b0c8c53505021950a (diff)
downloadscummvm-rg350-34e835a20ca648b0fd2e67cefc84511c1dab6217.tar.gz
scummvm-rg350-34e835a20ca648b0fd2e67cefc84511c1dab6217.tar.bz2
scummvm-rg350-34e835a20ca648b0fd2e67cefc84511c1dab6217.zip
3DS: Implement dynamic plugins
Allows a full build to run on old generation devices
Diffstat (limited to 'backends/plugins/elf')
-rw-r--r--backends/plugins/elf/arm-loader.cpp34
-rw-r--r--backends/plugins/elf/elf-loader.cpp32
-rw-r--r--backends/plugins/elf/elf-loader.h4
-rw-r--r--backends/plugins/elf/mips-loader.cpp2
4 files changed, 58 insertions, 14 deletions
diff --git a/backends/plugins/elf/arm-loader.cpp b/backends/plugins/elf/arm-loader.cpp
index 3aabf44452..3bbb5b2ee5 100644
--- a/backends/plugins/elf/arm-loader.cpp
+++ b/backends/plugins/elf/arm-loader.cpp
@@ -65,6 +65,7 @@ bool ARMDLObject::relocate(Elf32_Off offset, Elf32_Word size, byte *relSegment)
// Act differently based on the type of relocation
switch (REL_TYPE(rel[i].r_info)) {
case R_ARM_ABS32:
+ case R_ARM_TARGET1:
if (sym->st_shndx < SHN_LOPROC) { // Only shift for plugin section.
a = *target; // Get full 32 bits of addend
relocation = a + Elf32_Addr(_segment); // Shift by main offset
@@ -80,13 +81,36 @@ bool ARMDLObject::relocate(Elf32_Off offset, Elf32_Word size, byte *relSegment)
break;
case R_ARM_CALL:
- debug(8, "elfloader: R_ARM_CALL: PC-relative jump, ld takes care of necessary relocation work for us.");
- break;
-
case R_ARM_JUMP24:
- debug(8, "elfloader: R_ARM_JUMP24: PC-relative jump, ld takes care of all relocation work for us.");
- break;
+ if (sym->st_shndx == SHN_ABS) { // Absolute symbols used in PC-relative instructions need to be relocated
+ // Extract the PC offset from the instruction
+ int32 pcOffset = *target;
+ pcOffset = (pcOffset & 0x00ffffff) << 2;
+
+ if (pcOffset & 0x02000000)
+ pcOffset -= 0x04000000;
+
+ // Shift by the segment offset
+ pcOffset -= Elf32_Addr(_segment);
+ // Check the relocated offset is valid for the instruction
+ if (pcOffset <= (int32)0xfe000000 ||
+ pcOffset >= (int32)0x02000000) {
+ warning("elfloader: R_ARM_CALL/R_ARM_JUMP24: Out of range relocation i=%d, target=%p, pcOffset=%d", i, target, pcOffset);
+ free(rel);
+ return false;
+ }
+
+ // Put the relocated offset back in the instruction
+ pcOffset >>= 2;
+ pcOffset &= 0x00ffffff;
+
+ *target &= (uint32)0xff000000;
+ *target |= (uint32)pcOffset;
+
+ debug(8, "elfloader: R_ARM_CALL/R_ARM_JUMP24: i=%d, origTarget=%x, target=%x", i, origTarget, *target);
+ }
+ break;
case R_ARM_V4BX:
debug(8, "elfloader: R_ARM_V4BX: No relocation calculation necessary.");
break;
diff --git a/backends/plugins/elf/elf-loader.cpp b/backends/plugins/elf/elf-loader.cpp
index 5198fa8088..39f1f7dfa5 100644
--- a/backends/plugins/elf/elf-loader.cpp
+++ b/backends/plugins/elf/elf-loader.cpp
@@ -50,8 +50,7 @@ DLObject::DLObject() :
DLObject::~DLObject() {
discardSymtab();
- ELFMemMan.pluginDeallocate(_segment);
- _segment = 0;
+ discardSegment();
}
// Expel the symbol table from memory
@@ -65,11 +64,12 @@ void DLObject::discardSymtab() {
_symbol_cnt = 0;
}
-// Unload all objects from memory
-void DLObject::unload() {
- discardSymtab();
-
- ELFMemMan.pluginDeallocate(_segment);
+void DLObject::discardSegment() {
+ if (_segment) {
+ // Restore default protection before returning memory
+ protectMemory(_segment, _segmentSize, PF_R | PF_W);
+ deallocateMemory(_segment, _segmentSize);
+ }
_segment = 0;
_segmentSize = 0;
@@ -77,6 +77,12 @@ void DLObject::unload() {
_segmentVMA = 0;
}
+// Unload all objects from memory
+void DLObject::unload() {
+ discardSymtab();
+ discardSegment();
+}
+
bool DLObject::readElfHeader(Elf32_Ehdr *ehdr) {
assert(_file);
@@ -163,7 +169,7 @@ bool DLObject::readProgramHeaders(Elf32_Ehdr *ehdr, Elf32_Phdr *phdr, Elf32_Half
}
bool DLObject::loadSegment(Elf32_Phdr *phdr) {
- _segment = (byte *)ELFMemMan.pluginAllocate(phdr->p_align, phdr->p_memsz);
+ _segment = (byte *)allocateMemory(phdr->p_align, phdr->p_memsz);
if (!_segment) {
warning("elfloader: Out of memory.");
@@ -402,6 +408,8 @@ bool DLObject::load() {
return false;
}
+ protectMemory(_segment, _segmentSize, phdr.p_flags);
+
return true;
}
@@ -488,4 +496,12 @@ void *DLObject::symbol(const char *name) {
return 0;
}
+void *DLObject::allocateMemory(uint32 align, uint32 size) {
+ return ELFMemMan.pluginAllocate(align, size);
+}
+
+void DLObject::deallocateMemory(void *ptr, uint32 size) {
+ ELFMemMan.pluginDeallocate(ptr);
+}
+
#endif /* defined(DYNAMIC_MODULES) && defined(USE_ELF_LOADER) */
diff --git a/backends/plugins/elf/elf-loader.h b/backends/plugins/elf/elf-loader.h
index 17ca35482a..1730338807 100644
--- a/backends/plugins/elf/elf-loader.h
+++ b/backends/plugins/elf/elf-loader.h
@@ -68,6 +68,7 @@ protected:
int loadSymbolTable(Elf32_Ehdr *ehdr, Elf32_Shdr *shdr);
bool loadStringTable(Elf32_Shdr *shdr);
virtual void relocateSymbols(ptrdiff_t offset);
+ void discardSegment();
// architecture specific
@@ -83,6 +84,9 @@ protected:
// platform specific
virtual void flushDataCache(void *ptr, uint32 len) const = 0;
+ virtual void *allocateMemory(uint32 align, uint32 size);
+ virtual void deallocateMemory(void *ptr, uint32 size);
+ virtual void protectMemory(void *ptr, uint32 len, int prot) const {};
public:
DLObject();
diff --git a/backends/plugins/elf/mips-loader.cpp b/backends/plugins/elf/mips-loader.cpp
index 47ae00a8ea..e1e62234d5 100644
--- a/backends/plugins/elf/mips-loader.cpp
+++ b/backends/plugins/elf/mips-loader.cpp
@@ -279,7 +279,7 @@ bool MIPSDLObject::loadSegment(Elf32_Phdr *phdr) {
// We need to take account of non-allocated segment for shorts
if (phdr->p_flags & PF_X) { // This is a relocated segment
// Attempt to allocate memory for segment
- _segment = (byte *)ELFMemMan.pluginAllocate(phdr->p_align, phdr->p_memsz);
+ _segment = (byte *)allocateMemory(phdr->p_align, phdr->p_memsz);
if (!_segment) {
warning("elfloader: Out of memory.");