From 7059bacfce1abf11d93c89ac55c85ebd17abda7a Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Mon, 12 Oct 2020 00:59:50 -0400 Subject: [PATCH] Improved ProgramInfo XML generation. Also added some additional fields. Thanks to 0Liam. --- source/program_info.c | 385 +++++++++++++++++++++++++++++++++--------- 1 file changed, 309 insertions(+), 76 deletions(-) diff --git a/source/program_info.c b/source/program_info.c index a6a5df6..8d45266 100644 --- a/source/program_info.c +++ b/source/program_info.c @@ -24,10 +24,18 @@ #include "program_info.h" #include "elf_symbol.h" +/* Global variables. */ + +static const char *g_trueString = "True", *g_falseString = "False"; +static const char *g_nnSdkString = "NintendoSdk_nnSdk"; + /* Function prototypes. */ -static bool programInfoAddNsoMiddlewareListToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const NsoContext *nso_ctx); -static bool programInfoAddNsoSymbolsToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const NsoContext *nso_ctx, bool is_64bit); +static bool programInfoGetSdkVersionAndBuildTypeFromSdkNso(ProgramInfoContext *program_info_ctx, char **sdk_version, char **build_type); +static bool programInfoAddStringFieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const char *tag_name, const char *value); +static bool programInfoAddNsoApiListToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, ProgramInfoContext *program_info_ctx, const char *api_list_tag, const char *api_entry_prefix, \ + const char *sdk_prefix); +static bool programInfoAddNsoSymbolsToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, ProgramInfoContext *program_info_ctx); bool programInfoInitializeContext(ProgramInfoContext *out, NcaContext *nca_ctx) { @@ -136,7 +144,7 @@ bool programInfoGenerateAuthoringToolXml(ProgramInfoContext *program_info_ctx) char *xml_buf = NULL; u64 xml_buf_size = 0; - VersionType2 *sdk_addon_version = &(program_info_ctx->nca_ctx->header.sdk_addon_version); + char *sdk_version = NULL, *build_type = NULL; bool is_64bit = (program_info_ctx->npdm_ctx.meta_header->flags.is_64bit_instruction == 1); u8 *npdm_acid = (u8*)program_info_ctx->npdm_ctx.acid_header; @@ -172,53 +180,71 @@ bool programInfoGenerateAuthoringToolXml(ProgramInfoContext *program_info_ctx) goto end; } + /* Get SDK version and build type strings. */ + if (!programInfoGetSdkVersionAndBuildTypeFromSdkNso(program_info_ctx, &sdk_version, &build_type)) goto end; + if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \ "\n" \ - "\n" \ - " %u_%u_%u\n" \ - " %u\n" \ - " Release\n" /* Default to Release. */ \ + "\n")) goto end; + + /* SdkVersion. */ + if (!programInfoAddStringFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "SdkVersion", sdk_version)) goto end; + + if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \ + " \n" /* Impossible to get. */ \ + " \n" /* Impossible to get. */ \ + " %u\n", \ + is_64bit ? 64 : 32)) goto end; + + /* BuildType. */ + if (!programInfoAddStringFieldToAuthoringToolXml(&xml_buf, &xml_buf_size, "BuildType", build_type)) goto end; + + if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \ + " \n" /* Impossible to get. */ \ " %s\n" \ + " \n" /* Impossible to get. */ \ " \n" \ " %s\n" \ " %s\n" \ - " \n" \ - " \n", \ - sdk_addon_version->major, sdk_addon_version->minor, sdk_addon_version->micro, \ - is_64bit ? 64 : 32, \ + " \n", \ npdm_acid_b64, \ - program_info_ctx->npdm_ctx.acid_header->flags.production ? "true" : "false", \ - program_info_ctx->npdm_ctx.acid_header->flags.unqualified_approval ? "true" : "false")) goto end; + program_info_ctx->npdm_ctx.acid_header->flags.production ? g_trueString : g_falseString, \ + program_info_ctx->npdm_ctx.acid_header->flags.unqualified_approval ? g_trueString : g_falseString)) goto end; + + /* MiddlewareList. */ + if (!programInfoAddNsoApiListToAuthoringToolXml(&xml_buf, &xml_buf_size, program_info_ctx, "Middleware", "Module", "SDK MW")) goto end; + + /* DebugApiList. */ + if (!programInfoAddNsoApiListToAuthoringToolXml(&xml_buf, &xml_buf_size, program_info_ctx, "DebugApi", "Api", "SDK Debug")) goto end; + + /* PrivateApiList. */ + if (!programInfoAddNsoApiListToAuthoringToolXml(&xml_buf, &xml_buf_size, program_info_ctx, "PrivateApi", "Api", "SDK Private")) goto end; + + /* UnresolvedApiList. Add symbols from "main" NSO. */ + if (!programInfoAddNsoSymbolsToAuthoringToolXml(&xml_buf, &xml_buf_size, program_info_ctx)) goto end; + + /* GuidelineList. */ + if (!programInfoAddNsoApiListToAuthoringToolXml(&xml_buf, &xml_buf_size, program_info_ctx, "GuidelineApi", "Api", "SDK Guideline")) goto end; + + - /* Add Middleware info. */ - for(u32 i = 0; i < program_info_ctx->nso_count; i++) - { - if (!programInfoAddNsoMiddlewareListToAuthoringToolXml(&xml_buf, &xml_buf_size, &(program_info_ctx->nso_ctx[i]))) goto end; - } - if (!utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \ - " \n" \ - " \n" /* Fill this? */ \ - " \n" /* Fill this? */ \ - " \n")) goto end; - /* Add symbols from main NSO. */ - for(u32 i = 0; i < program_info_ctx->nso_count; i++) - { - /* Only proceed if we're dealing with the main NSO. */ - NsoContext *nso_ctx = &(program_info_ctx->nso_ctx[i]); - if (!nso_ctx->nso_filename || strlen(nso_ctx->nso_filename) != 4 || strcmp(nso_ctx->nso_filename, "main") != 0) continue; - if (!programInfoAddNsoSymbolsToAuthoringToolXml(&xml_buf, &xml_buf_size, nso_ctx, is_64bit)) goto end; - break; - } - /* To do: add GuidelineApi. */ if (!(success = utilsAppendFormattedStringToBuffer(&xml_buf, &xml_buf_size, \ - " \n" \ - " \n" /* Fill this? */ \ + " \n" \ + " \n" /* Impossible to get. */ \ ""))) goto end; + + + + + + + + /* Update ProgramInfo context. */ program_info_ctx->authoring_tool_xml = xml_buf; program_info_ctx->authoring_tool_xml_size = strlen(xml_buf); @@ -226,6 +252,10 @@ bool programInfoGenerateAuthoringToolXml(ProgramInfoContext *program_info_ctx) end: if (npdm_acid_b64) free(npdm_acid_b64); + if (build_type) free(build_type); + + if (sdk_version) free(sdk_version); + if (!success) { if (xml_buf) free(xml_buf); @@ -235,66 +265,263 @@ end: return success; } -static bool programInfoAddNsoMiddlewareListToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const NsoContext *nso_ctx) +static bool programInfoGetSdkVersionAndBuildTypeFromSdkNso(ProgramInfoContext *program_info_ctx, char **sdk_version, char **build_type) { - if (!xml_buf || !xml_buf_size || !nso_ctx || !nso_ctx->nso_filename) + if (!program_info_ctx || !program_info_ctx->nso_count || !program_info_ctx->nso_ctx || !sdk_version || !build_type) { LOGFILE("Invalid parameters!"); return false; } - /* Check if this NSO holds an .api_info section. */ - /* If not, then just return right away. */ - if (!nso_ctx->rodata_api_info_section || !nso_ctx->rodata_api_info_section_size) return true; + NsoContext *nso_ctx = NULL; + char *sdk_entry = NULL, *sdk_entry_vender = NULL, *sdk_entry_name = NULL, *sdk_entry_version = NULL, *sdk_entry_build_type = NULL; + size_t sdk_entry_version_len = 0; + bool success = true; - /* Look for SDK Middlewares in the .api_info section from this NSO's .rodata segment. */ + /* Locate "sdk" NSO. */ + for(u32 i = 0; i < program_info_ctx->nso_count; i++) + { + nso_ctx = &(program_info_ctx->nso_ctx[i]); + if (nso_ctx->nso_filename && strlen(nso_ctx->nso_filename) == 3 && !strcmp(nso_ctx->nso_filename, "sdk") && nso_ctx->rodata_api_info_section && nso_ctx->rodata_api_info_section_size) break; + nso_ctx = NULL; + } + + /* Check if we found the "sdk" NSO. */ + if (!nso_ctx) goto end; + + /* Look for the "nnSdk" entry in .api_info section. */ for(u64 i = 0; i < nso_ctx->rodata_api_info_section_size; i++) { - if ((nso_ctx->rodata_api_info_section_size - i) <= 7) break; - - char *sdk_mw = (nso_ctx->rodata_api_info_section + i); - if (strncmp(sdk_mw, "SDK MW+", 7) != 0) continue; - - /* Found a match. Let's retrieve pointers to the middleware vender and name. */ - char *sdk_mw_vender = (sdk_mw + 7); - char *sdk_mw_name = (strchr(sdk_mw_vender, '+') + 1); - - /* Filter nnSdk entries. */ - if (!strncasecmp(sdk_mw_name, "NintendoSdk_nnSdk", 17)) + sdk_entry = (nso_ctx->rodata_api_info_section + i); + if (strncmp(sdk_entry, "SDK ", 4) != 0) { - i += strlen(sdk_mw); + i += strlen(sdk_entry); + sdk_entry = NULL; continue; } - if (!utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, \ - " \n" \ - " %s\n" \ - " %.*s\n" \ - " %s\n" \ - " \n", \ - sdk_mw_name, \ - (int)(sdk_mw_name - sdk_mw_vender - 1), sdk_mw_vender, \ - nso_ctx->nso_filename)) return false; + sdk_entry_vender = (strchr(sdk_entry, '+') + 1); + sdk_entry_name = (strchr(sdk_entry_vender, '+') + 1); - /* Update counter. */ - i += strlen(sdk_mw); + /* Check if we're dealing with a "nnSdk" entry. */ + if (strncmp(sdk_entry_name, g_nnSdkString, strlen(g_nnSdkString)) != 0) + { + i += strlen(sdk_entry); + sdk_entry = sdk_entry_vender = sdk_entry_name = NULL; + continue; + } + + /* Jackpot. */ + break; } - return true; + /* Bail out if we couldn't find the "nnSdk" entry. */ + if (!sdk_entry) goto end; + + /* Get the SDK version and build type. */ + sdk_entry_version = (strchr(sdk_entry_name, '-') + 1); + sdk_entry_build_type = (strchr(sdk_entry_version, '-') + 1); + sdk_entry_version_len = (sdk_entry_build_type - sdk_entry_version - 1); + + /* Duplicate strings. */ + if (!(*sdk_version = strndup(sdk_entry_version, sdk_entry_version_len)) || !(*build_type = strdup(sdk_entry_build_type))) + { + LOGFILE("Failed to allocate memory for output strings!"); + success = false; + } + +end: + if (success) + { + /* Set output pointers to NULL if we have no usable nnSdk information. */ + if (!nso_ctx || !sdk_entry) *sdk_version = *build_type = NULL; + } else { + if (*sdk_version) + { + free(*sdk_version); + *sdk_version = NULL; + } + + if (*build_type) + { + free(*build_type); + *build_type = NULL; + } + } + + return success; } -static bool programInfoAddNsoSymbolsToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const NsoContext *nso_ctx, bool is_64bit) +static bool programInfoAddStringFieldToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, const char *tag_name, const char *value) { - if (!xml_buf || !xml_buf_size || !nso_ctx || !nso_ctx->nso_filename || !nso_ctx->rodata_dynstr_section || !nso_ctx->rodata_dynstr_section_size || !nso_ctx->rodata_dynsym_section || \ - !nso_ctx->rodata_dynsym_section_size) + if (!xml_buf || !xml_buf_size || !tag_name || !strlen(tag_name)) { LOGFILE("Invalid parameters!"); return false; } - u64 symbol_size = (!is_64bit ? sizeof(Elf32Symbol) : sizeof(Elf64Symbol)); + return ((value && strlen(value)) ? utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <%s>%s\n", tag_name, value, tag_name) : \ + utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <%s />\n", tag_name)); +} + +static bool programInfoAddNsoApiListToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, ProgramInfoContext *program_info_ctx, const char *api_list_tag, const char *api_entry_prefix, \ + const char *sdk_prefix) +{ + size_t sdk_prefix_len = 0; + bool success = false, api_list_exists = false; - /* Parse ELF dynamic symbol table to retrieve the right symbol strings. */ + if (!xml_buf || !xml_buf_size || !program_info_ctx || !program_info_ctx->nso_count || !program_info_ctx->nso_ctx || !api_list_tag || !strlen(api_list_tag) || !api_entry_prefix || \ + !strlen(api_entry_prefix) || !sdk_prefix || !(sdk_prefix_len = strlen(sdk_prefix))) + { + LOGFILE("Invalid parameters!"); + return false; + } + + /* Check if any entries for this API list exist. */ + for(u32 i = 0; i < program_info_ctx->nso_count; i++) + { + NsoContext *nso_ctx = &(program_info_ctx->nso_ctx[i]); + if (!nso_ctx->nso_filename || !strlen(nso_ctx->nso_filename) || !nso_ctx->rodata_api_info_section || !nso_ctx->rodata_api_info_section_size) continue; + + for(u64 j = 0; j < nso_ctx->rodata_api_info_section_size; j++) + { + char *sdk_entry = (nso_ctx->rodata_api_info_section + j); + if (strncmp(sdk_entry, sdk_prefix, sdk_prefix_len) != 0) + { + j += strlen(sdk_entry); + continue; + } + + char *sdk_entry_vender = (strchr(sdk_entry, '+') + 1); + char *sdk_entry_name = (strchr(sdk_entry_vender, '+') + 1); + + /* Filter "nnSdk" entries. */ + if (!strncmp(sdk_entry_name, g_nnSdkString, strlen(g_nnSdkString))) + { + j += strlen(sdk_entry); + continue; + } + + /* Jackpot. */ + api_list_exists = true; + break; + } + + if (api_list_exists) break; + } + + /* Append an empty XML element if no entries for this API list exist. */ + if (!api_list_exists) + { + success = utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <%sList />\n", api_list_tag); + goto end; + } + + if (!utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " <%sList>\n", api_list_tag)) goto end; + + /* Retrieve full API list. */ + for(u32 i = 0; i < program_info_ctx->nso_count; i++) + { + NsoContext *nso_ctx = &(program_info_ctx->nso_ctx[i]); + if (!nso_ctx->nso_filename || !strlen(nso_ctx->nso_filename) || !nso_ctx->rodata_api_info_section || !nso_ctx->rodata_api_info_section_size) continue; + + for(u64 j = 0; j < nso_ctx->rodata_api_info_section_size; j++) + { + char *sdk_entry = (nso_ctx->rodata_api_info_section + j); + if (strncmp(sdk_entry, sdk_prefix, sdk_prefix_len) != 0) + { + j += strlen(sdk_entry); + continue; + } + + char *sdk_entry_vender = (strchr(sdk_entry, '+') + 1); + char *sdk_entry_name = (strchr(sdk_entry_vender, '+') + 1); + + /* Filter "nnSdk" entries. */ + if (!strncmp(sdk_entry_name, g_nnSdkString, strlen(g_nnSdkString))) + { + j += strlen(sdk_entry); + continue; + } + + if (!utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, \ + " <%s>\n" \ + " <%sName>%s\n" \ + " %.*s\n" \ + " %s\n" \ + " \n", \ + api_list_tag, \ + api_entry_prefix, sdk_entry_name, api_entry_prefix, \ + (int)(sdk_entry_name - sdk_entry_vender - 1), sdk_entry_vender, \ + nso_ctx->nso_filename, \ + api_list_tag)) goto end; + + /* Update counter. */ + j += strlen(sdk_entry); + } + } + + success = utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " \n", api_list_tag); + +end: + return success; +} + +static bool programInfoAddNsoSymbolsToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, ProgramInfoContext *program_info_ctx) +{ + if (!xml_buf || !xml_buf_size || !program_info_ctx || !program_info_ctx->npdm_ctx.meta_header || !program_info_ctx->nso_count || !program_info_ctx->nso_ctx) + { + LOGFILE("Invalid parameters!"); + return false; + } + + NsoContext *nso_ctx = NULL; + bool is_64bit = (program_info_ctx->npdm_ctx.meta_header->flags.is_64bit_instruction == 1); + u64 symbol_size = (!is_64bit ? sizeof(Elf32Symbol) : sizeof(Elf64Symbol)); + bool success = false, symbols_exist = false; + + /* Locate "main" NSO. */ + for(u32 i = 0; i < program_info_ctx->nso_count; i++) + { + nso_ctx = &(program_info_ctx->nso_ctx[i]); + if (nso_ctx->nso_filename && strlen(nso_ctx->nso_filename) == 4 && !strcmp(nso_ctx->nso_filename, "main") && nso_ctx->rodata_dynstr_section && nso_ctx->rodata_dynstr_section_size && \ + nso_ctx->rodata_dynsym_section && nso_ctx->rodata_dynsym_section_size) break; + nso_ctx = NULL; + } + + /* Check if we found the "main" NSO. */ + if (!nso_ctx) goto end; + + /* Check if any symbols matching the required filters exist. */ + for(u64 i = 0; i < nso_ctx->rodata_dynsym_section_size; i += symbol_size) + { + if ((nso_ctx->rodata_dynsym_section_size - i) < symbol_size) break; + + u8 st_type = 0; + + /* To do: change ELF symbol filters? */ + if (!is_64bit) + { + /* Parse 32-bit ELF symbol. */ + Elf32Symbol *elf32_symbol = (Elf32Symbol*)(nso_ctx->rodata_dynsym_section + i); + st_type = ELF_ST_TYPE(elf32_symbol->st_info); + symbols_exist = (elf32_symbol->st_name < nso_ctx->rodata_dynstr_section_size && (st_type == STT_NOTYPE || st_type == STT_FUNC) && elf32_symbol->st_shndx == SHN_UNDEF); + } else { + /* Parse 64-bit ELF symbol. */ + Elf64Symbol *elf64_symbol = (Elf64Symbol*)(nso_ctx->rodata_dynsym_section + i); + st_type = ELF_ST_TYPE(elf64_symbol->st_info); + symbols_exist = (elf64_symbol->st_name < nso_ctx->rodata_dynstr_section_size && (st_type == STT_NOTYPE || st_type == STT_FUNC) && elf64_symbol->st_shndx == SHN_UNDEF); + } + + if (symbols_exist) break; + } + + /* Bail out if we couldn't find any valid symbols. */ + if (!symbols_exist) goto end; + + if (!utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " \n")) goto end; + + /* Parse ELF dynamic symbol table to retrieve the symbol strings. */ for(u64 i = 0; i < nso_ctx->rodata_dynsym_section_size; i += symbol_size) { if ((nso_ctx->rodata_dynsym_section_size - i) < symbol_size) break; @@ -309,15 +536,15 @@ static bool programInfoAddNsoSymbolsToAuthoringToolXml(char **xml_buf, u64 *xml_ Elf32Symbol *elf32_symbol = (Elf32Symbol*)(nso_ctx->rodata_dynsym_section + i); st_type = ELF_ST_TYPE(elf32_symbol->st_info); - symbol_str = ((elf32_symbol->st_name < nso_ctx->rodata_dynstr_section_size && !elf32_symbol->st_value && (st_type == STT_NOTYPE || st_type == STT_FUNC) && \ - elf32_symbol->st_shndx == SHN_UNDEF) ? (nso_ctx->rodata_dynstr_section + elf32_symbol->st_name) : NULL); + symbol_str = ((elf32_symbol->st_name < nso_ctx->rodata_dynstr_section_size && (st_type == STT_NOTYPE || st_type == STT_FUNC) && elf32_symbol->st_shndx == SHN_UNDEF) ? \ + (nso_ctx->rodata_dynstr_section + elf32_symbol->st_name) : NULL); } else { /* Parse 64-bit ELF symbol. */ Elf64Symbol *elf64_symbol = (Elf64Symbol*)(nso_ctx->rodata_dynsym_section + i); st_type = ELF_ST_TYPE(elf64_symbol->st_info); - symbol_str = ((elf64_symbol->st_name < nso_ctx->rodata_dynstr_section_size && !elf64_symbol->st_value && (st_type == STT_NOTYPE || st_type == STT_FUNC) && \ - elf64_symbol->st_shndx == SHN_UNDEF) ? (nso_ctx->rodata_dynstr_section + elf64_symbol->st_name) : NULL); + symbol_str = ((elf64_symbol->st_name < nso_ctx->rodata_dynstr_section_size && (st_type == STT_NOTYPE || st_type == STT_FUNC) && elf64_symbol->st_shndx == SHN_UNDEF) ? \ + (nso_ctx->rodata_dynstr_section + elf64_symbol->st_name) : NULL); } if (!symbol_str) continue; @@ -328,8 +555,14 @@ static bool programInfoAddNsoSymbolsToAuthoringToolXml(char **xml_buf, u64 *xml_ " %s\n" \ " \n", \ symbol_str, \ - nso_ctx->nso_filename)) return false; + nso_ctx->nso_filename)) goto end; } - return true; + success = utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " \n"); + +end: + /* Append an empty XML element if no valid symbols exist. */ + if (!success && (!nso_ctx || !symbols_exist)) success = utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " \n"); + + return success; }