Skip to content

Commit

Permalink
Add necessary features for LgyBg and Process9 development (#151)
Browse files Browse the repository at this point in the history
* [makerom] Add -baremetal flag

This is a workaround to us not supporting multiple phdr per "segment".

K9 and legacy FIRM k11 don't have memory management and have everything
be RWX. We can exploit how they load CIPs to have the segment be
anything, and be able to be loaded at any VMA (first segment defines LMA
and entrypoint, however). With this flag, the first phdr in the .elf
file is always chosen as the "code" segment and so on.

For example, we can have a FCRAM segment where the image is initially
loaded and jumped to, as the first segment, then an AXIWRAM segment. For
TwlBg, Nintendo seems to have a RWX phdr inside the code "segment" that
it relocates to AXIWRAM, but we don't support this.

"-baremetal" implies "-nocodepadding".

* [makerom] Add -pagesize option

Kernel9 uses pagesize=0x100 instead of the usual 0x1000.

* [makerom] Remove 32 interrupt ID limitation
  • Loading branch information
TuxSH authored Jul 20, 2024
1 parent c0488dc commit 681d17d
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 107 deletions.
84 changes: 52 additions & 32 deletions makerom/src/code.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@

#include <blz.h>

const u32 CTR_PAGE_SIZE = 0x1000;
const u32 DEFAULT_STACK_SIZE = 0x4000; // 10KB
static const u32 DEFAULT_STACK_SIZE = 0x4000; // 10KB

typedef struct code_segment
{
Expand All @@ -18,14 +17,14 @@ typedef struct code_segment
const u8 *data;
} code_segment;

u32 SizeToPage(u32 memorySize)
u32 SizeToPage(u32 memorySize, u32 pageSize)
{
return align(memorySize, CTR_PAGE_SIZE) / CTR_PAGE_SIZE;
return align(memorySize, pageSize) / pageSize;
}

u32 PageToSize(u32 pageNum)
u32 PageToSize(u32 pageNum, u32 pageSize)
{
return pageNum * CTR_PAGE_SIZE;
return pageNum * pageSize;
}

int ImportPlainRegionFromFile(ncch_settings *set)
Expand Down Expand Up @@ -123,10 +122,11 @@ int ImportPlainRegionFromElf(elf_context *elf, ncch_settings *set)
return 0;
}

void CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u64 segment_flags)
void CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u64 segment_flags, u32 pageSize, bool baremetal, u32 baremetalOrder)
{
u32 segmentNum = elf_SegmentNum(elf);
const elf_segment *segments = elf_GetSegments(elf);
u16 foundSegmentId = segmentNum;

/* Initialise struct data */
out->address = 0;
Expand All @@ -135,24 +135,42 @@ void CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u64 segment_f
out->fileSize = 0;
out->data = NULL;

/* Find segment */
for (u16 i = 0; i < segmentNum; i++) {
/* Skip SDK ELF .module_id segment
The last segment should always be data in valid ELFs,
unless this is an SDK ELF with .module_id segment */
if (i == segmentNum-1 && segments[i].flags == PF_RODATA)
continue;

/* Found segment */
if ((segments[i].flags & ~PF_CTRSDK) == segment_flags && segments[i].type == PT_LOAD) {
out->address = segments[i].vAddr;
out->memSize = segments[i].memSize;
out->fileSize = segments[i].fileSize;
out->pageNum = SizeToPage(out->fileSize);
out->data = segments[i].ptr;
break;
if (baremetal) {
/* With Kernel9 and with LGY firm K11, everything is RWX with no memory management.
While it is a bit hard to replicate what Nintendo do with their toolchain, we can
abuse how these kernels work and remove meaning from "text segment" etc.
The other option would be to add support for multiple phdr per "section" and allow
for RWX phdrs (and perhaps parse phdr list from .rsf), plus read LMA instead of VMA below,
but that's a lot more work compared to this dirty workaround.
First segment defines entrypoint and LMA for the rest of the image.*/
if (baremetalOrder < segmentNum && (segments[baremetalOrder].flags & segment_flags) == segment_flags && segments[baremetalOrder].type == PT_LOAD)
foundSegmentId = baremetalOrder;
} else {
/* Find segment */
for (u16 i = 0; i < segmentNum; i++) {
/* Skip SDK ELF .module_id segment
The last segment should always be data in valid ELFs,
unless this is an SDK ELF with .module_id segment */
if (i == segmentNum-1 && segments[i].flags == PF_RODATA)
continue;

/* Found segment */
if ((segments[i].flags & ~PF_CTRSDK) == segment_flags && segments[i].type == PT_LOAD) {
foundSegmentId = i;
break;
}
}
}

if (foundSegmentId < segmentNum) {
out->address = segments[foundSegmentId].vAddr;
out->memSize = segments[foundSegmentId].memSize;
out->fileSize = segments[foundSegmentId].fileSize;
out->pageNum = SizeToPage(out->fileSize, pageSize);
out->data = segments[foundSegmentId].ptr;
}
}

int CreateExeFsCode(elf_context *elf, ncch_settings *set)
Expand All @@ -162,28 +180,30 @@ int CreateExeFsCode(elf_context *elf, ncch_settings *set)
code_segment rodata;
code_segment rwdata;

CreateCodeSegmentFromElf(&text, elf, PF_TEXT);
CreateCodeSegmentFromElf(&rodata, elf, PF_RODATA);
CreateCodeSegmentFromElf(&rwdata, elf, PF_DATA);
bool baremetal = set->options.baremetal;
u32 pageSize = set->options.pageSize;
CreateCodeSegmentFromElf(&text, elf, PF_TEXT, pageSize, baremetal, 0);
CreateCodeSegmentFromElf(&rodata, elf, PF_RODATA, pageSize, baremetal, 1);
CreateCodeSegmentFromElf(&rwdata, elf, PF_DATA, pageSize, baremetal, 2);

/* Checking the existence of essential ELF Segments */
if (!text.fileSize) return NOT_FIND_TEXT_SEGMENT;

/* Allocating Buffer for ExeFs Code */
bool noCodePadding = set->options.noCodePadding;
bool noCodePadding = set->options.noCodePadding || baremetal;
u32 size;
if (noCodePadding) {
size = text.fileSize + rodata.fileSize + rwdata.fileSize;
}
else {
size = PageToSize(text.pageNum + rodata.pageNum + rwdata.pageNum);
size = PageToSize(text.pageNum + rodata.pageNum + rwdata.pageNum, pageSize);
}
u8 *code = calloc(1, size);

/* Writing Code into Buffer */
u8 *textPos = code;
u8 *rodataPos = (textPos + (noCodePadding ? text.fileSize : PageToSize(text.pageNum)));
u8 *rwdataPos = (rodataPos + (noCodePadding ? rodata.fileSize : PageToSize(rodata.pageNum)));
u8 *rodataPos = (textPos + (noCodePadding ? text.fileSize : PageToSize(text.pageNum, pageSize)));
u8 *rwdataPos = (rodataPos + (noCodePadding ? rodata.fileSize : PageToSize(rodata.pageNum, pageSize)));
if (text.fileSize) memcpy(textPos, text.data, text.fileSize);
if (rodata.fileSize) memcpy(rodataPos, rodata.data, rodata.fileSize);
if (rwdata.fileSize) memcpy(rwdataPos, rwdata.data, rwdata.fileSize);
Expand Down Expand Up @@ -219,7 +239,7 @@ int CreateExeFsCode(elf_context *elf, ncch_settings *set)
set->codeDetails.rwSize = rwdata.fileSize;

/* Calculating BSS size */
if (rwdata.fileSize) {
if (rwdata.fileSize && !baremetal) {
set->codeDetails.bssSize = rwdata.memSize - rwdata.fileSize;
}
else {
Expand Down Expand Up @@ -313,7 +333,7 @@ int BuildExeFsCode(ncch_settings *set)
default:
fprintf(stderr, "[CODE ERROR] Failed to process ELF file (%d)\n", result);
}

elf_Free(&elf);
free(elfFile);
return result;
Expand Down
Loading

0 comments on commit 681d17d

Please sign in to comment.