Files
Project_CTR/makerom/code.c
T
Yifan Lu cf2ba24d69 Added support for disabling padding between segments for code
Fixed potential bug where code size is calculated by taking the page size of combined segments rather than the combined padded segment size. This could be a problem, for example, if two segments add up to require two pages of padding.
Fixed potential bug where memory-size is used for codeDetails.rwSize; this counts .bss which is not in the file
2016-03-24 17:53:02 -05:00

316 lines
10 KiB
C

#include "lib.h"
#include "elf.h"
#include "blz.h"
#include "ncch_build.h"
#include "exheader_read.h"
#include "code.h"
const u32 CTR_PAGE_SIZE = 0x1000;
const u32 DEFAULT_STACK_SIZE = 0x4000; // 10KB
typedef struct code_segment
{
u32 address;
u32 memSize;
u32 fileSize;
u32 pageNum;
const u8 *data;
} code_segment;
u32 SizeToPage(u32 memorySize)
{
return align(memorySize, CTR_PAGE_SIZE) / CTR_PAGE_SIZE;
}
u32 PageToSize(u32 pageNum)
{
return pageNum * CTR_PAGE_SIZE;
}
int ImportPlainRegionFromFile(ncch_settings *set)
{
set->sections.plainRegion.size = align(set->componentFilePtrs.plainregionSize, set->options.blockSize);
set->sections.plainRegion.buffer = calloc(set->sections.plainRegion.size, 1);
if (!set->sections.plainRegion.buffer) { fprintf(stderr, "[ELF ERROR] Not enough memory\n"); return MEM_ERROR; }
ReadFile64(set->sections.plainRegion.buffer, set->componentFilePtrs.plainregionSize, 0, set->componentFilePtrs.plainregion);
return 0;
}
int ImportExeFsCodeBinaryFromFile(ncch_settings *set)
{
u32 size = set->componentFilePtrs.codeSize;
u8 *buffer = malloc(size);
if (!buffer) {
fprintf(stderr, "[CODE ERROR] Not enough memory\n");
return MEM_ERROR;
}
ReadFile64(buffer, size, 0, set->componentFilePtrs.code);
set->exefsSections.code.size = set->componentFilePtrs.codeSize;
set->exefsSections.code.buffer = malloc(set->exefsSections.code.size);
if (!set->exefsSections.code.buffer) { fprintf(stderr, "[ELF ERROR] Not enough memory\n"); return MEM_ERROR; }
ReadFile64(set->exefsSections.code.buffer, set->exefsSections.code.size, 0, set->componentFilePtrs.code);
if (set->options.CompressCode) {
if (set->options.verbose)
printf("[CODE] Compressing code... ");
u32 new_len;
set->exefsSections.code.buffer = BLZ_Code(buffer, size, &new_len, BLZ_NORMAL);
set->exefsSections.code.size = new_len;
free(buffer);
if (set->options.verbose)
printf("Done!\n");
}
else {
set->exefsSections.code.size = size;
set->exefsSections.code.buffer = buffer;
}
size = set->componentFilePtrs.exhdrSize;
if (size < sizeof(extended_hdr)) {
fprintf(stderr, "[CODE ERROR] Exheader code info template is too small\n");
return FAILED_TO_IMPORT_FILE;
}
extended_hdr *exhdr = malloc(size);
if (!exhdr) {
fprintf(stderr, "[CODE ERROR] Not enough memory\n");
return MEM_ERROR;
}
ReadFile64(exhdr, size, 0, set->componentFilePtrs.exhdr);
/* Setting code_segment data */
set->codeDetails.textAddress = u8_to_u32(exhdr->codeSetInfo.text.address, LE);
set->codeDetails.textMaxPages = u8_to_u32(exhdr->codeSetInfo.text.numMaxPages, LE);
set->codeDetails.textSize = u8_to_u32(exhdr->codeSetInfo.text.codeSize, LE);
set->codeDetails.roAddress = u8_to_u32(exhdr->codeSetInfo.rodata.address, LE);
set->codeDetails.roMaxPages = u8_to_u32(exhdr->codeSetInfo.rodata.numMaxPages, LE);
set->codeDetails.roSize = u8_to_u32(exhdr->codeSetInfo.rodata.codeSize, LE);
set->codeDetails.rwAddress = u8_to_u32(exhdr->codeSetInfo.data.address, LE);
set->codeDetails.rwMaxPages = u8_to_u32(exhdr->codeSetInfo.data.numMaxPages, LE);
set->codeDetails.rwSize = u8_to_u32(exhdr->codeSetInfo.data.codeSize, LE);
set->codeDetails.bssSize = u8_to_u32(exhdr->codeSetInfo.bssSize, LE);
set->codeDetails.stackSize = u8_to_u32(exhdr->codeSetInfo.stackSize, LE);
free(exhdr);
return 0;
}
int ImportPlainRegionFromElf(elf_context *elf, ncch_settings *set)
{
elf_segment segment = elf_GetSegments(elf)[elf_SegmentNum(elf) - 1];
/* Check last segment
If the last segment is RO segment, this must be an SDK .module_id segment */
if (segment.flags != PF_RODATA) {
/* not a RO segment */
return 0;
}
if (segment.fileSize > 0) {
/* Creating Output Buffer */
set->sections.plainRegion.size = align(segment.fileSize, set->options.blockSize);
set->sections.plainRegion.buffer = calloc(set->sections.plainRegion.size, 1);
if (!set->sections.plainRegion.buffer) { fprintf(stderr, "[CODE ERROR] Not enough memory\n"); return MEM_ERROR; }
/* Copy Plain Region */
memcpy(set->sections.plainRegion.buffer, segment.ptr, segment.fileSize);
}
return 0;
}
void CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u64 segment_flags)
{
u32 segmentNum = elf_SegmentNum(elf);
const elf_segment *segments = elf_GetSegments(elf);
/* Initialise struct data */
out->address = 0;
out->memSize = 0;
out->pageNum = 0;
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;
}
}
}
int CreateExeFsCode(elf_context *elf, ncch_settings *set)
{
/* Getting Code Segments */
code_segment text;
code_segment rodata;
code_segment rwdata;
CreateCodeSegmentFromElf(&text, elf, PF_TEXT);
CreateCodeSegmentFromElf(&rodata, elf, PF_RODATA);
CreateCodeSegmentFromElf(&rwdata, elf, PF_DATA);
/* Checking the existence of essential ELF Segments */
if (!text.fileSize) return NOT_FIND_TEXT_SEGMENT;
if (!rwdata.fileSize) return NOT_FIND_DATA_SEGMENT;
/* Calculating BSS size */
set->codeDetails.bssSize = rwdata.memSize - rwdata.fileSize;
/* Allocating Buffer for ExeFs Code */
bool noCodePadding = set->options.noCodePadding;
u32 size;
if (noCodePadding) {
size = text.fileSize + rodata.fileSize + rwdata.fileSize;
}
else {
size = PageToSize(text.pageNum + rodata.pageNum + rwdata.pageNum);
}
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)));
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);
/* Compressing if needed */
if (set->options.CompressCode) {
if (set->options.verbose)
printf("[CODE] Compressing code... ");
u32 new_len;
set->exefsSections.code.buffer = BLZ_Code(code, size, &new_len, BLZ_NORMAL);
set->exefsSections.code.size = new_len;
free(code);
if (set->options.verbose)
printf("Done!\n");
}
else {
set->exefsSections.code.size = size;
set->exefsSections.code.buffer = code;
}
/* Setting code_segment data and freeing original buffers */
set->codeDetails.textAddress = text.address;
set->codeDetails.textMaxPages = text.pageNum;
set->codeDetails.textSize = text.fileSize;
set->codeDetails.roAddress = rodata.address;
set->codeDetails.roMaxPages = rodata.pageNum;
set->codeDetails.roSize = rodata.fileSize;
set->codeDetails.rwAddress = rwdata.address;
set->codeDetails.rwMaxPages = rwdata.pageNum;
set->codeDetails.rwSize = rwdata.fileSize;
if (set->rsfSet->SystemControlInfo.StackSize)
set->codeDetails.stackSize = strtoul(set->rsfSet->SystemControlInfo.StackSize, NULL, 0);
else {
set->codeDetails.stackSize = DEFAULT_STACK_SIZE;
fprintf(stderr, "[CODE WARNING] \"SystemControlInfo/StackSize\" not specified, defaulting to 0x%x bytes\n", DEFAULT_STACK_SIZE);
}
/* Return */
return 0;
}
/*
void PrintElfContext(elf_context *elf)
{
printf("[ELF] Program Table Data\n");
printf(" Offset: 0x%x\n", elf->programTableOffset);
printf(" Size: 0x%x\n", elf->programTableEntrySize);
printf(" Count: 0x%x\n", elf->programTableEntryCount);
printf("[ELF] Section Table Data\n");
printf(" Offset: 0x%x\n", elf->sectionTableOffset);
printf(" Size: 0x%x\n", elf->sectionTableEntrySize);
printf(" Count: 0x%x\n", elf->sectionTableEntryCount);
printf(" Label index: 0x%x\n", elf->sectionHeaderNameEntryIndex);
for (int i = 0; i < elf->activeSegments; i++) {
printf(" Segment [%d][%s]\n", i, elf->segments[i].name);
printf(" > Size(Memory): 0x%x\n", elf->segments[i].header->sizeInMemory);
printf(" > Size(File): 0x%x\n", elf->segments[i].header->sizeInFile);
printf(" > Address: 0x%x\n", elf->segments[i].vAddr);
printf(" > Flags: 0x%x\n", elf->segments[i].header->flags);
printf(" > Type: 0x%x\n", elf->segments[i].header->type);
printf(" > Sections: %d\n", elf->segments[i].sectionNum);
for (int j = 0; j < elf->segments[i].sectionNum; j++)
printf(" > Section [%d][%s][0x%x][0x%x]\n", j, elf->segments[i].sections[j].name, elf->segments[i].sections[j].flags, elf->segments[i].sections[j].type);
}
}
*/
int BuildExeFsCode(ncch_settings *set)
{
int result = 0;
if (set->options.IsCfa)
return result;
if (!set->options.IsBuildingCodeSection) { // Import ExeFs Code from file and return
if (set->componentFilePtrs.plainregion) // Import PlainRegion from file
if ((result = ImportPlainRegionFromFile(set))) return result;
return ImportExeFsCodeBinaryFromFile(set);
}
/* Import ELF */
u8 *elfFile = malloc(set->componentFilePtrs.elfSize);
if (!elfFile) {
fprintf(stderr, "[CODE ERROR] Not enough memory\n");
return MEM_ERROR;
}
ReadFile64(elfFile, set->componentFilePtrs.elfSize, 0, set->componentFilePtrs.elf);
/* Create ELF Context */
elf_context elf;
if ((result = elf_Init(&elf, elfFile))) goto finish;
if ((result = ImportPlainRegionFromElf(&elf, set))) goto finish;
if ((result = CreateExeFsCode(&elf, set))) goto finish;
finish:
switch (result) {
case (0) :
break;
case (NOT_ELF_FILE) :
fprintf(stderr, "[CODE ERROR] Not ELF File\n");
break;
case (NOT_CTR_ARM_ELF) :
fprintf(stderr, "[CODE ERROR] Not CTR ARM ELF\n");
break;
case (NON_EXECUTABLE_ELF) :
fprintf(stderr, "[CODE ERROR] Not Executeable ELF\n");
break;
case (NOT_FIND_TEXT_SEGMENT) :
fprintf(stderr, "[CODE ERROR] Failed to retrieve text sections from ELF\n");
break;
case (NOT_FIND_DATA_SEGMENT) :
fprintf(stderr, "[CODE ERROR] Failed to retrieve data sections from ELF\n");
break;
default:
fprintf(stderr, "[CODE ERROR] Failed to process ELF file (%d)\n", result);
}
elf_Free(&elf);
free(elfFile);
return result;
}