diff --git a/AutomatedTests/CMakeLists.txt b/AutomatedTests/CMakeLists.txt index 957b25280a..3e670a38a0 100644 --- a/AutomatedTests/CMakeLists.txt +++ b/AutomatedTests/CMakeLists.txt @@ -75,6 +75,10 @@ if(CMAKE_SYSTEM_NAME MATCHES Retro68) LINK_FLAGS "-Wl,-gc-sections -Wl,--mac-segments -Wl,${CMAKE_CURRENT_SOURCE_DIR}/Segments.segmap") add_test(NAME ${TESTCASE_PREFIX}Segments COMMAND ${LAUNCH_APPL} ${LAUNCH_METHOD_FLAG} ${RETRO68_TEST_CONFIG} Segments.bin) + + + test(PCRel32.c PROPERTIES PASS_REGULAR_EXPRESSION "OK") + target_compile_options(PCRel32 PRIVATE -march=68020) endif() test(Exceptions.cc PROPERTIES PASS_REGULAR_EXPRESSION "OK") diff --git a/AutomatedTests/PCRel32.c b/AutomatedTests/PCRel32.c new file mode 100644 index 0000000000..3e040fad4f --- /dev/null +++ b/AutomatedTests/PCRel32.c @@ -0,0 +1,29 @@ +#include "Test.h" +#include + +__attribute__((noinline)) static void* foo(size_t x) +{ + return malloc(x); +} + +int main() +{ + if(*(short*)&foo != 0x60FF) + { + TEST_LOG_NO(); + return 0; + } + + uint32_t offset = *(uint32_t*) ((char*)&foo + 2); + if(((char*)&foo + 2) + offset != (char*)&malloc) + { + TEST_LOG_NO(); + return 0; + } + + char *p = foo(42); + strcpy(p, "OK"); + TestLog(p); + free(p); + return 0; +} diff --git a/AutomatedTests/Test.h b/AutomatedTests/Test.h index ad84002808..05cd052d99 100644 --- a/AutomatedTests/Test.h +++ b/AutomatedTests/Test.h @@ -87,8 +87,8 @@ void TestLog(const char *str); #define TEST_LOG_NO() \ do { \ char no[3]; \ - no[0] = 'O'; \ - no[1] = 'K'; \ + no[0] = 'N'; \ + no[1] = 'O'; \ no[2] = '\0'; \ TEST_LOG_SIZED(no, 2); \ } while(0) diff --git a/Elf2Mac/Reloc.cc b/Elf2Mac/Reloc.cc index 2e30606ac8..8ec241fd05 100644 --- a/Elf2Mac/Reloc.cc +++ b/Elf2Mac/Reloc.cc @@ -44,26 +44,33 @@ std::string SerializeRelocsUncompressed(std::vector relocs) std::string SerializeRelocs(std::vector relocs) { std::ostringstream out; - uint32_t offset = -1; - for(const auto& r : relocs) + for(int relative = 0; relative <= 1; relative++) { - uint32_t delta = r.offset - offset; - offset = r.offset; + uint32_t offset = -1; - uint32_t base = (uint32_t) r.base; - - uint32_t encoded = (delta << 2) | base; - - while(encoded >= 128) + for(const auto& r : relocs) { - byte(out, (encoded & 0x7F) | 0x80); - encoded >>= 7; - } - byte(out, encoded); - } + if(r.relative == (bool)relative) + { + uint32_t delta = r.offset - offset; + offset = r.offset; - byte(out, 0); + uint32_t base = (uint32_t) r.base; + + uint32_t encoded = (delta << 2) | base; + + while(encoded >= 128) + { + byte(out, (encoded & 0x7F) | 0x80); + encoded >>= 7; + } + byte(out, encoded); + } + } + + byte(out, 0); + } return out.str(); } diff --git a/Elf2Mac/Reloc.h b/Elf2Mac/Reloc.h index 9448661655..16cd264456 100644 --- a/Elf2Mac/Reloc.h +++ b/Elf2Mac/Reloc.h @@ -47,11 +47,11 @@ public: class RuntimeReloc { public: - RelocBase base; - uint32_t offset; + RelocBase base = RelocBase::code; + uint32_t offset = 0; + bool relative = false; - RuntimeReloc() : base(RelocBase::code), offset(0) {} - RuntimeReloc(RelocBase b, uint32_t o) : base(b), offset(o) {} + RuntimeReloc(RelocBase b = RelocBase::code, uint32_t o = 0, bool r = false) : base(b), offset(o), relative(r) {} }; std::string SerializeRelocsUncompressed(std::vector relocs); diff --git a/Elf2Mac/Section.cc b/Elf2Mac/Section.cc index c05cf2a915..7cca25598e 100644 --- a/Elf2Mac/Section.cc +++ b/Elf2Mac/Section.cc @@ -104,17 +104,20 @@ std::vector Section::GetRelocations(bool useOffsets) if(sym.sectionKind == SectionKind::undefined) continue; + uint32_t offset = rela.r_offset; + if(useOffsets) + offset -= shdr.sh_addr; + if(GELF_R_TYPE(rela.r_info) == R_68K_32) { - assert(sym.sectionKind != SectionKind::undefined); - - uint32_t offset = rela.r_offset; - if(useOffsets) - offset -= shdr.sh_addr; - - //longword(out, offset | ((int)rela.relocBase << 24)); outRelocs.emplace_back(rela.relocBase, offset); } + + if(GELF_R_TYPE(rela.r_info) == R_68K_PC32 + && sym.st_shndx != idx) + { + outRelocs.emplace_back(rela.relocBase, offset, true); + } } return outRelocs; @@ -154,7 +157,7 @@ void Section::FixRelocs(bool allowDirectCodeRefs) { for(Reloc& rela : relocs) { - if(GELF_R_TYPE(rela.r_info) != R_68K_32) + if(GELF_R_TYPE(rela.r_info) != R_68K_32 && GELF_R_TYPE(rela.r_info) != R_68K_PC32) continue; int symidx = GELF_R_SYM(rela.r_info); @@ -165,6 +168,9 @@ void Section::FixRelocs(bool allowDirectCodeRefs) if(sym.sectionKind == SectionKind::undefined) continue; + if(GELF_R_TYPE(rela.r_info) == R_68K_PC32 && sym.st_shndx == idx) + continue; + RelocBase relocBase; switch(sym.sectionKind) { diff --git a/libretro/relocate.c b/libretro/relocate.c index 6ec38c9e5e..49cd9d5377 100644 --- a/libretro/relocate.c +++ b/libretro/relocate.c @@ -111,31 +111,39 @@ void Retro68ApplyRelocations(uint8_t *base, uint32_t size, void *relocations, ui void Retro68ApplyRelocations(uint8_t *base, uint32_t size, void *relocations, uint32_t displacements[]) { uint8_t *reloc = (uint8_t*) relocations; - uint8_t *addrPtr = base - 1; - while(*reloc) + for(int relative = 0; relative <= 1; relative++) { - // read an uleb128 value - uint32_t val = 0; - uint8_t b; - int i = 0; - do + uint8_t *addrPtr = base - 1; + while(*reloc) { - b = *reloc++; - val |= (b & 0x7F) << i; - i += 7; - } while(b & 0x80); + // read an uleb128 value + uint32_t val = 0; + uint8_t b; + int i = 0; + do + { + b = *reloc++; + val |= (b & 0x7F) << i; + i += 7; + } while(b & 0x80); - // ... which consists of an offset and the displacement base index - // the offset is relative to the previous relocation, or to base-1 - addrPtr += val >> 2; - uint8_t kind = val & 0x3; + // ... which consists of an offset and the displacement base index + // the offset is relative to the previous relocation, or to base-1 + addrPtr += val >> 2; + uint8_t kind = val & 0x3; - assert(addrPtr >= base); - assert(addrPtr <= base + size - 4); + assert(addrPtr >= base); + assert(addrPtr <= base + size - 4); - uint8_t *addr = (uint8_t*) READ_UNALIGNED_LONGWORD(addrPtr); - addr += displacements[kind]; - WRITE_UNALIGNED_LONGWORD(addrPtr, (uint32_t) addr); + uint32_t addr = READ_UNALIGNED_LONGWORD(addrPtr); + addr += displacements[kind]; + if(relative) + addr -= (uint32_t) addrPtr; + + WRITE_UNALIGNED_LONGWORD(addrPtr, addr); + } + + reloc++; } } #endif @@ -237,8 +245,8 @@ void Retro68Relocate() // this crashes with some implementations of the memory manager // if we guess wrong, so let's don't for now. // Therefore, all Retro68-compiled code resources have to be locked, - // or they might get relocated as soon as the global variables are - // relocated below. + // or they might get moved as soon as the global variables are + // allocated below. // TODO: figure out a way to reliably determine the offset from the // start of the resource (to pass it from Elf2Mac, probably). @@ -270,11 +278,9 @@ void Retro68Relocate() } /* - Relocation records consist of 4 bytes each. - The lower three bytes are the offset of the longword being relocated. - the first byte of each longword specifies which segment - the relocation leads to; the corresponding displacements are taken - from the following table: + Relocation records logically consist of + * the offset of the longword being relocated + * the displacement base, specified as an index into the following table: */ long displacements[4] = { displacement, // code @@ -307,22 +313,6 @@ void Retro68Relocate() RealApplyRelocations(base, relocatableSize, reloc, displacements); // We're basically done. - /*// Now check whether we're on 68040 or later and need to flush the cache. - // only do this if SysEnvirons is available. - // if SysEnvirons is not available, that means we're on an old System or ROM - // and likely not using a 68040, so we won't do this - if (hasSysEnvirons) - { - SysEnvRec env; - - env.processor = 0; - SysEnvirons(0, &env); - if(env.processor >= env68040) - { - rState->needFlushCache = true; - FlushCodeCache(); - } - }*/ if(hasFlushCodeCache) FlushCodeCache();