mirror of
https://github.com/zeldaret/ph
synced 2026-05-29 08:42:55 -04:00
Add elfkill
This commit is contained in:
@@ -85,6 +85,7 @@ all: tools rom
|
||||
tools:
|
||||
cd $(TOOLS_DIR)/compress && $(MAKE)
|
||||
cd $(TOOLS_DIR)/rom && $(MAKE)
|
||||
cd $(TOOLS_DIR)/elf && $(MAKE)
|
||||
|
||||
.PHONY: rom
|
||||
rom: arm9
|
||||
@@ -126,6 +127,7 @@ $(ASM_OBJS): $(TARGET_DIR)/%.o: %
|
||||
$(CXX_OBJS): $(TARGET_DIR)/%.o: %
|
||||
mkdir -p $(dir $@)
|
||||
LM_LICENSE_FILE=$(MW_LICENSE) $(WINE) $(MW_CC) $(CC_FLAGS) $(CXX_FLAGS) $< -o $@
|
||||
$(TOOLS_DIR)/elf/elfkill -s $< -e $@
|
||||
|
||||
$(C_OBJS): $(TARGET_DIR)/%.o: %
|
||||
mkdir -p $(dir $@)
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
#define NONMATCH(name) asm name
|
||||
#endif
|
||||
|
||||
// KILL(name) causes a function to be excluded from the output ROM, see elfkill.cpp
|
||||
#define KILL(name)
|
||||
|
||||
// Prevent the IDE from reporting errors that the compiler/linker won't report
|
||||
#ifdef __INTELLISENSE__
|
||||
#endif
|
||||
@@ -28,4 +31,7 @@
|
||||
// Define .sbss variables by using #pragma section sbss begin|end
|
||||
#pragma define_section sbss ".data" ".sbss"
|
||||
|
||||
// Force variables to be in .data by using #pragma section force_data begin|end
|
||||
#pragma define_section force_data ".data" ".data"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
mwccarm/
|
||||
temp/
|
||||
deps/
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
elfkill
|
||||
elfkill.exe
|
||||
@@ -0,0 +1,22 @@
|
||||
CXX := g++
|
||||
CFLAGS := -std=c++17 -g -Wall -I../include -I../deps/elfio
|
||||
|
||||
ifneq ($(DEBUG),1)
|
||||
CFLAGS += -O2 -DNDEBUG
|
||||
endif
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
ELFKILLFILE := elfkill.exe
|
||||
else
|
||||
ELFKILLFILE := elfkill
|
||||
endif
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(ELFKILLFILE)
|
||||
|
||||
clean:
|
||||
rm -f $(ELFKILLFILE)
|
||||
|
||||
$(ELFKILLFILE): elfkill.cpp
|
||||
$(CXX) $(CFLAGS) -o $(ELFKILLFILE) elfkill.cpp
|
||||
@@ -0,0 +1,284 @@
|
||||
#include <iostream> // cout, cerr, endl
|
||||
#include <iomanip> // setw
|
||||
#include <vector> // vector
|
||||
#include <string> // string
|
||||
#include <unordered_set> // unordered_set
|
||||
#include <cstring> // strcmp, strncpy
|
||||
#include <fstream> // ifstream
|
||||
#include <cstdio> // remove
|
||||
|
||||
#include <elfio/elfio.hpp>
|
||||
#include <elfio/elfio_dump.hpp>
|
||||
|
||||
#define VERSION "1.0"
|
||||
|
||||
using namespace ELFIO;
|
||||
|
||||
struct SymbolSection {
|
||||
Elf_Half index;
|
||||
section *elfSection;
|
||||
std::string name;
|
||||
|
||||
bool Get(const elfio &elf, Elf_Half index) {
|
||||
this->index = index;
|
||||
|
||||
elfSection = elf.sections[index];
|
||||
if (elfSection == nullptr) {
|
||||
std::cerr << "Failed to get section " << index << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
name = elfSection->get_name();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetName(const elfio &elf, const std::string &name) {
|
||||
elfSection->set_name(name);
|
||||
this->name = name;
|
||||
|
||||
Elf_Word nameIndex = elfSection->get_name_string_offset();
|
||||
|
||||
section *shstrtab = elf.sections[".shstrtab"];
|
||||
if (shstrtab == nullptr) {
|
||||
std::cerr << "Failed to get string section" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
string_section_accessor strAccessor(shstrtab);
|
||||
const char *tabStr = strAccessor.get_string(nameIndex);
|
||||
size_t len = strlen(tabStr);
|
||||
|
||||
if (len < name.length()) {
|
||||
std::cerr << "Cannot rename section " << tabStr << " because it is shorter than " << name << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// HACK: Strings shorter than `name` can't be renamed due to lack of space
|
||||
strncpy((char*) tabStr, name.c_str(), len);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct Symbol {
|
||||
Elf_Xword index;
|
||||
std::string name;
|
||||
Elf64_Addr value;
|
||||
Elf_Xword size;
|
||||
unsigned char bind;
|
||||
unsigned char type;
|
||||
SymbolSection section;
|
||||
unsigned char other;
|
||||
|
||||
bool Get(const elfio &elf, const symbol_section_accessor &accessor, Elf_Xword index) {
|
||||
this->index = index;
|
||||
|
||||
if (!accessor.get_symbol(index, name, value, size, bind, type, section.index, other)) {
|
||||
std::cerr << "Failed to get symbol at index " << index << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!section.Get(elf, section.index)) {
|
||||
std::cerr << "...for symbol '" << name << "'" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void PrintHeader() {
|
||||
std::cout
|
||||
<< std::setw(75) << "name"
|
||||
<< std::setw(6) << "value"
|
||||
<< std::setw(6) << "size"
|
||||
<< std::setw(5) << "bind"
|
||||
<< std::setw(5) << "type"
|
||||
<< std::setw(8) << "section"
|
||||
<< std::setw(12) << ""
|
||||
<< std::setw(6) << "other"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void Print() const {
|
||||
std::cout
|
||||
<< std::setw(75) << name
|
||||
<< std::setw(6) << value
|
||||
<< std::setw(6) << size
|
||||
<< std::setw(5) << (int) bind
|
||||
<< std::setw(5) << (int) type
|
||||
<< std::setw(5) << section.index << ' '
|
||||
<< std::setw(14) << std::left << section.name << std::right
|
||||
<< std::setw(6) << (int) other
|
||||
<< std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
bool GetFunctionSymbols(const elfio &elf, std::vector<Symbol> &outSymbols) {
|
||||
section *symtab = elf.sections[".symtab"];
|
||||
if (symtab == nullptr) {
|
||||
std::cerr << "No section called .symtab" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
symbol_section_accessor symAccessor(elf, symtab);
|
||||
std::vector<Symbol> symbols;
|
||||
for (Elf_Xword i = 0; i < symAccessor.get_symbols_num(); ++i) {
|
||||
Symbol symbol;
|
||||
if (!symbol.Get(elf, symAccessor, i)) return false;
|
||||
|
||||
if (symbol.name.find("@", 0) == 0) continue;
|
||||
if (symbol.name.find("$", 0) == 0) continue;
|
||||
if (symbol.name.find(".", 0) == 0) continue;
|
||||
if (symbol.section.name != ".text") continue;
|
||||
|
||||
symbols.push_back(symbol);
|
||||
}
|
||||
|
||||
outSymbols = symbols;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FindSymbolsToKill(const char *srcFile, std::unordered_set<std::string> &outSymbolsToKill) {
|
||||
std::ifstream file(srcFile);
|
||||
|
||||
const std::string killMacro = "KILL(";
|
||||
std::string line;
|
||||
size_t row = 0;
|
||||
std::unordered_set<std::string> symbolsToKill;
|
||||
while (std::getline(file, line)) {
|
||||
row += 1;
|
||||
size_t endOffset = 0;
|
||||
while (true) {
|
||||
size_t macroOffset = line.find(killMacro, endOffset);
|
||||
if (macroOffset == std::string::npos) break;
|
||||
|
||||
size_t symbolOffset = macroOffset + killMacro.length();
|
||||
symbolOffset = line.find_first_not_of(" \t", symbolOffset);
|
||||
if (symbolOffset == std::string::npos) {
|
||||
std::cerr
|
||||
<< srcFile << ':' << row << ':' << macroOffset + 1
|
||||
<< ": Expected non-whitespace character after " << killMacro << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
endOffset = line.find_first_of(" \t)", symbolOffset);
|
||||
if (endOffset == std::string::npos) {
|
||||
std::cerr
|
||||
<< srcFile << ':' << row << ':' << symbolOffset + 1
|
||||
<< ": Expected whitespace character or ')' after kill symbol" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string symbolToKill = line.substr(symbolOffset, endOffset - symbolOffset);
|
||||
symbolsToKill.insert(symbolToKill);
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
outSymbolsToKill = symbolsToKill;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KillFunctionSymbols(
|
||||
const elfio &elf,
|
||||
std::vector<Symbol> &symbols,
|
||||
const std::unordered_set<std::string> &symbolsToKill
|
||||
) {
|
||||
for (Symbol &symbol : symbols) {
|
||||
auto it = symbolsToKill.find(symbol.name);
|
||||
if (it == symbolsToKill.end()) continue;
|
||||
|
||||
if (!symbol.section.SetName(elf, ".dead")) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeleteElf(const char *elfFile) {
|
||||
// Delete ELF file so the Makefile doesn't skip elfkill on next build
|
||||
if (std::remove(elfFile) == 0) return true;
|
||||
std::cerr << "Failed to delete ELF '" << elfFile << "' upon previous error" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
void PrintUsage(const char *program) {
|
||||
std::cout
|
||||
<< "elfkill " VERSION "\n"
|
||||
<< "\n"
|
||||
<< "Usage: " << program << " -s SRCFILE -e ELFFILE\n"
|
||||
<< " -s SRCFILE\tSource C/C++ file\n"
|
||||
<< " -e ELFFILE\tELF file corresponding to SRCFILE\n"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
const char *program = argv[0];
|
||||
if (argc == 1) {
|
||||
PrintUsage(program);
|
||||
return 0;
|
||||
}
|
||||
const char *srcFile = nullptr;
|
||||
const char *elfFile = nullptr;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (strcmp(argv[i], "-s") == 0) {
|
||||
if (++i >= argc) {
|
||||
std::cerr << "Expected filename after -s" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
srcFile = argv[i];
|
||||
} else if (strcmp(argv[i], "-e") == 0) {
|
||||
if (++i >= argc) {
|
||||
std::cerr << "Expected filename after -e" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
elfFile = argv[i];
|
||||
} else {
|
||||
std::cerr << "Unknown option '" << argv[i] << "'" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (srcFile == nullptr) {
|
||||
PrintUsage(program);
|
||||
std::cerr << "Please provide a source file, see usage above" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
if (elfFile == nullptr) {
|
||||
PrintUsage(program);
|
||||
std::cerr << "Please provide an ELF file, see usage above" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
elfio elf;
|
||||
if (!elf.load(elfFile)) {
|
||||
std::cerr << "Failed to load ELF file '" << elfFile << "'" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<Symbol> symbols;
|
||||
if (!GetFunctionSymbols(elf, symbols)) {
|
||||
DeleteElf(elfFile);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Symbol::PrintHeader();
|
||||
// for (const Symbol &symbol : symbols) {
|
||||
// symbol.Print();
|
||||
// }
|
||||
// return 0;
|
||||
|
||||
std::unordered_set<std::string> symbolsToKill;
|
||||
if (!FindSymbolsToKill(srcFile, symbolsToKill)) {
|
||||
DeleteElf(elfFile);
|
||||
return 1;
|
||||
}
|
||||
if (!KillFunctionSymbols(elf, symbols, symbolsToKill)) {
|
||||
DeleteElf(elfFile);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Symbol::PrintHeader();
|
||||
// for (const Symbol &symbol : symbols) {
|
||||
// symbol.Print();
|
||||
// }
|
||||
|
||||
elf.save(elfFile);
|
||||
}
|
||||
@@ -4,8 +4,11 @@ import io
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
tools_path = Path(__file__).parent
|
||||
deps_path = tools_path / 'deps'
|
||||
if not deps_path.exists(): deps_path.mkdir()
|
||||
|
||||
print('\nInstalling toolchain...')
|
||||
response = requests.get('http://decomp.aetias.com/mwccarm.zip')
|
||||
@@ -14,3 +17,12 @@ zip_file.extractall(tools_path)
|
||||
|
||||
print('\nPatching...')
|
||||
subprocess.run([sys.executable, 'patch_mwcc.py', 'mwccarm/2.0/sp1p5/mwasmarm.exe'], cwd=tools_path)
|
||||
|
||||
print('\nInstalling ELFIO...')
|
||||
response = requests.get('https://github.com/serge1/ELFIO/releases/download/Release_3.12/elfio-3.12.zip')
|
||||
zip_file = zipfile.ZipFile(io.BytesIO(response.content))
|
||||
zip_file.extractall(deps_path)
|
||||
elfio_path = deps_path / 'elfio-3.12'
|
||||
elfio_new_path = deps_path / 'elfio'
|
||||
if elfio_new_path.exists(): shutil.rmtree(elfio_new_path)
|
||||
elfio_path.rename(elfio_new_path)
|
||||
|
||||
Reference in New Issue
Block a user