Merge pull request #2618 from oneandonlydean/fix/carry-2549
fix(relinker): leave zero-filled PT_LOAD memory out of Windows files
This commit is contained in:
commit
05e884ca08
|
|
@ -253,6 +253,8 @@ if(BUILD_TESTING)
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_windows_working_directory.py $<TARGET_FILE:relinker>)
|
||||
add_test(NAME windows_address_space COMMAND ${Python3_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_windows_address_space.py $<TARGET_FILE:relinker>)
|
||||
add_test(NAME windows_bss COMMAND ${Python3_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_windows_bss.py $<TARGET_FILE:relinker>)
|
||||
add_test(NAME windows_icon COMMAND ${Python3_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_windows_icon.py $<TARGET_FILE:relinker>)
|
||||
add_test(NAME guest_intel_trampolines COMMAND ${Python3_EXECUTABLE}
|
||||
|
|
|
|||
|
|
@ -44,8 +44,9 @@ std::vector<std::uint8_t> WindowsPeWriter::Write(const std::vector<PeSection>& s
|
|||
const auto& section = sections[index];
|
||||
if (section.Name.empty() || section.Name.size() > 8 || section.Data.empty() || section.Rva != endRva)
|
||||
throw Domain::RelinkerException("Invalid PE section layout: " + section.Name, section.Rva);
|
||||
const auto rawSize = Io::AlignUp(CheckedRva(section.Data.size()), FileAlignment);
|
||||
const auto rawOffset = CheckedRva(result.size());
|
||||
const auto initialized = static_cast<std::size_t>(std::find_if(section.Data.rbegin(), section.Data.rend(), [](const std::uint8_t value) { return value != 0; }).base() - section.Data.begin());
|
||||
const auto rawSize = Io::AlignUp(CheckedRva(initialized), FileAlignment);
|
||||
const auto rawOffset = rawSize == 0 ? 0u : CheckedRva(result.size());
|
||||
const auto header = sectionTable + index * 40;
|
||||
std::copy(section.Name.begin(), section.Name.end(), result.begin() + static_cast<std::ptrdiff_t>(header));
|
||||
Io::WriteU32(result, header + 8, CheckedRva(section.Data.size()));
|
||||
|
|
@ -61,8 +62,10 @@ std::vector<std::uint8_t> WindowsPeWriter::Write(const std::vector<PeSection>& s
|
|||
} else {
|
||||
dataSize = CheckedRva(static_cast<std::uint64_t>(dataSize) + rawSize);
|
||||
}
|
||||
result.insert(result.end(), section.Data.begin(), section.Data.end());
|
||||
result.resize(static_cast<std::size_t>(rawOffset) + rawSize);
|
||||
if (rawSize != 0) {
|
||||
result.insert(result.end(), section.Data.begin(), section.Data.begin() + static_cast<std::ptrdiff_t>(initialized));
|
||||
result.resize(static_cast<std::size_t>(rawOffset) + rawSize);
|
||||
}
|
||||
endRva = AlignRva(section.Rva + static_cast<std::uint64_t>(section.Data.size()));
|
||||
}
|
||||
if (!foundEntry)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
"""Leave the zero-filled tail of PT_LOAD memory out of the Windows file and let the loader zero-fill it."""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from test_optional_plt import fixture
|
||||
|
||||
BSS = 64 << 20
|
||||
FILE_ALIGNMENT = 0x200
|
||||
SECTION_ALIGNMENT = 0x1000
|
||||
LOAD_RVA = 0x10000
|
||||
|
||||
|
||||
def sections(pe):
|
||||
header = struct.unpack_from("<I", pe, 0x3c)[0]
|
||||
count = struct.unpack_from("<H", pe, header + 6)[0]
|
||||
optional = header + 24
|
||||
table = optional + struct.unpack_from("<H", pe, header + 20)[0]
|
||||
result = []
|
||||
for index in range(count):
|
||||
offset = table + index * 40
|
||||
name = pe[offset:offset + 8].rstrip(b"\0").decode()
|
||||
virtual_size, rva, raw_size, raw = struct.unpack_from("<IIII", pe, offset + 8)
|
||||
result.append((name, virtual_size, rva, raw_size, raw))
|
||||
return result, struct.unpack_from("<I", pe, optional + 56)[0]
|
||||
|
||||
|
||||
def main():
|
||||
relinker = Path(sys.argv[1]).resolve()
|
||||
with tempfile.TemporaryDirectory(prefix="anyps5-windows-bss-") as directory:
|
||||
work = Path(directory)
|
||||
image = fixture()
|
||||
memory_size = struct.unpack_from("<Q", image, 64 + 40)[0] + BSS
|
||||
struct.pack_into("<Q", image, 64 + 40, memory_size)
|
||||
last = memory_size - 4
|
||||
image[0x210:0x21a] = b"\x8b\x05" + struct.pack("<i", last - 0x216) + b"\x83\xc0\x2a\xc3"
|
||||
source = work / "input.elf"
|
||||
source.write_bytes(image)
|
||||
output = work / "output.exe"
|
||||
result = subprocess.run([str(relinker), "--skip-sce-module", "--windows", str(source), str(output)],
|
||||
capture_output=True, text=True, timeout=20)
|
||||
assert result.returncode == 0, (result.stdout, result.stderr)
|
||||
pe = output.read_bytes()
|
||||
assert len(pe) < BSS // 16, f"{len(pe)} byte executable for {BSS} bytes of zero-filled memory"
|
||||
table, image_size = sections(pe)
|
||||
first = next(section for section in table if section[2] == LOAD_RVA)
|
||||
name, virtual_size, rva, raw_size, raw = first
|
||||
assert virtual_size >= memory_size and rva + virtual_size <= image_size, first
|
||||
assert 0 < raw_size < SECTION_ALIGNMENT * 2, first
|
||||
for name, virtual_size, rva, raw_size, raw in table:
|
||||
assert raw_size % FILE_ALIGNMENT == 0 and raw % FILE_ALIGNMENT == 0, name
|
||||
assert raw_size <= -(-virtual_size // FILE_ALIGNMENT) * FILE_ALIGNMENT, name
|
||||
assert (raw_size == 0) == (raw == 0) and raw + raw_size <= len(pe), name
|
||||
mapped = bytearray(image_size)
|
||||
for name, virtual_size, rva, raw_size, raw in table:
|
||||
size = min(raw_size, virtual_size)
|
||||
mapped[rva:rva + size] = pe[raw:raw + size]
|
||||
loaded = mapped[LOAD_RVA:LOAD_RVA + memory_size]
|
||||
assert loaded[0x210:0x21a] == image[0x210:0x21a]
|
||||
assert not any(loaded[0x1000:])
|
||||
if os.name == "nt":
|
||||
executed = subprocess.run([str(output)], capture_output=True, text=True, timeout=20)
|
||||
assert executed.returncode == 42, f"exit code {executed.returncode:#x}"
|
||||
print("Windows zero-filled memory tests passed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue