[nx|nstool] Refactor ELF Symbol Processing and NSO/NRO ro metadata processing.

This commit is contained in:
jakcron 2018-06-19 18:37:27 +08:00
parent 605cd3a459
commit c59f209b72
11 changed files with 555 additions and 533 deletions

View file

@ -3,12 +3,11 @@
namespace nx
{
namespace dynsym
namespace elf
{
enum SpecialSectionIndex
{
SHN_UNDEF,
SHN_EXPORT = 1,
SHN_LORESERVE = 0xFF00,
SHN_LOPROC = 0xFF00,
SHN_HIPROC = 0xFF1F,
@ -31,10 +30,21 @@ namespace nx
STT_LOPROC,
STT_HIPROC = 0xF
};
enum SymbolBinding
{
STB_LOCAL,
STB_GLOBAL,
STB_WEAK,
STB_LOOS = 10,
STB_HIOS = 12,
STB_LOPROC,
STB_HIPROC = 0xF
};
}
#pragma pack(push,1)
struct sDynSymbol32Bit
struct sElfSymbol32Bit
{
le_uint32_t name;
le_uint32_t value;
@ -44,7 +54,7 @@ namespace nx
le_uint32_t special_section_index;
};
struct sDynSymbol64Bit
struct sElfSymbol64Bit
{
le_uint32_t name;
byte_t info;

View file

@ -1,70 +0,0 @@
#include "DynamicSymbolParser.h"
DynamicSymbolParser::DynamicSymbolParser()
{
mDynSymbolList.clear();
}
bool DynamicSymbolParser::operator==(const DynamicSymbolParser& other) const
{
return isEqual(other);
}
bool DynamicSymbolParser::operator!=(const DynamicSymbolParser& other) const
{
return !isEqual(other);
}
void DynamicSymbolParser::operator=(const DynamicSymbolParser& other)
{
copyFrom(other);
}
void DynamicSymbolParser::parseData(const byte_t *dyn_sym, size_t dyn_sym_size, const byte_t *dyn_str, size_t dyn_str_size, bool is64Bit)
{
//printf("DynamicSymbolParser::parseData()");
size_t dynSymSize = is64Bit ? sizeof(nx::sDynSymbol64Bit) : sizeof(nx::sDynSymbol32Bit);
sDynSymbol symbol;
for (size_t i = 0; i < dyn_sym_size; i += dynSymSize)
{
//printf("pos %x\n", i);
uint32_t name_pos;
if (is64Bit)
{
name_pos = ((nx::sDynSymbol64Bit*)(dyn_sym + i))->name.get();
symbol.shn_index = (nx::dynsym::SpecialSectionIndex)((nx::sDynSymbol64Bit*)(dyn_sym + i))->special_section_index.get();
symbol.symbol_type = (nx::dynsym::SymbolType)((((nx::sDynSymbol64Bit*)(dyn_sym + i))->info) & nx::dynsym::STT_HIPROC);
}
else
{
name_pos = ((nx::sDynSymbol64Bit*)(dyn_sym + i))->name.get();
symbol.shn_index = (nx::dynsym::SpecialSectionIndex)((nx::sDynSymbol32Bit*)(dyn_sym + i))->special_section_index.get();
symbol.symbol_type = (nx::dynsym::SymbolType)((((nx::sDynSymbol32Bit*)(dyn_sym + i))->info.get()) & nx::dynsym::STT_HIPROC);
}
for (; dyn_str[name_pos] == 0x00 && name_pos < dyn_str_size; name_pos++);
//printf("name_pos = 0x%x\n", name_pos);
symbol.name = std::string((char*)&dyn_str[name_pos]);
mDynSymbolList.addElement(symbol);
}
//printf("DynamicSymbolParser::parseData() end\n");
}
const fnd::List<DynamicSymbolParser::sDynSymbol>& DynamicSymbolParser::getDynamicSymbolList() const
{
return mDynSymbolList;
}
bool DynamicSymbolParser::isEqual(const DynamicSymbolParser& other) const
{
return mDynSymbolList == other.mDynSymbolList;
}
void DynamicSymbolParser::copyFrom(const DynamicSymbolParser& other)
{
mDynSymbolList = other.mDynSymbolList;
}

View file

@ -1,49 +0,0 @@
#pragma once
#include <string>
#include <fnd/List.h>
#include <nx/dynamic_symbol.h>
class DynamicSymbolParser
{
public:
struct sDynSymbol
{
nx::dynsym::SpecialSectionIndex shn_index;
nx::dynsym::SymbolType symbol_type;
std::string name;
void operator=(const sDynSymbol& other)
{
shn_index = other.shn_index;
symbol_type = other.symbol_type;
name = other.name;
}
bool operator==(const sDynSymbol& other) const
{
return (shn_index == other.shn_index && symbol_type == other.symbol_type && name == other.name);
}
bool operator!=(const sDynSymbol& other) const
{
return !(*this == other);
}
};
DynamicSymbolParser();
bool operator==(const DynamicSymbolParser& other) const;
bool operator!=(const DynamicSymbolParser& other) const;
void operator=(const DynamicSymbolParser& other);
void parseData(const byte_t *dyn_sym, size_t dyn_sym_size, const byte_t *dyn_str, size_t dyn_str_size, bool is64Bit);
const fnd::List<sDynSymbol>& getDynamicSymbolList() const;
private:
// data
fnd::List<sDynSymbol> mDynSymbolList;
bool isEqual(const DynamicSymbolParser& other) const;
void copyFrom(const DynamicSymbolParser& other);
};

View file

@ -0,0 +1,72 @@
#include "ElfSymbolParser.h"
ElfSymbolParser::ElfSymbolParser()
{
mSymbolList.clear();
}
bool ElfSymbolParser::operator==(const ElfSymbolParser& other) const
{
return isEqual(other);
}
bool ElfSymbolParser::operator!=(const ElfSymbolParser& other) const
{
return !isEqual(other);
}
void ElfSymbolParser::operator=(const ElfSymbolParser& other)
{
copyFrom(other);
}
void ElfSymbolParser::parseData(const byte_t *dyn_sym, size_t dyn_sym_size, const byte_t *dyn_str, size_t dyn_str_size, bool is64Bit)
{
//printf("ElfSymbolParser::parseData()");
size_t dynSymSize = is64Bit ? sizeof(nx::sElfSymbol64Bit) : sizeof(nx::sElfSymbol32Bit);
sElfSymbol symbol;
for (size_t i = 0; i < dyn_sym_size; i += dynSymSize)
{
//printf("pos %x\n", i);
uint32_t name_pos;
if (is64Bit)
{
name_pos = ((nx::sElfSymbol64Bit*)(dyn_sym + i))->name.get();
symbol.shn_index = (nx::elf::SpecialSectionIndex)((nx::sElfSymbol64Bit*)(dyn_sym + i))->special_section_index.get();
symbol.symbol_type = (nx::elf::SymbolType)((((nx::sElfSymbol64Bit*)(dyn_sym + i))->info) & nx::elf::STT_HIPROC);
symbol.symbol_binding = (nx::elf::SymbolBinding)(((((nx::sElfSymbol64Bit*)(dyn_sym + i))->info) >> 4) & nx::elf::STB_HIPROC);
}
else
{
name_pos = ((nx::sElfSymbol64Bit*)(dyn_sym + i))->name.get();
symbol.shn_index = (nx::elf::SpecialSectionIndex)((nx::sElfSymbol32Bit*)(dyn_sym + i))->special_section_index.get();
symbol.symbol_type = (nx::elf::SymbolType)((((nx::sElfSymbol32Bit*)(dyn_sym + i))->info.get()) & nx::elf::STT_HIPROC);
symbol.symbol_binding = (nx::elf::SymbolBinding)(((((nx::sElfSymbol32Bit*)(dyn_sym + i))->info.get()) >> 4) & nx::elf::STB_HIPROC);
}
for (; dyn_str[name_pos] == 0x00 && name_pos < dyn_str_size; name_pos++);
//printf("name_pos = 0x%x\n", name_pos);
symbol.name = std::string((char*)&dyn_str[name_pos]);
mSymbolList.addElement(symbol);
}
//printf("ElfSymbolParser::parseData() end\n");
}
const fnd::List<ElfSymbolParser::sElfSymbol>& ElfSymbolParser::getSymbolList() const
{
return mSymbolList;
}
bool ElfSymbolParser::isEqual(const ElfSymbolParser& other) const
{
return mSymbolList == other.mSymbolList;
}
void ElfSymbolParser::copyFrom(const ElfSymbolParser& other)
{
mSymbolList = other.mSymbolList;
}

View file

@ -0,0 +1,51 @@
#pragma once
#include <string>
#include <fnd/List.h>
#include <nx/elf.h>
class ElfSymbolParser
{
public:
struct sElfSymbol
{
nx::elf::SpecialSectionIndex shn_index;
nx::elf::SymbolType symbol_type;
nx::elf::SymbolBinding symbol_binding;
std::string name;
void operator=(const sElfSymbol& other)
{
shn_index = other.shn_index;
symbol_type = other.symbol_type;
symbol_binding = other.symbol_binding;
name = other.name;
}
bool operator==(const sElfSymbol& other) const
{
return (shn_index == other.shn_index && symbol_type == other.symbol_type && symbol_binding == other.symbol_binding && name == other.name);
}
bool operator!=(const sElfSymbol& other) const
{
return !(*this == other);
}
};
ElfSymbolParser();
bool operator==(const ElfSymbolParser& other) const;
bool operator!=(const ElfSymbolParser& other) const;
void operator=(const ElfSymbolParser& other);
void parseData(const byte_t *dyn_sym, size_t dyn_sym_size, const byte_t *dyn_str, size_t dyn_str_size, bool is64Bit);
const fnd::List<sElfSymbol>& getSymbolList() const;
private:
// data
fnd::List<sElfSymbol> mSymbolList;
bool isEqual(const ElfSymbolParser& other) const;
void copyFrom(const ElfSymbolParser& other);
};

View file

@ -1,4 +1,3 @@
#include <sstream>
#include <fnd/SimpleTextOutput.h>
#include <fnd/MemoryBlob.h>
#include <compress/lz4.h>
@ -10,10 +9,7 @@ NroProcess::NroProcess():
mFile(nullptr),
mOwnIFile(false),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mVerify(false),
mInstructionType(nx::npdm::INSTR_64BIT),
mListApi(false),
mListSymbols(false)
mVerify(false)
{
}
@ -34,12 +30,12 @@ void NroProcess::process()
importHeader();
importCodeSegments();
importApiList();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
{
displayHeader();
displayRoMetaData();
}
processRoMeta();
if (mIsHomebrewNro)
mAssetProc.process();
}
@ -62,17 +58,17 @@ void NroProcess::setVerifyMode(bool verify)
void NroProcess::setInstructionType(nx::npdm::InstructionType type)
{
mInstructionType = type;
mRoMeta.setInstructionType(type);
}
void NroProcess::setListApi(bool listApi)
{
mListApi = listApi;
mRoMeta.setListApi(listApi);
}
void NroProcess::setListSymbols(bool listSymbols)
{
mListSymbols = listSymbols;
mRoMeta.setListSymbols(listSymbols);
}
void NroProcess::setAssetListFs(bool list)
@ -108,8 +104,8 @@ void NroProcess::importHeader()
mHdr.importBinary(scratch.getBytes(), scratch.getSize());
// setup homebrew extension
nx::sNroHeader* raw_hdr = (nx::sNroHeader*)scratch.getBytes();
if (((le_uint64_t*)raw_hdr->reserved_0)->get() == nx::nro::kNroHomebrewSig && mFile->size() > mHdr.getNroSize())
{
mIsHomebrewNro = true;
@ -119,7 +115,6 @@ void NroProcess::importHeader()
}
else
mIsHomebrewNro = false;
}
void NroProcess::importCodeSegments()
@ -132,38 +127,6 @@ void NroProcess::importCodeSegments()
mFile->read(mDataBlob.getBytes(), mHdr.getDataInfo().memory_offset, mDataBlob.getSize());
}
void NroProcess::importApiList()
{
struct sLayout { size_t offset; size_t size; } api_info, dyn_str, dyn_sym;
api_info.offset = mHdr.getRoEmbeddedInfo().memory_offset;
api_info.size = mHdr.getRoEmbeddedInfo().size;
dyn_str.offset = mHdr.getRoDynStrInfo().memory_offset;
dyn_str.size = mHdr.getRoDynStrInfo().size;
dyn_sym.offset = mHdr.getRoDynSymInfo().memory_offset;
dyn_sym.size = mHdr.getRoDynSymInfo().size;
if (api_info.size > 0)
{
std::stringstream list_stream(std::string((char*)mRoBlob.getBytes() + api_info.offset, api_info.size));
std::string api;
while(std::getline(list_stream, api, (char)0x00))
{
mApiList.push_back(api);
}
}
else
{
mApiList.clear();
}
if (dyn_sym.size > 0)
{
mDynSymbolList.parseData(mRoBlob.getBytes() + dyn_sym.offset, dyn_sym.size, mRoBlob.getBytes() + dyn_str.offset, dyn_str.size, mInstructionType == nx::npdm::INSTR_64BIT);
}
}
void NroProcess::displayHeader()
{
#define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0)
@ -203,125 +166,16 @@ void NroProcess::displayHeader()
#undef _HEXDUMP_L
}
void NroProcess::displayRoMetaData()
void NroProcess::processRoMeta()
{
if (mApiList.size() > 0 && (mListApi || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)))
if (mRoBlob.getSize())
{
printf("[SDK API List]\n");
for (size_t i = 0; i < mApiList.size(); i++)
{
printf(" API %d:\n", (int)i);
printf(" Type: %s\n", getApiTypeStr(mApiList[i].getApiType()));
printf(" Vender: %s\n", mApiList[i].getVenderName().c_str());
printf(" Module: %s\n", mApiList[i].getModuleName().c_str());
}
// setup ro metadata
mRoMeta.setApiInfo(mHdr.getRoEmbeddedInfo().memory_offset, mHdr.getRoEmbeddedInfo().size);
mRoMeta.setDynSym(mHdr.getRoDynSymInfo().memory_offset, mHdr.getRoDynSymInfo().size);
mRoMeta.setDynStr(mHdr.getRoDynStrInfo().memory_offset, mHdr.getRoDynStrInfo().size);
mRoMeta.setRoBinary(mRoBlob);
mRoMeta.setCliOutputMode(mCliOutputMode);
mRoMeta.process();
}
if (mDynSymbolList.getDynamicSymbolList().getSize() > 0 && (mListSymbols || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)))
{
printf("[Symbol List]\n");
for (size_t i = 0; i < mDynSymbolList.getDynamicSymbolList().getSize(); i++)
{
const DynamicSymbolParser::sDynSymbol& symbol = mDynSymbolList.getDynamicSymbolList()[i];
printf(" %s [SHN=%s (%04x)][STT=%s]\n", symbol.name.c_str(), getSectionIndexStr(symbol.shn_index), symbol.shn_index, getSymbolTypeStr(symbol.symbol_type));
}
}
}
const char* NroProcess::getApiTypeStr(SdkApiString::ApiType type) const
{
const char* str;
switch (type)
{
case (SdkApiString::API_MIDDLEWARE):
str = "Middleware";
break;
case (SdkApiString::API_DEBUG):
str = "Debug";
break;
case (SdkApiString::API_PRIVATE):
str = "Private";
break;
case (SdkApiString::API_SDK_VERSION):
str = "SDK Version";
break;
default:
str = "UNKNOWN";
break;
}
return str;
}
const char* NroProcess::getSectionIndexStr(nx::dynsym::SpecialSectionIndex shn_index) const
{
const char* str;
switch (shn_index)
{
case (nx::dynsym::SHN_UNDEF):
str = "UNDEF";
break;
case (nx::dynsym::SHN_EXPORT):
str = "EXPORT";
break;
case (nx::dynsym::SHN_LOPROC):
str = "LOPROC";
break;
case (nx::dynsym::SHN_HIPROC):
str = "HIPROC";
break;
case (nx::dynsym::SHN_LOOS):
str = "LOOS";
break;
case (nx::dynsym::SHN_HIOS):
str = "HIOS";
break;
case (nx::dynsym::SHN_ABS):
str = "ABS";
break;
case (nx::dynsym::SHN_COMMON):
str = "COMMON";
break;
default:
str = "UNKNOWN";
break;
}
return str;
}
const char* NroProcess::getSymbolTypeStr(nx::dynsym::SymbolType symbol_type) const
{
const char* str;
switch (symbol_type)
{
case (nx::dynsym::STT_NOTYPE):
str = "NOTYPE";
break;
case (nx::dynsym::STT_OBJECT):
str = "OBJECT";
break;
case (nx::dynsym::STT_FUNC):
str = "FUNC";
break;
case (nx::dynsym::STT_SECTION):
str = "SECTION";
break;
case (nx::dynsym::STT_FILE):
str = "FILE";
break;
case (nx::dynsym::STT_LOOS):
str = "LOOS";
break;
case (nx::dynsym::STT_HIOS):
str = "HIOS";
break;
case (nx::dynsym::STT_LOPROC):
str = "LOPROC";
break;
case (nx::dynsym::STT_HIPROC):
str = "HIPROC";
break;
default:
str = "UNKNOWN";
break;
}
return str;
}

View file

@ -8,8 +8,7 @@
#include "AssetProcess.h"
#include "nstool.h"
#include "SdkApiString.h"
#include "DynamicSymbolParser.h"
#include "RoMetadataProcess.h"
class NroProcess
{
@ -40,24 +39,15 @@ private:
CliOutputMode mCliOutputMode;
bool mVerify;
nx::npdm::InstructionType mInstructionType;
bool mListApi;
bool mListSymbols;
nx::NroHeader mHdr;
fnd::MemoryBlob mTextBlob, mRoBlob, mDataBlob;
RoMetadataProcess mRoMeta;
bool mIsHomebrewNro;
AssetProcess mAssetProc;
fnd::MemoryBlob mTextBlob, mRoBlob, mDataBlob;
std::vector<SdkApiString> mApiList;
DynamicSymbolParser mDynSymbolList;
void importHeader();
void importCodeSegments();
void importApiList();
void displayHeader();
void displayRoMetaData();
const char* getApiTypeStr(SdkApiString::ApiType type) const;
const char* getSectionIndexStr(nx::dynsym::SpecialSectionIndex shn_index) const;
const char* getSymbolTypeStr(nx::dynsym::SymbolType symbol_type) const;
void processRoMeta();
};

View file

@ -1,4 +1,3 @@
#include <sstream>
#include <fnd/SimpleTextOutput.h>
#include <fnd/MemoryBlob.h>
#include <compress/lz4.h>
@ -9,10 +8,7 @@ NsoProcess::NsoProcess():
mFile(nullptr),
mOwnIFile(false),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mVerify(false),
mInstructionType(nx::npdm::INSTR_64BIT),
mListApi(false),
mListSymbols(false)
mVerify(false)
{
}
@ -33,12 +29,10 @@ void NsoProcess::process()
importHeader();
importCodeSegments();
importApiList();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
{
displayNsoHeader();
displayRoMetaData();
}
processRoMeta();
}
void NsoProcess::setInputFile(fnd::IFile* file, bool ownIFile)
@ -59,17 +53,17 @@ void NsoProcess::setVerifyMode(bool verify)
void NsoProcess::setInstructionType(nx::npdm::InstructionType type)
{
mInstructionType = type;
mRoMeta.setInstructionType(type);
}
void NsoProcess::setListApi(bool listApi)
{
mListApi = listApi;
mRoMeta.setListApi(listApi);
}
void NsoProcess::setListSymbols(bool listSymbols)
{
mListSymbols = listSymbols;
mRoMeta.setListSymbols(listSymbols);
}
void NsoProcess::importHeader()
@ -83,7 +77,7 @@ void NsoProcess::importHeader()
scratch.alloc(sizeof(nx::sNsoHeader));
mFile->read(scratch.getBytes(), 0, scratch.getSize());
mNsoHdr.importBinary(scratch.getBytes(), scratch.getSize());
mHdr.importBinary(scratch.getBytes(), scratch.getSize());
}
void NsoProcess::importCodeSegments()
@ -93,11 +87,11 @@ void NsoProcess::importCodeSegments()
crypto::sha::sSha256Hash calc_hash;
// process text segment
if (mNsoHdr.getTextSegmentInfo().is_compressed)
if (mHdr.getTextSegmentInfo().is_compressed)
{
scratch.alloc(mNsoHdr.getTextSegmentInfo().file_layout.size);
mFile->read(scratch.getBytes(), mNsoHdr.getTextSegmentInfo().file_layout.offset, scratch.getSize());
mTextBlob.alloc(mNsoHdr.getTextSegmentInfo().memory_layout.size);
scratch.alloc(mHdr.getTextSegmentInfo().file_layout.size);
mFile->read(scratch.getBytes(), mHdr.getTextSegmentInfo().file_layout.offset, scratch.getSize());
mTextBlob.alloc(mHdr.getTextSegmentInfo().memory_layout.size);
compress::lz4::decompressData(scratch.getBytes(), (uint32_t)scratch.getSize(), mTextBlob.getBytes(), (uint32_t)mTextBlob.getSize(), decompressed_len);
if (decompressed_len != mTextBlob.getSize())
{
@ -106,24 +100,24 @@ void NsoProcess::importCodeSegments()
}
else
{
mTextBlob.alloc(mNsoHdr.getTextSegmentInfo().file_layout.size);
mFile->read(mTextBlob.getBytes(), mNsoHdr.getTextSegmentInfo().file_layout.offset, mTextBlob.getSize());
mTextBlob.alloc(mHdr.getTextSegmentInfo().file_layout.size);
mFile->read(mTextBlob.getBytes(), mHdr.getTextSegmentInfo().file_layout.offset, mTextBlob.getSize());
}
if (mNsoHdr.getTextSegmentInfo().is_hashed)
if (mHdr.getTextSegmentInfo().is_hashed)
{
crypto::sha::Sha256(mTextBlob.getBytes(), mTextBlob.getSize(), calc_hash.bytes);
if (calc_hash != mNsoHdr.getTextSegmentInfo().hash)
if (calc_hash != mHdr.getTextSegmentInfo().hash)
{
throw fnd::Exception(kModuleName, "NSO text segment failed SHA256 verification");
}
}
// process ro segment
if (mNsoHdr.getRoSegmentInfo().is_compressed)
if (mHdr.getRoSegmentInfo().is_compressed)
{
scratch.alloc(mNsoHdr.getRoSegmentInfo().file_layout.size);
mFile->read(scratch.getBytes(), mNsoHdr.getRoSegmentInfo().file_layout.offset, scratch.getSize());
mRoBlob.alloc(mNsoHdr.getRoSegmentInfo().memory_layout.size);
scratch.alloc(mHdr.getRoSegmentInfo().file_layout.size);
mFile->read(scratch.getBytes(), mHdr.getRoSegmentInfo().file_layout.offset, scratch.getSize());
mRoBlob.alloc(mHdr.getRoSegmentInfo().memory_layout.size);
compress::lz4::decompressData(scratch.getBytes(), (uint32_t)scratch.getSize(), mRoBlob.getBytes(), (uint32_t)mRoBlob.getSize(), decompressed_len);
if (decompressed_len != mRoBlob.getSize())
{
@ -132,24 +126,24 @@ void NsoProcess::importCodeSegments()
}
else
{
mRoBlob.alloc(mNsoHdr.getRoSegmentInfo().file_layout.size);
mFile->read(mRoBlob.getBytes(), mNsoHdr.getRoSegmentInfo().file_layout.offset, mRoBlob.getSize());
mRoBlob.alloc(mHdr.getRoSegmentInfo().file_layout.size);
mFile->read(mRoBlob.getBytes(), mHdr.getRoSegmentInfo().file_layout.offset, mRoBlob.getSize());
}
if (mNsoHdr.getRoSegmentInfo().is_hashed)
if (mHdr.getRoSegmentInfo().is_hashed)
{
crypto::sha::Sha256(mRoBlob.getBytes(), mRoBlob.getSize(), calc_hash.bytes);
if (calc_hash != mNsoHdr.getRoSegmentInfo().hash)
if (calc_hash != mHdr.getRoSegmentInfo().hash)
{
throw fnd::Exception(kModuleName, "NSO ro segment failed SHA256 verification");
}
}
// process data segment
if (mNsoHdr.getDataSegmentInfo().is_compressed)
if (mHdr.getDataSegmentInfo().is_compressed)
{
scratch.alloc(mNsoHdr.getDataSegmentInfo().file_layout.size);
mFile->read(scratch.getBytes(), mNsoHdr.getDataSegmentInfo().file_layout.offset, scratch.getSize());
mDataBlob.alloc(mNsoHdr.getDataSegmentInfo().memory_layout.size);
scratch.alloc(mHdr.getDataSegmentInfo().file_layout.size);
mFile->read(scratch.getBytes(), mHdr.getDataSegmentInfo().file_layout.offset, scratch.getSize());
mDataBlob.alloc(mHdr.getDataSegmentInfo().memory_layout.size);
compress::lz4::decompressData(scratch.getBytes(), (uint32_t)scratch.getSize(), mDataBlob.getBytes(), (uint32_t)mDataBlob.getSize(), decompressed_len);
if (decompressed_len != mDataBlob.getSize())
{
@ -158,239 +152,99 @@ void NsoProcess::importCodeSegments()
}
else
{
mDataBlob.alloc(mNsoHdr.getDataSegmentInfo().file_layout.size);
mFile->read(mDataBlob.getBytes(), mNsoHdr.getDataSegmentInfo().file_layout.offset, mDataBlob.getSize());
mDataBlob.alloc(mHdr.getDataSegmentInfo().file_layout.size);
mFile->read(mDataBlob.getBytes(), mHdr.getDataSegmentInfo().file_layout.offset, mDataBlob.getSize());
}
if (mNsoHdr.getDataSegmentInfo().is_hashed)
if (mHdr.getDataSegmentInfo().is_hashed)
{
crypto::sha::Sha256(mDataBlob.getBytes(), mDataBlob.getSize(), calc_hash.bytes);
if (calc_hash != mNsoHdr.getDataSegmentInfo().hash)
if (calc_hash != mHdr.getDataSegmentInfo().hash)
{
throw fnd::Exception(kModuleName, "NSO data segment failed SHA256 verification");
}
}
}
void NsoProcess::importApiList()
{
struct sLayout { size_t offset; size_t size; } api_info, dyn_str, dyn_sym;
api_info.offset = mNsoHdr.getRoEmbeddedInfo().offset;
api_info.size = mNsoHdr.getRoEmbeddedInfo().size;
dyn_str.offset = mNsoHdr.getRoDynStrInfo().offset;
dyn_str.size = mNsoHdr.getRoDynStrInfo().size;
dyn_sym.offset = mNsoHdr.getRoDynSymInfo().offset;
dyn_sym.size = mNsoHdr.getRoDynSymInfo().size;
if (api_info.size > 0)
{
std::stringstream list_stream(std::string((char*)mRoBlob.getBytes() + api_info.offset, api_info.size));
std::string api;
while(std::getline(list_stream, api, (char)0x00))
{
mApiList.push_back(api);
}
}
else
{
mApiList.clear();
}
if (dyn_sym.size > 0)
{
mDynSymbolList.parseData(mRoBlob.getBytes() + dyn_sym.offset, dyn_sym.size, mRoBlob.getBytes() + dyn_str.offset, dyn_str.size, mInstructionType == nx::npdm::INSTR_64BIT);
}
}
void NsoProcess::displayNsoHeader()
{
#define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0)
printf("[NSO Header]\n");
printf(" ModuleId: ");
_HEXDUMP_L(mNsoHdr.getModuleId().data, nx::nso::kModuleIdSize);
_HEXDUMP_L(mHdr.getModuleId().data, nx::nso::kModuleIdSize);
printf("\n");
if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
printf(" Program Segments:\n");
printf(" .module_name:\n");
printf(" FileOffset: 0x%" PRIx32 "\n", mNsoHdr.getModuleNameInfo().offset);
printf(" FileSize: 0x%" PRIx32 "\n", mNsoHdr.getModuleNameInfo().size);
printf(" .text:\n");
printf(" FileOffset: 0x%" PRIx32 "\n", mNsoHdr.getTextSegmentInfo().file_layout.offset);
printf(" FileSize: 0x%" PRIx32 "%s\n", mNsoHdr.getTextSegmentInfo().file_layout.size, mNsoHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : "");
printf(" .ro:\n");
printf(" FileOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoSegmentInfo().file_layout.offset);
printf(" FileSize: 0x%" PRIx32 "%s\n", mNsoHdr.getRoSegmentInfo().file_layout.size, mNsoHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : "");
printf(" .data:\n");
printf(" FileOffset: 0x%" PRIx32 "\n", mNsoHdr.getDataSegmentInfo().file_layout.offset);
printf(" FileSize: 0x%" PRIx32 "%s\n", mNsoHdr.getDataSegmentInfo().file_layout.size, mNsoHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : "");
{
printf(" Program Segments:\n");
printf(" .module_name:\n");
printf(" FileOffset: 0x%" PRIx32 "\n", mHdr.getModuleNameInfo().offset);
printf(" FileSize: 0x%" PRIx32 "\n", mHdr.getModuleNameInfo().size);
printf(" .text:\n");
printf(" FileOffset: 0x%" PRIx32 "\n", mHdr.getTextSegmentInfo().file_layout.offset);
printf(" FileSize: 0x%" PRIx32 "%s\n", mHdr.getTextSegmentInfo().file_layout.size, mHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : "");
printf(" .ro:\n");
printf(" FileOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().file_layout.offset);
printf(" FileSize: 0x%" PRIx32 "%s\n", mHdr.getRoSegmentInfo().file_layout.size, mHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : "");
printf(" .data:\n");
printf(" FileOffset: 0x%" PRIx32 "\n", mHdr.getDataSegmentInfo().file_layout.offset);
printf(" FileSize: 0x%" PRIx32 "%s\n", mHdr.getDataSegmentInfo().file_layout.size, mHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : "");
}
printf(" Program Sections:\n");
printf(" .text:\n");
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getTextSegmentInfo().memory_layout.offset);
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getTextSegmentInfo().memory_layout.size);
if (mNsoHdr.getTextSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getTextSegmentInfo().memory_layout.offset);
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getTextSegmentInfo().memory_layout.size);
if (mHdr.getTextSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
printf(" Hash: ");
_HEXDUMP_L(mNsoHdr.getTextSegmentInfo().hash.bytes, 32);
_HEXDUMP_L(mHdr.getTextSegmentInfo().hash.bytes, 32);
printf("\n");
}
printf(" .ro:\n");
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoSegmentInfo().memory_layout.offset);
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getRoSegmentInfo().memory_layout.size);
if (mNsoHdr.getRoSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.offset);
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.size);
if (mHdr.getRoSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
printf(" Hash: ");
_HEXDUMP_L(mNsoHdr.getRoSegmentInfo().hash.bytes, 32);
_HEXDUMP_L(mHdr.getRoSegmentInfo().hash.bytes, 32);
printf("\n");
}
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
printf(" .api_info:\n");
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoEmbeddedInfo().offset);
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getRoEmbeddedInfo().size);
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoEmbeddedInfo().offset);
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoEmbeddedInfo().size);
printf(" .dynstr:\n");
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoDynStrInfo().offset);
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getRoDynStrInfo().size);
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoDynStrInfo().offset);
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoDynStrInfo().size);
printf(" .dynsym:\n");
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoDynSymInfo().offset);
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getRoDynSymInfo().size);
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoDynSymInfo().offset);
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoDynSymInfo().size);
}
printf(" .data:\n");
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getDataSegmentInfo().memory_layout.offset);
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getDataSegmentInfo().memory_layout.size);
if (mNsoHdr.getDataSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getDataSegmentInfo().memory_layout.offset);
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getDataSegmentInfo().memory_layout.size);
if (mHdr.getDataSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
printf(" Hash: ");
_HEXDUMP_L(mNsoHdr.getDataSegmentInfo().hash.bytes, 32);
_HEXDUMP_L(mHdr.getDataSegmentInfo().hash.bytes, 32);
printf("\n");
}
printf(" .bss:\n");
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getBssSize());
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getBssSize());
#undef _HEXDUMP_L
}
void NsoProcess::displayRoMetaData()
void NsoProcess::processRoMeta()
{
if (mApiList.size() > 0 && (mListApi || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)))
if (mRoBlob.getSize())
{
printf("[SDK API List]\n");
for (size_t i = 0; i < mApiList.size(); i++)
{
printf(" API %d:\n", (int)i);
printf(" Type: %s\n", getApiTypeStr(mApiList[i].getApiType()));
printf(" Vender: %s\n", mApiList[i].getVenderName().c_str());
printf(" Module: %s\n", mApiList[i].getModuleName().c_str());
}
// setup ro metadata
mRoMeta.setApiInfo(mHdr.getRoEmbeddedInfo().offset, mHdr.getRoEmbeddedInfo().size);
mRoMeta.setDynSym(mHdr.getRoDynSymInfo().offset, mHdr.getRoDynSymInfo().size);
mRoMeta.setDynStr(mHdr.getRoDynStrInfo().offset, mHdr.getRoDynStrInfo().size);
mRoMeta.setRoBinary(mRoBlob);
mRoMeta.setCliOutputMode(mCliOutputMode);
mRoMeta.process();
}
if (mDynSymbolList.getDynamicSymbolList().getSize() > 0 && (mListSymbols || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)))
{
printf("[Symbol List]\n");
for (size_t i = 0; i < mDynSymbolList.getDynamicSymbolList().getSize(); i++)
{
const DynamicSymbolParser::sDynSymbol& symbol = mDynSymbolList.getDynamicSymbolList()[i];
printf(" %s [SHN=%s (%04x)][STT=%s]\n", symbol.name.c_str(), getSectionIndexStr(symbol.shn_index), symbol.shn_index, getSymbolTypeStr(symbol.symbol_type));
}
}
}
const char* NsoProcess::getApiTypeStr(SdkApiString::ApiType type) const
{
const char* str;
switch (type)
{
case (SdkApiString::API_MIDDLEWARE):
str = "Middleware";
break;
case (SdkApiString::API_DEBUG):
str = "Debug";
break;
case (SdkApiString::API_PRIVATE):
str = "Private";
break;
case (SdkApiString::API_SDK_VERSION):
str = "SDK Version";
break;
default:
str = "UNKNOWN";
break;
}
return str;
}
const char* NsoProcess::getSectionIndexStr(nx::dynsym::SpecialSectionIndex shn_index) const
{
const char* str;
switch (shn_index)
{
case (nx::dynsym::SHN_UNDEF):
str = "UNDEF";
break;
case (nx::dynsym::SHN_EXPORT):
str = "EXPORT";
break;
case (nx::dynsym::SHN_LOPROC):
str = "LOPROC";
break;
case (nx::dynsym::SHN_HIPROC):
str = "HIPROC";
break;
case (nx::dynsym::SHN_LOOS):
str = "LOOS";
break;
case (nx::dynsym::SHN_HIOS):
str = "HIOS";
break;
case (nx::dynsym::SHN_ABS):
str = "ABS";
break;
case (nx::dynsym::SHN_COMMON):
str = "COMMON";
break;
default:
str = "UNKNOWN";
break;
}
return str;
}
const char* NsoProcess::getSymbolTypeStr(nx::dynsym::SymbolType symbol_type) const
{
const char* str;
switch (symbol_type)
{
case (nx::dynsym::STT_NOTYPE):
str = "NOTYPE";
break;
case (nx::dynsym::STT_OBJECT):
str = "OBJECT";
break;
case (nx::dynsym::STT_FUNC):
str = "FUNC";
break;
case (nx::dynsym::STT_SECTION):
str = "SECTION";
break;
case (nx::dynsym::STT_FILE):
str = "FILE";
break;
case (nx::dynsym::STT_LOOS):
str = "LOOS";
break;
case (nx::dynsym::STT_HIOS):
str = "HIOS";
break;
case (nx::dynsym::STT_LOPROC):
str = "LOPROC";
break;
case (nx::dynsym::STT_HIPROC):
str = "HIPROC";
break;
default:
str = "UNKNOWN";
break;
}
return str;
}

