Skip to content

Commit fcf3b7a

Browse files
authored
Merge pull request #3 from devkitPro/rplwrap
wut 1.1.0: rplwrap, garbage collection
2 parents 6a54839 + 98f6581 commit fcf3b7a

File tree

5 files changed

+150
-36
lines changed

5 files changed

+150
-36
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#### wut-tools 1.1.0
2+
- elf2rpl: Added a new feature, `__rplwrap`. This will rename any symbol named `__rplwrap_<name>` (where `<name>` is any string) to just `<name>`. If a `<name>` already exists that would conflict with the new symbol, it is renamed to `__rplwrap_name`.
3+
- rplimportgen: Add support for `:TEXT_WRAP` and `:DATA_WRAP` sections. Every symbol in these sections will be prefixed with `__rplwrap_`. This is useful for cases where Cafe functions conflict with libc functions, and should not be used outside of libc or wut internals.
4+
- No known loader, including decaf, readrpl and the Cafe system loader.elf, actually uses or checks the crc32 and count parameters on an import section. To allow import garbage-collection, these have been hardcoded to dummy values.
5+
- rplimportgen now places each imported function into a dedicated section, -ffunction-sections style. This allows ld to garbage-collect unused imports, but also requires an updated linker script that only ships with wut 1.0.0-beta9 and later.
6+
7+
#### wut-tools 1.0.0

configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Process this file with autoconf to produce a configure script.
33

