285 lines
9.4 KiB
C
285 lines
9.4 KiB
C
/*
|
|
* This file is part of Luma3DS
|
|
* Copyright (C) 2016-2020 Aurora Wright, TuxSH
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
|
* * Requiring preservation of specified reasonable legal notices or
|
|
* author attributions in that material or in the Appropriate Legal
|
|
* Notices displayed by works containing it.
|
|
* * Prohibiting misrepresentation of the origin of that material,
|
|
* or requiring that modified versions of such material be marked in
|
|
* reasonable ways as different from the original version.
|
|
*/
|
|
|
|
/* This file was entirely written by fincs */
|
|
|
|
#include <3ds.h>
|
|
#include "3dsx.h"
|
|
#include "memory.h"
|
|
|
|
#define Log_PrintP(...) ((void)0)
|
|
|
|
#define MAXRELOCS 512
|
|
static _3DSX_Reloc s_relocBuf[MAXRELOCS];
|
|
u32 ldrArgvBuf[ARGVBUF_SIZE/4];
|
|
|
|
#define SEC_ASSERT(x) do { if (!(x)) { Log_PrintP("Assertion failed: %s", #x); return false; } } while (0)
|
|
|
|
typedef struct
|
|
{
|
|
void* segPtrs[3]; // code, rodata & data
|
|
u32 segAddrs[3];
|
|
u32 segSizes[3];
|
|
} _3DSX_LoadInfo;
|
|
|
|
static inline u32 TranslateAddr(u32 off, _3DSX_LoadInfo* d, u32* offsets)
|
|
{
|
|
if (off < offsets[0])
|
|
return d->segAddrs[0] + off;
|
|
if (off < offsets[1])
|
|
return d->segAddrs[1] + off - offsets[0];
|
|
return d->segAddrs[2] + off - offsets[1];
|
|
}
|
|
|
|
u32 IFile_Read2(IFile *file, void* buffer, u32 size, u32 offset)
|
|
{
|
|
Result res;
|
|
u64 total = 0;
|
|
file->pos = offset;
|
|
res = IFile_Read(file, &total, buffer, size);
|
|
return R_SUCCEEDED(res) ? total : 0;
|
|
}
|
|
|
|
bool Ldr_Get3dsxSize(u32* pSize, IFile *file)
|
|
{
|
|
_3DSX_Header hdr;
|
|
|
|
if (IFile_Read2(file, &hdr, sizeof(hdr), 0) != sizeof(hdr))
|
|
{
|
|
Log_PrintP("Cannot read 3DSX header");
|
|
return false;
|
|
}
|
|
|
|
if (hdr.magic != _3DSX_MAGIC)
|
|
{
|
|
Log_PrintP("Not a valid 3DSX file");
|
|
return false;
|
|
}
|
|
|
|
u32 segSizes[3];
|
|
segSizes[0] = (hdr.codeSegSize+0xFFF) &~ 0xFFF;
|
|
segSizes[1] = (hdr.rodataSegSize+0xFFF) &~ 0xFFF;
|
|
segSizes[2] = (hdr.dataSegSize+0xFFF) &~ 0xFFF;
|
|
SEC_ASSERT(segSizes[0] >= hdr.codeSegSize);
|
|
SEC_ASSERT(segSizes[1] >= hdr.rodataSegSize);
|
|
SEC_ASSERT(segSizes[2] >= hdr.dataSegSize);
|
|
|
|
// TODO: Check int overflow
|
|
*pSize = segSizes[0] + segSizes[1] + segSizes[2] + 0x1000; // One extra page reserved for settings/etc
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline u32 min(u32 a, u32 b)
|
|
{
|
|
return a < b ? a : b;
|
|
}
|
|
|
|
Handle Ldr_CodesetFrom3dsx(const char* name, u32* codePages, u32 baseAddr, IFile *file, u64 tid)
|
|
{
|
|
u32 i,j,k,m;
|
|
Result res;
|
|
_3DSX_Header hdr;
|
|
IFile_Read2(file, &hdr, sizeof(hdr), 0);
|
|
|
|
_3DSX_LoadInfo d;
|
|
d.segSizes[0] = (hdr.codeSegSize+0xFFF) &~ 0xFFF;
|
|
d.segSizes[1] = (hdr.rodataSegSize+0xFFF) &~ 0xFFF;
|
|
d.segSizes[2] = (hdr.dataSegSize+0xFFF) &~ 0xFFF;
|
|
d.segPtrs[0] = codePages;
|
|
d.segPtrs[1] = (char*)d.segPtrs[0] + d.segSizes[0];
|
|
d.segPtrs[2] = (char*)d.segPtrs[1] + d.segSizes[1];
|
|
d.segAddrs[0] = baseAddr;
|
|
d.segAddrs[1] = d.segAddrs[0] + d.segSizes[0];
|
|
d.segAddrs[2] = d.segAddrs[1] + d.segSizes[1];
|
|
|
|
u32 offsets[2] = { d.segSizes[0], d.segSizes[0] + d.segSizes[1] };
|
|
u32* segLimit = d.segPtrs[2] + d.segSizes[2];
|
|
|
|
u32 readOffset = hdr.headerSize;
|
|
|
|
u32 nRelocTables = hdr.relocHdrSize/4;
|
|
SEC_ASSERT((3*4*nRelocTables) <= 0x1000);
|
|
u32* extraPage = (u32*)((char*)d.segPtrs[2] + d.segSizes[2]);
|
|
u32 extraPageAddr = d.segAddrs[2] + d.segSizes[2];
|
|
|
|
// Read the relocation headers
|
|
for (i = 0; i < 3; i ++)
|
|
{
|
|
if (IFile_Read2(file, &extraPage[i*nRelocTables], hdr.relocHdrSize, readOffset) != hdr.relocHdrSize)
|
|
{
|
|
Log_PrintP("Cannot read relheader %d", i);
|
|
return 0;
|
|
}
|
|
readOffset += hdr.relocHdrSize;
|
|
}
|
|
|
|
// Read the code segment
|
|
if (IFile_Read2(file, d.segPtrs[0], hdr.codeSegSize, readOffset) != hdr.codeSegSize)
|
|
{
|
|
Log_PrintP("Cannot read code segment");
|
|
return 0;
|
|
}
|
|
readOffset += hdr.codeSegSize;
|
|
|
|
// Read the rodata segment
|
|
if (IFile_Read2(file, d.segPtrs[1], hdr.rodataSegSize, readOffset) != hdr.rodataSegSize)
|
|
{
|
|
Log_PrintP("Cannot read rodata segment");
|
|
return 0;
|
|
}
|
|
readOffset += hdr.rodataSegSize;
|
|
|
|
// Read the data segment
|
|
u32 dataLoadSegSize = hdr.dataSegSize - hdr.bssSize;
|
|
if (IFile_Read2(file, d.segPtrs[2], dataLoadSegSize, readOffset) != dataLoadSegSize)
|
|
{
|
|
Log_PrintP("Cannot read data segment");
|
|
return 0;
|
|
}
|
|
readOffset += dataLoadSegSize;
|
|
|
|
// Relocate the segments
|
|
for (i = 0; i < 3; i ++)
|
|
{
|
|
for (j = 0; j < nRelocTables; j ++)
|
|
{
|
|
int nRelocs = extraPage[i*nRelocTables + j];
|
|
if (j >= (sizeof(_3DSX_RelocHdr)/4))
|
|
{
|
|
// Not using this header
|
|
readOffset += nRelocs;
|
|
continue;
|
|
}
|
|
|
|
u32* pos = (u32*)d.segPtrs[i];
|
|
u32* endPos = pos + (d.segSizes[i]/4);
|
|
SEC_ASSERT(endPos <= segLimit);
|
|
|
|
while (nRelocs)
|
|
{
|
|
u32 toDo = nRelocs > MAXRELOCS ? MAXRELOCS : nRelocs;
|
|
nRelocs -= toDo;
|
|
|
|
u32 readSize = toDo*sizeof(_3DSX_Reloc);
|
|
if (IFile_Read2(file, s_relocBuf, readSize, readOffset) != readSize)
|
|
{
|
|
Log_PrintP("Cannot read reloc table (%d,%d)", i, j);
|
|
return 0;
|
|
}
|
|
readOffset += readSize;
|
|
|
|
for (k = 0; k < toDo && pos < endPos; k ++)
|
|
{
|
|
pos += s_relocBuf[k].skip;
|
|
u32 nPatches = s_relocBuf[k].patch;
|
|
for (m = 0; m < nPatches && pos < endPos; m ++)
|
|
{
|
|
u32 inAddr = baseAddr + 4*(pos - codePages);
|
|
u32 origData = *pos;
|
|
u32 subType = origData >> (32-4);
|
|
u32 addr = TranslateAddr(origData &~ 0xF0000000, &d, offsets);
|
|
//Log_PrintP("%08lX<-%08lX", inAddr, addr);
|
|
switch (j)
|
|
{
|
|
case 0:
|
|
{
|
|
if (subType != 0)
|
|
{
|
|
Log_PrintP("Unsupported absolute reloc subtype (%lu)", subType);
|
|
return 0;
|
|
}
|
|
*pos = addr;
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
u32 data = addr - inAddr;
|
|
switch (subType)
|
|
{
|
|
case 0: *pos = data; break; // 32-bit signed offset
|
|
case 1: *pos = data &~ BIT(31); break; // 31-bit signed offset
|
|
default:
|
|
Log_PrintP("Unsupported relative reloc subtype (%lu)", subType);
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
pos++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Detect and fill _prm structure
|
|
PrmStruct* pst = (PrmStruct*) &codePages[1];
|
|
if (pst->magic == _PRM_MAGIC)
|
|
{
|
|
memset(extraPage, 0, 0x1000);
|
|
memcpy(extraPage, ldrArgvBuf, sizeof(ldrArgvBuf));
|
|
pst->pSrvOverride = extraPageAddr + 0xFFC;
|
|
pst->pArgList = extraPageAddr;
|
|
pst->runFlags |= RUNFLAG_APTCHAINLOAD;
|
|
s64 dummy;
|
|
bool isN3DS = svcGetSystemInfo(&dummy, 0x10001, 0) == 0;
|
|
if (isN3DS)
|
|
{
|
|
pst->heapSize = 48*1024*1024;
|
|
pst->linearHeapSize = 64*1024*1024;
|
|
} else
|
|
{
|
|
pst->heapSize = 24*1024*1024;
|
|
pst->linearHeapSize = 32*1024*1024;
|
|
}
|
|
}
|
|
|
|
// Create the codeset
|
|
CodeSetInfo csinfo;
|
|
memset(&csinfo, 0, sizeof(csinfo));
|
|
memcpy(csinfo.name, name, 8);
|
|
csinfo.program_id = tid;
|
|
csinfo.text_addr = d.segAddrs[0];
|
|
csinfo.text_size = d.segSizes[0] >> 12;
|
|
csinfo.ro_addr = d.segAddrs[1];
|
|
csinfo.ro_size = d.segSizes[1] >> 12;
|
|
csinfo.rw_addr = d.segAddrs[2];
|
|
csinfo.rw_size = (d.segSizes[2] >> 12) + 1; // One extra page reserved for settings/etc
|
|
csinfo.text_size_total = csinfo.text_size;
|
|
csinfo.ro_size_total = csinfo.ro_size;
|
|
csinfo.rw_size_total = csinfo.rw_size;
|
|
Handle hCodeset = 0;
|
|
res = svcCreateCodeSet(&hCodeset, &csinfo, d.segPtrs[0], d.segPtrs[1], d.segPtrs[2]);
|
|
if (res)
|
|
{
|
|
Log_PrintP("svcCreateCodeSet: %08lX", res);
|
|
return 0;
|
|
}
|
|
|
|
return hCodeset;
|
|
}
|