View file

@ -7,8 +7,7 @@
#include <nx/NsoHeader.h>
#include "nstool.h"
#include "SdkApiString.h"
#include "DynamicSymbolParser.h"
#include "RoMetadataProcess.h"
class NsoProcess
{
@ -37,18 +36,12 @@ private:
bool mListApi;
bool mListSymbols;
nx::NsoHeader mNsoHdr;
nx::NsoHeader mHdr;
fnd::MemoryBlob mTextBlob, mRoBlob, mDataBlob;
std::vector<SdkApiString> mApiList;
DynamicSymbolParser mDynSymbolList;
RoMetadataProcess mRoMeta;
void importHeader();
void importCodeSegments();
void importApiList();
void displayNsoHeader();
void displayRoMetaData();
const char* getApiTypeStr(SdkApiString::ApiType type) const;
const char* getSectionIndexStr(nx::dynsym::SpecialSectionIndex shn_index) const;
const char* getSymbolTypeStr(nx::dynsym::SymbolType symbol_type) const;
void processRoMeta();
};

View file

@ -0,0 +1,255 @@
#include <sstream>
#include <fnd/types.h>
#include "RoMetadataProcess.h"
RoMetadataProcess::RoMetadataProcess() :
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mInstructionType(nx::npdm::INSTR_64BIT),
mListApi(false),
mListSymbols(false),
mApiInfo(),
mDynSym(),
mDynStr(),
mRoBlob(),
mSdkVerApiList(),
mPublicApiList(),
mDebugApiList(),
mPrivateApiList(),
mSymbolList()
{
}
void RoMetadataProcess::process()
{
if (mRoBlob.getSize() == 0)
{
throw fnd::Exception(kModuleName, "No ro binary set.");
}
importApiList();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
displayRoMetaData();
}
void RoMetadataProcess::setRoBinary(const fnd::MemoryBlob& bin)
{
mRoBlob = bin;
}
void RoMetadataProcess::setApiInfo(size_t offset, size_t size)
{
mApiInfo.offset = offset;
mApiInfo.size = size;
}
void RoMetadataProcess::setDynSym(size_t offset, size_t size)
{
mDynSym.offset = offset;
mDynSym.size = size;
}
void RoMetadataProcess::setDynStr(size_t offset, size_t size)
{
mDynStr.offset = offset;
mDynStr.size = size;
}
void RoMetadataProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
void RoMetadataProcess::setInstructionType(nx::npdm::InstructionType type)
{
mInstructionType = type;
}
void RoMetadataProcess::setListApi(bool listApi)
{
mListApi = listApi;
}
void RoMetadataProcess::setListSymbols(bool listSymbols)
{
mListSymbols = listSymbols;
}
void RoMetadataProcess::importApiList()
{
if (mApiInfo.size > 0)
{
std::stringstream list_stream(std::string((char*)mRoBlob.getBytes() + mApiInfo.offset, mApiInfo.size));
std::string api_str;
while(std::getline(list_stream, api_str, (char)0x00))
{
SdkApiString api(api_str);
if (api.getApiType() == SdkApiString::API_SDK_VERSION)
mSdkVerApiList.push_back(api);
else if (api.getApiType() == SdkApiString::API_MIDDLEWARE)
mPublicApiList.push_back(api);
else if (api.getApiType() == SdkApiString::API_DEBUG)
mDebugApiList.push_back(api);
else if (api.getApiType() == SdkApiString::API_PRIVATE)
mPrivateApiList.push_back(api);
}
}
if (mDynSym.size > 0)
{
mSymbolList.parseData(mRoBlob.getBytes() + mDynSym.offset, mDynSym.size, mRoBlob.getBytes() + mDynStr.offset, mDynStr.size, mInstructionType == nx::npdm::INSTR_64BIT);
}
}
void RoMetadataProcess::displayRoMetaData()
{
size_t api_num = mSdkVerApiList.size() + mPublicApiList.size() + mDebugApiList.size() + mPrivateApiList.size();
if (api_num > 0 && (mListApi || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)))
{
printf("[SDK API List]\n");
if (mSdkVerApiList.size() > 0)
{
printf(" Sdk Revision: %s\n", mSdkVerApiList[0].getModuleName().c_str());
}
if (mPublicApiList.size() > 0)
{
printf(" Public APIs:\n");
for (size_t i = 0; i < mPublicApiList.size(); i++)
{
printf(" %s (vender: %s)\n", mPublicApiList[i].getModuleName().c_str(), mPublicApiList[i].getVenderName().c_str());
}
}
if (mDebugApiList.size() > 0)
{
printf(" Debug APIs:\n");
for (size_t i = 0; i < mDebugApiList.size(); i++)
{
printf(" %s (vender: %s)\n", mDebugApiList[i].getModuleName().c_str(), mDebugApiList[i].getVenderName().c_str());
}
}
if (mPrivateApiList.size() > 0)
{
printf(" Private APIs:\n");
for (size_t i = 0; i < mPrivateApiList.size(); i++)
{
printf(" %s (vender: %s)\n", mPrivateApiList[i].getModuleName().c_str(), mPrivateApiList[i].getVenderName().c_str());
}
}
}
if (mSymbolList.getSymbolList().getSize() > 0 && (mListSymbols || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)))
{
printf("[Symbol List]\n");
for (size_t i = 0; i < mSymbolList.getSymbolList().getSize(); i++)
{
const ElfSymbolParser::sElfSymbol& symbol = mSymbolList.getSymbolList()[i];
printf(" %s [SHN=%s (%04x)][STT=%s][STB=%s]\n", symbol.name.c_str(), getSectionIndexStr(symbol.shn_index), symbol.shn_index, getSymbolTypeStr(symbol.symbol_type), getSymbolBindingStr(symbol.symbol_binding));
}
}
}
const char* RoMetadataProcess::getSectionIndexStr(nx::elf::SpecialSectionIndex shn_index) const
{
const char* str;
switch (shn_index)
{
case (nx::elf::SHN_UNDEF):
str = "UNDEF";
break;
case (nx::elf::SHN_LOPROC):
str = "LOPROC";
break;
case (nx::elf::SHN_HIPROC):
str = "HIPROC";
break;
case (nx::elf::SHN_LOOS):
str = "LOOS";
break;
case (nx::elf::SHN_HIOS):
str = "HIOS";
break;
case (nx::elf::SHN_ABS):
str = "ABS";
break;
case (nx::elf::SHN_COMMON):
str = "COMMON";
break;
default:
str = "UNKNOWN";
break;
}
return str;
}
const char* RoMetadataProcess::getSymbolTypeStr(nx::elf::SymbolType symbol_type) const
{
const char* str;
switch (symbol_type)
{
case (nx::elf::STT_NOTYPE):
str = "NOTYPE";
break;
case (nx::elf::STT_OBJECT):
str = "OBJECT";
break;
case (nx::elf::STT_FUNC):
str = "FUNC";
break;
case (nx::elf::STT_SECTION):
str = "SECTION";
break;
case (nx::elf::STT_FILE):
str = "FILE";
break;
case (nx::elf::STT_LOOS):
str = "LOOS";
break;
case (nx::elf::STT_HIOS):
str = "HIOS";
break;
case (nx::elf::STT_LOPROC):
str = "LOPROC";
break;
case (nx::elf::STT_HIPROC):
str = "HIPROC";
break;
default:
str = "UNKNOWN";
break;
}
return str;
}
const char* RoMetadataProcess::getSymbolBindingStr(nx::elf::SymbolBinding symbol_binding) const
{
const char* str;
switch (symbol_binding)
{
case (nx::elf::STB_LOCAL):
str = "LOCAL";
break;
case (nx::elf::STB_GLOBAL):
str = "GLOBAL";
break;
case (nx::elf::STB_WEAK):
str = "WEAK";
break;
case (nx::elf::STB_LOOS):
str = "LOOS";
break;
case (nx::elf::STB_HIOS):
str = "HIOS";
break;
case (nx::elf::STB_LOPROC):
str = "LOPROC";
break;
case (nx::elf::STB_HIPROC):
str = "HIPROC";
break;
default:
str = "UNKNOWN";
break;
}
return str;
}

