fix(relinker): hand the guest the real argv on Linux
The Linux entry stub built a copy of the entry parameter block holding argc and a pointer to the host argv array, so the guest read that pointer as argv[0] and uninitialised stack as argv[1..]. The process stack at entry already has the guest's layout (argc as a qword, then the argv pointers and a null), so pass rsp itself. Test: linux_entry_argv relinks a fixture whose entry checks argc and argv and runs the output on Linux x86-64.
This commit is contained in:
parent
7d40a57d18
commit
d8b8aafc65
|
|
@ -180,6 +180,8 @@ if(BUILD_TESTING)
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_tls_function_coverage.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_entry_argv COMMAND ${Python3_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_linux_entry_argv.py $<TARGET_FILE:relinker>)
|
||||
add_test(NAME windows_gui COMMAND ${Python3_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_windows_gui.py $<TARGET_FILE:relinker>)
|
||||
add_test(NAME guest_intel_trampolines COMMAND ${Python3_EXECUTABLE}
|
||||
|
|
|
|||
|
|
@ -115,12 +115,7 @@ inline constexpr std::uint32_t kDynSymInfoFirstGlobal = 1;
|
|||
inline constexpr std::uint64_t kDynSymAlign = 8;
|
||||
inline constexpr std::uint32_t kDynamicLinkToStrTab = 2;
|
||||
|
||||
inline constexpr std::uint8_t kStubOpPopRax = 0x58;
|
||||
inline constexpr std::uint8_t kStubOpMovRbxRsp[] = {0x48, 0x89, 0xe3};
|
||||
inline constexpr std::uint8_t kStubOpSubRsp0x30[] = {0x48, 0x83, 0xec, 0x30};
|
||||
inline constexpr std::uint8_t kStubOpAndRsp0xf0[] = {0x48, 0x83, 0xe4, 0xf0};
|
||||
inline constexpr std::uint8_t kStubOpMovDwordPtrRsp[] = {0x89, 0x04, 0x24};
|
||||
inline constexpr std::uint8_t kStubOpMovQwordPtrRsp8Rbx[] = {0x48, 0x89, 0x5c, 0x24, 0x08};
|
||||
inline constexpr std::uint8_t kStubOpMovRdiRsp[] = {0x48, 0x89, 0xe7};
|
||||
inline constexpr std::uint8_t kStubOpXorRsiRsi[] = {0x48, 0x31, 0xf6};
|
||||
inline constexpr std::uint8_t kStubOpCallRel32 = 0xe8;
|
||||
|
|
|
|||
|
|
@ -15,13 +15,8 @@ std::vector<std::uint8_t> EntryStubBuilder::BuildEntryStub(
|
|||
const std::uint64_t realEntryVaddr
|
||||
) const {
|
||||
std::vector<std::uint8_t> s;
|
||||
s.push_back(kStubOpPopRax);
|
||||
_appendBytes(s, kStubOpMovRbxRsp, sizeof(kStubOpMovRbxRsp));
|
||||
_appendBytes(s, kStubOpSubRsp0x30, sizeof(kStubOpSubRsp0x30));
|
||||
_appendBytes(s, kStubOpAndRsp0xf0, sizeof(kStubOpAndRsp0xf0));
|
||||
_appendBytes(s, kStubOpMovDwordPtrRsp, sizeof(kStubOpMovDwordPtrRsp));
|
||||
_appendBytes(s, kStubOpMovQwordPtrRsp8Rbx, sizeof(kStubOpMovQwordPtrRsp8Rbx));
|
||||
_appendBytes(s, kStubOpMovRdiRsp, sizeof(kStubOpMovRdiRsp));
|
||||
_appendBytes(s, kStubOpAndRsp0xf0, sizeof(kStubOpAndRsp0xf0));
|
||||
_appendBytes(s, kStubOpXorRsiRsi, sizeof(kStubOpXorRsiRsi));
|
||||
const std::uint64_t callInsnVaddr = stubVaddr + s.size();
|
||||
const std::uint64_t callNextVaddr = callInsnVaddr + kStubCallInstructionSize;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
"""Check that a Linux relink hands the guest entry the process argc and inline argv array."""
|
||||
|
||||
from pathlib import Path
|
||||
import platform
|
||||
import signal
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from test_linux_load_alignment import fixture
|
||||
|
||||
ENTRY = 0x10
|
||||
ARGV_CHECK = bytes.fromhex(
|
||||
"833f02" "751e"
|
||||
"48837f0800" "7417"
|
||||
"488b4710"
|
||||
"80385a" "750e"
|
||||
"80780100" "7508"
|
||||
"48837f1800" "7501"
|
||||
"cc"
|
||||
"0f0b")
|
||||
|
||||
|
||||
def argv_fixture():
|
||||
image = fixture()
|
||||
struct.pack_into("<Q", image, 24, ENTRY)
|
||||
image[0x4000 + ENTRY:0x4000 + ENTRY + len(ARGV_CHECK)] = ARGV_CHECK
|
||||
return image
|
||||
|
||||
|
||||
def main():
|
||||
relinker = Path(sys.argv[1]).resolve()
|
||||
with tempfile.TemporaryDirectory(prefix="anyps5-argv-") as directory:
|
||||
source = Path(directory) / "input.elf"
|
||||
output = Path(directory) / "output.elf"
|
||||
source.write_bytes(argv_fixture())
|
||||
result = subprocess.run([str(relinker), "--skip-sce-module", str(source), str(output)],
|
||||
capture_output=True, text=True, timeout=20)
|
||||
assert result.returncode == 0, (result.stdout, result.stderr)
|
||||
if sys.platform.startswith("linux") and platform.machine() in ("x86_64", "AMD64"):
|
||||
output.chmod(0o755)
|
||||
for arguments, expected in ((["Z"], -signal.SIGTRAP), (["Z", "extra"], -signal.SIGILL)):
|
||||
executed = subprocess.run([str(output), *arguments], capture_output=True, timeout=20)
|
||||
assert executed.returncode == expected, (arguments, executed.returncode)
|
||||
print("Linux entry argv test passed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue