Merge pull request #4621 from MiniRML/fix/nid-elf-shared-string-roles
fix(nid): preserve each ELF string consumer independently
This commit is contained in:
commit
72a8e67122
|
|
@ -228,6 +228,10 @@ if(BUILD_TESTING)
|
|||
target_link_libraries(guest_offline_imports_tests PRIVATE libSceHttp libSceNpEntitlementAccess libSceRudp libc)
|
||||
configure_windows_unwind(guest_offline_imports_tests)
|
||||
add_test(NAME guest_offline_imports COMMAND guest_offline_imports_tests)
|
||||
if(Python3_Interpreter_FOUND)
|
||||
add_test(NAME nid_shared_strings COMMAND ${Python3_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/nid/tests/test_shared_strings.py $<TARGET_FILE:nid_patcher>)
|
||||
endif()
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_offline_imports PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceHttp>")
|
||||
|
|
|
|||
|
|
@ -427,7 +427,7 @@ void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string&
|
|||
const auto addName = [&](std::uint32_t oldOffset, std::string value) {
|
||||
const auto existing = std::find_if(
|
||||
names.begin(), names.end(),
|
||||
[&](const auto& entry) { return entry.first == oldOffset; }
|
||||
[&](const auto& entry) { return entry.first == oldOffset && entry.second == value; }
|
||||
);
|
||||
if (existing == names.end()) names.emplace_back(oldOffset, std::move(value));
|
||||
};
|
||||
|
|
@ -463,8 +463,9 @@ void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string&
|
|||
newDynStr.push_back(0u);
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::uint32_t, std::uint32_t>> oldToNewOffset;
|
||||
for (const auto& name : names) oldToNewOffset.emplace_back(name.first, valueOffsets.at(name.second));
|
||||
std::vector<std::pair<std::uint32_t, std::uint32_t>> preservedToNewOffset;
|
||||
for (const auto offset : preservedOffsets)
|
||||
preservedToNewOffset.emplace_back(offset, valueOffsets.at(ReadCStr(origDynStr, offset)));
|
||||
|
||||
if (newDynStr.size() > dynStrSize) {
|
||||
throw std::runtime_error(
|
||||
|
|
@ -475,25 +476,15 @@ void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string&
|
|||
newDynStr.resize(dynStrSize, 0u);
|
||||
|
||||
std::vector<std::string> newSymbolNames(symCount);
|
||||
for (std::size_t i = 1u; i < symCount; ++i) {
|
||||
const std::size_t symOffset = dynSymOffset + i * sizeof(Elf64_Sym);
|
||||
const auto sym = Read<Elf64_Sym>(elf, symOffset);
|
||||
if (sym.st_name == 0u) continue;
|
||||
|
||||
const auto mapped = std::find_if(
|
||||
oldToNewOffset.begin(), oldToNewOffset.end(),
|
||||
[&](const auto& entry) { return entry.first == sym.st_name; }
|
||||
);
|
||||
if (mapped == oldToNewOffset.end()) continue;
|
||||
|
||||
newSymbolNames[i] = ReadCStr(newDynStr, mapped->second);
|
||||
Write(elf, symOffset + offsetof(Elf64_Sym, st_name), mapped->second);
|
||||
for (const auto& use : uses) {
|
||||
newSymbolNames[use.symIndex] = use.newValue;
|
||||
Write(elf, dynSymOffset + use.symIndex * sizeof(Elf64_Sym) + offsetof(Elf64_Sym, st_name), valueOffsets.at(use.newValue));
|
||||
}
|
||||
|
||||
std::memcpy(elf.data() + dynStrOffset, newDynStr.data(), newDynStr.size());
|
||||
|
||||
RemapDynamicNeededOffsets(elf, ehdr, oldToNewOffset);
|
||||
RemapVerneedOffsets(elf, ehdr, oldToNewOffset);
|
||||
RemapDynamicNeededOffsets(elf, ehdr, preservedToNewOffset);
|
||||
RemapVerneedOffsets(elf, ehdr, preservedToNewOffset);
|
||||
|
||||
if (gnuHashOffset != 0u) {
|
||||
const auto bucketCount = Read<std::uint32_t>(elf, gnuHashOffset);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
import struct
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from test_symbol_versions import build_image, symbols, patch, SHOFF, DYNSYM_OFFSET, read_dynsym, sections, DEFINITIONS
|
||||
|
||||
|
||||
class SharedStringTests(unittest.TestCase):
|
||||
def test_dynamic_names_keep_original_string_when_export_shares_offset(self):
|
||||
for tag in (1, 14, 15, 29):
|
||||
with self.subTest(tag=tag):
|
||||
data = build_image(symbols(False), False)
|
||||
name_offset = struct.unpack_from("<I", data, DYNSYM_OFFSET + 3 * 24)[0]
|
||||
struct.pack_into("<H", data, 0x3c, 5)
|
||||
struct.pack_into("<IIQQQQIIQQ", data, SHOFF + 4 * 64, 0, 6, 2, 0, 0x580, 32, 2, 0, 8, 16)
|
||||
struct.pack_into("<qQqQ", data, 0x580, tag, name_offset, 0, 0)
|
||||
result, after = patch(PATCHER, bytes(data), "shared-dynamic.so")
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
strings = sections(after)[2]
|
||||
new_offset = struct.unpack_from("<Q", after, 0x588)[0]
|
||||
start = strings[4] + new_offset
|
||||
self.assertEqual(after[start:after.index(0, start)].decode(), DEFINITIONS[0][0])
|
||||
renamed = next(entry[0] for entry in read_dynsym(after) if entry[2] == DEFINITIONS[0][2])
|
||||
self.assertNotEqual(renamed, DEFINITIONS[0][0])
|
||||
|
||||
def test_version_requirement_strings_keep_original_names(self):
|
||||
data = build_image(symbols(False), False)
|
||||
name_offset = struct.unpack_from("<I", data, DYNSYM_OFFSET + 3 * 24)[0]
|
||||
struct.pack_into("<H", data, 0x3c, 5)
|
||||
struct.pack_into("<IIQQQQIIQQ", data, SHOFF + 4 * 64, 0, 0x6ffffffe, 2, 0, 0x580, 32, 2, 1, 8, 0)
|
||||
struct.pack_into("<HHIII", data, 0x580, 1, 1, name_offset, 16, 0)
|
||||
struct.pack_into("<IHHII", data, 0x590, 0, 0, 2, name_offset, 0)
|
||||
result, after = patch(PATCHER, bytes(data), "shared-version.so")
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
strings = sections(after)[2]
|
||||
for field in (0x584, 0x598):
|
||||
offset = struct.unpack_from("<I", after, field)[0]
|
||||
start = strings[4] + offset
|
||||
self.assertEqual(after[start:after.index(0, start)].decode(), DEFINITIONS[0][0])
|
||||
renamed = next(entry[0] for entry in read_dynsym(after) if entry[2] == DEFINITIONS[0][2])
|
||||
self.assertNotEqual(renamed, DEFINITIONS[0][0])
|
||||
|
||||
def test_local_symbol_keeps_name_shared_with_renamed_global(self):
|
||||
entries = symbols(False)
|
||||
entries[1] = (DEFINITIONS[0][0], 1, 9, 2, 0x1500)
|
||||
data = build_image(entries, False)
|
||||
data[DYNSYM_OFFSET + 24 + 4] = 2
|
||||
result, after = patch(PATCHER, bytes(data), "shared-local.so")
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
names = {value: name for name, _, value in read_dynsym(after)}
|
||||
self.assertEqual(names[0x1500], DEFINITIONS[0][0])
|
||||
self.assertNotEqual(names[DEFINITIONS[0][2]], DEFINITIONS[0][0])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
PATCHER = Path(sys.argv.pop(1)).resolve()
|
||||
unittest.main()
|
||||
Loading…
Reference in New Issue