View file

@ -0,0 +1,62 @@
#pragma once
#include <vector>
#include <string>
#include <fnd/types.h>
#include <fnd/MemoryBlob.h>
#include <nx/npdm.h>
#include "nstool.h"
#include "SdkApiString.h"
#include "ElfSymbolParser.h"
class RoMetadataProcess
{
public:
RoMetadataProcess();
void process();
void setRoBinary(const fnd::MemoryBlob& bin);
void setApiInfo(size_t offset, size_t size);
void setDynSym(size_t offset, size_t size);
void setDynStr(size_t offset, size_t size);
void setCliOutputMode(CliOutputMode type);
void setInstructionType(nx::npdm::InstructionType type);
void setListApi(bool listApi);
void setListSymbols(bool listSymbols);
private:
const std::string kModuleName = "RoMetadataProcess";
CliOutputMode mCliOutputMode;
nx::npdm::InstructionType mInstructionType;
bool mListApi;
bool mListSymbols;
struct sLayout
{
sLayout() : offset(0), size(0) {}
size_t offset;
size_t size;
};
sLayout mApiInfo;
sLayout mDynSym;
sLayout mDynStr;
fnd::MemoryBlob mRoBlob;
std::vector<SdkApiString> mSdkVerApiList;
std::vector<SdkApiString> mPublicApiList;
std::vector<SdkApiString> mDebugApiList;
std::vector<SdkApiString> mPrivateApiList;
ElfSymbolParser mSymbolList;
void importApiList();
void displayRoMetaData();
const char* getSectionIndexStr(nx::elf::SpecialSectionIndex shn_index) const;
const char* getSymbolTypeStr(nx::elf::SymbolType symbol_type) const;
const char* getSymbolBindingStr(nx::elf::SymbolBinding symbol_binding) const;
};