Merge pull request #1586 from awake-devel/fix/relinker-keep-eh-frame-hdr
fix(relinker): keep PT_GNU_EH_FRAME in the relinked executable
This commit is contained in:
commit
51f69aa218
|
|
@ -211,6 +211,8 @@ if(BUILD_TESTING)
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_tls_diagnostics.py $<TARGET_FILE:relinker>)
|
||||
add_test(NAME linux_load_alignment COMMAND ${Python3_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_linux_load_alignment.py $<TARGET_FILE:relinker>)
|
||||
add_test(NAME linux_eh_frame_header COMMAND ${Python3_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_linux_eh_frame_header.py $<TARGET_FILE:relinker>)
|
||||
add_test(NAME linux_dynamic_strings COMMAND ${Python3_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_linux_dynamic_strings.py $<TARGET_FILE:relinker>)
|
||||
add_test(NAME executable_segments COMMAND ${Python3_EXECUTABLE}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ inline constexpr std::uint32_t PT_DYNAMIC = 2;
|
|||
inline constexpr std::uint32_t PT_INTERP = 3;
|
||||
inline constexpr std::uint32_t PT_NOTE = 4;
|
||||
inline constexpr std::uint32_t PT_PHDR = 6;
|
||||
inline constexpr std::uint32_t PT_GNU_EH_FRAME = 0x6474e550;
|
||||
inline constexpr std::uint32_t PT_GNU_RELRO = 0x6474e552;
|
||||
inline constexpr std::uint32_t PF_X = 0x1;
|
||||
inline constexpr std::uint32_t PF_W = 0x2;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include <elfpatcher/general/ElfConstants.hpp>
|
||||
#include <domain/Types.hpp>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace Elfpatcher {
|
||||
|
|
@ -131,10 +132,19 @@ std::uint16_t ProgramHeaderLayoutBuilder::WriteLayout(
|
|||
const ProgramHeaderLayoutRequest& request
|
||||
) const {
|
||||
std::uint16_t keptCount = 0;
|
||||
std::uint16_t frameCount = 0;
|
||||
for (const auto& ph : request.OriginalHeaders) {
|
||||
if (_segmentFilter->ShouldSkip(ph))
|
||||
continue;
|
||||
keptCount++;
|
||||
if (ph.Type == PT_GNU_EH_FRAME)
|
||||
frameCount++;
|
||||
}
|
||||
|
||||
const bool keepFrames = keptCount + kSyntheticProgramHeaderCount < request.PhNum;
|
||||
if (!keepFrames && frameCount > 0) {
|
||||
keptCount -= frameCount;
|
||||
std::cerr << "WARNING: No free program header slot for PT_GNU_EH_FRAME; C++ exceptions thrown in the executable cannot be caught.\n";
|
||||
}
|
||||
|
||||
const std::uint16_t neededPh = keptCount + kSyntheticProgramHeaderCount;
|
||||
|
|
@ -175,7 +185,7 @@ std::uint16_t ProgramHeaderLayoutBuilder::WriteLayout(
|
|||
writtenPh++;
|
||||
|
||||
for (const auto& ph : request.OriginalHeaders) {
|
||||
if (_segmentFilter->ShouldSkip(ph))
|
||||
if (_segmentFilter->ShouldSkip(ph) || (!keepFrames && ph.Type == PT_GNU_EH_FRAME))
|
||||
continue;
|
||||
const std::size_t phEntOff = static_cast<std::size_t>(request.PhOff) + writtenPh * request.PhEntSize;
|
||||
if (ph.Type == PT_LOAD) {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ bool SegmentFilter::_isSceSpecificSegment(const std::uint32_t type) const {
|
|||
|
||||
bool SegmentFilter::ShouldSkip(const Domain::ProgramHeader& ph) const {
|
||||
if (ph.Type == PT_OS_PROCPARAM) return false;
|
||||
if (ph.Type == PT_GNU_EH_FRAME) return false;
|
||||
if (_isSceSpecificSegment(ph.Type)) return true;
|
||||
if (ph.Type == PT_DYNAMIC) return true;
|
||||
if (ph.Type == PT_NOTE && ph.MappedAddress == 0 && ph.FileSize > 0) return true;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
from pathlib import Path
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
PT_LOAD = 1
|
||||
PT_DYNAMIC = 2
|
||||
PT_GNU_EH_FRAME = 0x6474E550
|
||||
PT_SCE_VERSION = 0x6FFFFF01
|
||||
EH_FRAME_HEADER = (PT_GNU_EH_FRAME, 4, 0x4800, 0x800, 0x800, 16, 16, 4)
|
||||
|
||||
|
||||
def fixture(header_count):
|
||||
image = bytearray(0x8000)
|
||||
image[:16] = b"\x7fELF\x02\x01\x01" + bytes(9)
|
||||
struct.pack_into("<HHIQQQIHHHHHH", image, 16,
|
||||
3, 62, 1, 0x4000, 64, 0, 0, 64, 56, header_count, 64, 0, 0)
|
||||
image[0x4000:0x4006] = b"\xb8\x2a\x00\x00\x00\xc3"
|
||||
tags = [(5, 0x600), (10, 1), (6, 0x620), (11, 24), (7, 0x700), (8, 0), (9, 24), (0, 0)]
|
||||
struct.pack_into("<IIQQQQQQ", image, 64,
|
||||
PT_LOAD, 5, 0x4000, 0, 0, 0x1000, 0x1000, 0x4000)
|
||||
struct.pack_into("<IIQQQQQQ", image, 120,
|
||||
PT_DYNAMIC, 6, 0x600 + 0x4000, 0x600, 0x600, len(tags) * 16, len(tags) * 16, 8)
|
||||
struct.pack_into("<IIQQQQQQ", image, 176, *EH_FRAME_HEADER)
|
||||
for index in range(3, header_count):
|
||||
struct.pack_into("<IIQQQQQQ", image, 64 + index * 56, PT_SCE_VERSION, 0, 0, 0, 0, 0, 0, 1)
|
||||
for index, tag in enumerate(tags):
|
||||
struct.pack_into("<qQ", image, 0x4600 + index * 16, *tag)
|
||||
struct.pack_into("<BBBBQI", image, 0x4800, 1, 0, 3, 0, 0x900, 0)
|
||||
return image
|
||||
|
||||
|
||||
def program_headers(elf):
|
||||
phoff, = struct.unpack_from("<Q", elf, 0x20)
|
||||
phentsize, phnum = struct.unpack_from("<HH", elf, 0x36)
|
||||
return [struct.unpack_from("<IIQQQQQQ", elf, phoff + index * phentsize) for index in range(phnum)]
|
||||
|
||||
|
||||
def relink(relinker, directory, name, image):
|
||||
source = Path(directory) / (name + ".elf")
|
||||
output = Path(directory) / (name + ".out")
|
||||
source.write_bytes(image)
|
||||
result = subprocess.run([str(relinker), "--skip-sce-module", str(source), str(output)], capture_output=True, text=True, timeout=20)
|
||||
assert result.returncode == 0, (name, result.returncode, result.stdout, result.stderr)
|
||||
return result, output.read_bytes()
|
||||
|
||||
|
||||
def main():
|
||||
relinker = Path(sys.argv[1]).resolve()
|
||||
with tempfile.TemporaryDirectory(prefix="anyps5-eh-frame-") as directory:
|
||||
image = fixture(7)
|
||||
result, elf = relink(relinker, directory, "spare-slot", image)
|
||||
assert "PT_GNU_EH_FRAME" not in result.stderr, result.stderr
|
||||
headers = program_headers(elf)
|
||||
frames = [header for header in headers if header[0] == PT_GNU_EH_FRAME]
|
||||
assert frames == [EH_FRAME_HEADER], ("PT_GNU_EH_FRAME must be kept unchanged", headers)
|
||||
offset, size = EH_FRAME_HEADER[2], EH_FRAME_HEADER[5]
|
||||
assert elf[offset:offset + size] == image[offset:offset + size], "eh_frame_hdr bytes changed"
|
||||
assert all(header[0] != PT_SCE_VERSION for header in headers), headers
|
||||
|
||||
for header_count in (5, 6):
|
||||
result, elf = relink(relinker, directory, "full-slots-" + str(header_count), fixture(header_count))
|
||||
headers = program_headers(elf)
|
||||
assert all(header[0] != PT_GNU_EH_FRAME for header in headers), (header_count, headers)
|
||||
assert len(headers) == 6, (header_count, headers)
|
||||
assert "No free program header slot for PT_GNU_EH_FRAME" in result.stderr, (header_count, result.stderr)
|
||||
print("Linux eh_frame header test passed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue