
When "load external firms and modules" is enabled, Loader will load the sysmodule from /luma/sysmodule/<titleid>.cxi (all uppercase, and with the N3DS title ID bit if relevant) and skip patching. Note that this is a title ID here, not a process name (unlike what we do for KIPs). While this is aimed at enabling people to easily load replacements for official sysmodules, you can load your own custom sysmodules that don't correspond to anything installed. You can use gdb to do so: set remote exec-file <tid> run
276 lines
9.2 KiB
C
276 lines
9.2 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];
|
|
}
|
|
|
|
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;
|
|
}
|