44
AC_PREREQ(2.61)
5-
AC_INIT([wut-tools],[1.0.0],[https://github.com/devkitPro/wut-tools/issues])
5+
AC_INIT([wut-tools],[1.1.0],[https://github.com/devkitPro/wut-tools/issues])
66
AC_CONFIG_SRCDIR([src/elf2rpl/main.cpp])
77
AC_CONFIG_MACRO_DIR([m4])
88

src/common/rplwrap.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#pragma once
2+
3+
#define RPLWRAP_PREFIX "__rplwrap_"

src/elf2rpl/main.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "elf.h"
22
#include "utils.h"
3+
#include "rplwrap.h"
34

45
#include <algorithm>
56
#include <excmd.h>
@@ -463,6 +464,99 @@ relocateSection(ElfFile &file,
463464
return true;
464465
}
465466

467+
const static std::string rplwrap_prefix(RPLWRAP_PREFIX);
468+
469+
/**
470+
* Rename __rplwrap_<name> to <name>, and if <name> already exists rename it to
471+
* __rplwrap_<name>.
472+
*
473+
* This is useful in situations where the imported libraries have names that
474+
* conflict with existing ones.
475+
*/
476+
static bool
477+
renameRplWrap(ElfFile &file)
478+
{
479+
auto strtabIndex = getSectionIndex(file, ".strtab");
480+
if (strtabIndex < 0) return false;
481+
auto& strtab = file.sections[strtabIndex];
482+
auto strtabd = reinterpret_cast<char *>(strtab->data.data());
483+
484+
for (auto &symSection : file.sections) {
485+
if (symSection->header.type != elf::SectionType::SHT_SYMTAB) {
486+
continue;
487+
}
488+
489+
auto symbols = reinterpret_cast<elf::Symbol *>(symSection->data.data());
490+
auto numSymbols = symSection->data.size() / sizeof(elf::Symbol);
491+
492+
// First pass - find all the symbols prefixed with __rplwrap_, don't do
493+
// anything yet
494+
std::vector<elf::Symbol*> foundRplWraps;
495+
496+
for (auto i = 0u; i < numSymbols; ++i) {
497+
auto type = symbols[i].info & 0xf;
498+
499+
// Only rename functions, data
500+
if (type != elf::STT_OBJECT &&
501+
type != elf::STT_FUNC) {
502+
continue;
503+
}
504+
505+
std::string name = &strtabd[symbols[i].name];
506+
if (!name.compare(0, rplwrap_prefix.size(), rplwrap_prefix)) {
507+
foundRplWraps.push_back(&symbols[i]);
508+
}
509+
}
510+
511+
// Second pass - Find any symbols that would conflict if __rplwrap_<name>
512+
// got renamed to <name>, and if so, swap the names
513+
514+
for (auto i = 0u; i < numSymbols; ++i) {
515+
auto type = symbols[i].info & 0xf;
516+
std::string symName = &strtabd[symbols[i].name];
517+
518+
// Only rename functions, data
519+
if (type != elf::STT_OBJECT &&
520+
type != elf::STT_FUNC) {
521+
continue;
522+
}
523+
524+
auto rplWrap = foundRplWraps.begin();
525+
while (rplWrap != foundRplWraps.end()) {
526+
std::string wrapName = &strtabd[(*rplWrap)->name];
527+
// Get the <name> part of __rplwrap_<name>
528+
std::string wrapNameBase = wrapName.substr(rplwrap_prefix.size());
529+
530+
if (wrapNameBase == symName) {
531+
// both __rplwrap_<name> and <name> exist, we can just swap their
532+
// name pointers
533+
#ifdef DEBUG
534+
fmt::print("DEBUG: renameRplWrap: {} <-> {}\n",
535+
wrapName, symName);
536+
#endif //DEBUG
537+
std::swap((*rplWrap)->name, symbols[i].name);
538+
// We're done, remove symbol from foundRplWraps
539+
rplWrap = foundRplWraps.erase(rplWrap);
540+
// Otherwise, increment the iterator and go around again
541+
} else ++rplWrap;
542+
}
543+
}
544+
545+
// Final pass: rename any remaining (non-conflicting) __rplwrap_<name>
546+
// symbols to <name>
547+
548+
for (auto symbol : foundRplWraps) {
549+
#ifdef DEBUG
550+
fmt::print("DEBUG: renameRplWrap: {} -> {}\n",
551+
std::string(&strtabd[symbol->name]),
552+
std::string(&strtabd[symbol->name] + rplwrap_prefix.length()));
553+
#endif //DEBUG
554+
symbol->name += rplwrap_prefix.length();
555+
}
556+
}
557+
558+
return true;
559+
}
466560

467561
/**
468562
* Fix the loader virtual addresses.
@@ -795,6 +889,11 @@ int main(int argc, char **argv)
795889
return -1;
796890
}
797891

892+
if (!renameRplWrap(elf)) {
893+
fmt::print("ERROR: renameRplWrap failed.\n");
894+
return -1;
895+
}
896+
798897
if (!fixLoaderVirtualAddresses(elf)) {
799898
fmt::print("ERROR: fixLoaderVirtualAddresses failed.\n");
800899
return -1;

src/rplimportgen/rplimportgen.cpp

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "utils.h"
2+
#include "rplwrap.h"
23

34
#include <array>
45
#include <algorithm>
@@ -17,7 +18,9 @@ enum class ReadMode
1718
{
1819
INVALID,
1920
TEXT,
20-
DATA
21+
TEXT_WRAP,
22+
DATA,
23+
DATA_WRAP,
2124
};
2225

2326
void
@@ -26,34 +29,6 @@ writeExports(std::ofstream &out,
2629
bool isData,
2730
const std::vector<std::string> &exports)
2831
{
29-
// Align module name up to 8 bytes
30-
auto moduleNameSize = (moduleName.length() + 1 + 7) & ~7;
31-
32-
// Calculate the data block size
33-
auto exportSecSize = exports.size() * 8;
34-
35-
if (exportSecSize < moduleNameSize) {
36-
exportSecSize = moduleNameSize;
37-
}
38-
39-
// Calculate export hash
40-
uint32_t exportsHash = crc32(0, Z_NULL, 0);
41-
42-
for (auto &exp : exports) {
43-
exportsHash = crc32(exportsHash, reinterpret_cast<const Bytef *>(exp.data()), exp.size() + 1);
44-
}
45-
46-
std::array<Bytef, 0xE> extraHashBytes;
47-
extraHashBytes.fill(0);
48-
exportsHash = crc32(exportsHash, extraHashBytes.data(), extraHashBytes.size());
49-
50-
// Setup section data
51-
std::vector<uint32_t> secData;
52-
secData.resize(exportSecSize / 4, 0);
53-
memcpy(secData.data(), moduleName.c_str(), moduleName.length());
54-
55-
out << std::endl;
56-
5732
if (isData) {
5833
out << ".section .dimport_" << moduleName << ", \"a\", @0x80000002" << std::endl;
5934
} else {
@@ -63,21 +38,43 @@ writeExports(std::ofstream &out,
6338
out << ".align 4" << std::endl;
6439
out << std::endl;
6540

66-
out << ".long " << exports.size() << std::endl;
67-
out << ".long 0x" << std::hex << exportsHash << std::endl;
41+
// Usually the symbol count, but isn't checked on hardware.
42+
// Spoofed to allow ld to garbage-collect later.
43+
out << ".long 1" << std::endl;
44+
// Supposed to be a crc32 of the imports. Again, not actually checked.
45+
out << ".long 0x00000000" << std::endl;
46+
out << std::endl;
47+
48+
// Align module name up to 8 bytes
49+
auto moduleNameSize = (moduleName.length() + 1 + 7) & ~7;
50+
51+
// Setup name data
52+
std::vector<uint32_t> secData;
53+
secData.resize(moduleNameSize / 4, 0);
54+
memcpy(secData.data(), moduleName.c_str(), moduleName.length());
55+
56+
// Add name data
57+
for (uint32_t data : secData) {
58+
out << ".long 0x" << std::hex << byte_swap(data) << std::endl;
59+
}
6860
out << std::endl;
6961

7062
const char *type = isData ? "@object" : "@function";
7163

72-
for (auto i = 0; i < exportSecSize / 8; ++i) {
64+
for (auto i = 0; i < exports.size(); ++i) {
7365
if (i < exports.size()) {
66+
// Basically do -ffunction-sections
67+
if (isData) {
68+
out << ".section .dimport_" << moduleName << "." << exports[i] << ", \"a\", @0x80000002" << std::endl;
69+
} else {
70+
out << ".section .fimport_" << moduleName << "." << exports[i] << ", \"ax\", @0x80000002" << std::endl;
71+
}
7472
out << ".global " << exports[i] << std::endl;
7573
out << ".type " << exports[i] << ", " << type << std::endl;
7674
out << exports[i] << ":" << std::endl;
7775
}
78-
79-
out << ".long 0x" << std::hex << byte_swap(secData[i * 2 + 0]) << std::endl;
80-
out << ".long 0x" << std::hex << byte_swap(secData[i * 2 + 1]) << std::endl;
76+
out << ".long 0x0" << std::endl;
77+
out << ".long 0x0" << std::endl;
8178
out << std::endl;
8279
}
8380
}
@@ -122,8 +119,12 @@ int main(int argc, char **argv)
122119
if (line[0] == ':') {
123120
if (line.substr(1) == "TEXT") {
124121
readMode = ReadMode::TEXT;
122+
} else if (line.substr(1) == "TEXT_WRAP") {
123+
readMode = ReadMode::TEXT_WRAP;
125124
} else if (line.substr(1) == "DATA") {
126125
readMode = ReadMode::DATA;
126+
} else if (line.substr(1) == "DATA_WRAP") {
127+
readMode = ReadMode::DATA_WRAP;
127128
} else if (line.substr(1, 4) == "NAME") {
128129
moduleName = line.substr(6);
129130
} else {
@@ -135,8 +136,12 @@ int main(int argc, char **argv)
135136

136137
if (readMode == ReadMode::TEXT) {
137138
funcExports.push_back(line);
139+
} else if (readMode == ReadMode::TEXT_WRAP) {
140+
funcExports.push_back(std::string(RPLWRAP_PREFIX) + line);
138141
} else if (readMode == ReadMode::DATA) {
139142
dataExports.push_back(line);
143+
} else if (readMode == ReadMode::DATA_WRAP) {
144+
dataExports.push_back(std::string(RPLWRAP_PREFIX) + line);
140145
} else {
141146
std::cout << "Unexpected section data" << std::endl;
142147
return -1;

0 commit comments

Comments
 (0)