Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include <elfpatcher/general/ProgramHeaderLayoutBuilder.hpp>
#include <elfpatcher/general/ElfConstants.hpp>
#include <domain/Types.hpp>
#include <io/BufferUtils.hpp>
#include <cstdint>
#include <limits>
#include <string>

namespace Elfpatcher {
Expand Down Expand Up @@ -108,6 +110,8 @@ std::uint64_t ProgramHeaderLayoutBuilder::ComputeExtraBlockVaddr(
if (ph.Type != PT_LOAD)
continue;
foundLoad = true;
if (ph.MemorySize > std::numeric_limits<std::uint64_t>::max() - ph.MappedAddress)
throw Domain::RelinkerException("PT_LOAD virtual address range overflows", ph.Offset);
const std::uint64_t vaddrEnd = ph.MappedAddress + ph.MemorySize;
if (vaddrEnd > highestVaddrEnd)
highestVaddrEnd = vaddrEnd;
Expand Down Expand Up @@ -149,8 +153,16 @@ std::uint16_t ProgramHeaderLayoutBuilder::WriteLayout(
if (ph.Type == PT_LOAD && !_segmentFilter->ShouldSkip(ph) && ph.Alignment > headerBlockAlign && (ph.Alignment & (ph.Alignment - 1)) == 0)
headerBlockAlign = ph.Alignment;
}
const std::uint64_t headerBlockVaddr =
(request.ExtraBlockVaddr + request.ExtraBlockSize + headerBlockAlign - 1) & ~(headerBlockAlign - 1);
if (request.ExtraBlockSize > std::numeric_limits<std::uint64_t>::max() - request.ExtraBlockVaddr)
throw Domain::RelinkerException("Extra block virtual address range overflows");
std::uint64_t headerBlockVaddr;
try {
headerBlockVaddr = Io::AlignUp64(request.ExtraBlockVaddr + request.ExtraBlockSize, headerBlockAlign);
} catch (const std::overflow_error&) {
throw Domain::RelinkerException("Header block virtual address alignment overflows");
}
if (headerBlockSize > std::numeric_limits<std::uint64_t>::max() - headerBlockVaddr)
throw Domain::RelinkerException("Header block virtual address range overflows");

if (request.DynamicSegmentOffset < request.ExtraBlockOffset)
throw Domain::RelinkerException("Dynamic segment offset lies before the extra block");
Expand Down
38 changes: 38 additions & 0 deletions core/relinker/relinker/tests/test_linux_load_alignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,44 @@ def main():
assert warning.returncode == 0, (warning.stdout, warning.stderr)
assert "--windows was not specified" in warning.stderr, warning.stderr
assert named_exe.read_bytes().startswith(b"\x7fELF"), "The filename must not select the output format"

maximum = (1 << 64) - 1

def bss_fixture(address, size, alignment=0x1000, file_size=0x8000, header_offset=64):
data = fixture()
data.extend(bytes(file_size - len(data)))
struct.pack_into("<H", data, 0x38, 6)
struct.pack_into("<IIQQQQQQ", data, 176,
PT_LOAD, 4, 0, address, address, 0, size, alignment)
struct.pack_into("<IIQQQQQQ", data, 64 + 5 * 56,
PT_SCE_VERSION, 0, 0, 0, 0, 0, 0, 1)
if header_offset != 64:
data[header_offset:header_offset + 6 * 56] = data[64:64 + 6 * 56]
struct.pack_into("<Q", data, 0x20, header_offset)
return data

cases = (
("valid-bss", bss_fixture(0x2000, 0x4000), None),
("valid-high-address", bss_fixture(0x2000, maximum - 0x11fff), None),
("load-range-wrap", bss_fixture(0x2000, maximum), "PT_LOAD virtual address range overflows"),
("extra-range-wrap", bss_fixture(0x2000, maximum - 0x2080, file_size=0x8ff5),
"Extra block virtual address range overflows"),
("header-alignment-wrap", bss_fixture(0, 1 << 63, 1 << 63),
"Header block virtual address alignment overflows"),
("header-range-wrap", bss_fixture(0x2000, maximum - 0x9fff, header_offset=0x4000),
"Header block virtual address range overflows"),
)
for name, data, error in cases:
source = Path(directory) / (name + ".elf")
output = Path(directory) / (name + ".out")
source.write_bytes(data)
result = subprocess.run([str(relinker), "--skip-sce-module", str(source), str(output)],
capture_output=True, text=True, timeout=20)
if error is None:
assert result.returncode == 0 and output.exists(), (name, result)
assert all(segment[3] + segment[6] <= maximum for segment in loads(output.read_bytes()))
else:
assert result.returncode == 2 and error in result.stderr and not output.exists(), (name, result)
print("Linux load alignment test passed")


Expand Down
Loading