mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-22 19:06:15 +08:00
Fix sceNetHtonl/Htons/Ntohl/Ntohs discarding their converted result (#211)
Each of the four byte-order helpers computed the swap into Rax and then returned via ctx.SetReturn(0). SetReturn writes its argument into Rax, so it immediately overwrote the converted value with 0 and the guest saw every sceNetHtonl / sceNetHtons / sceNetNtohl / sceNetNtohs call return 0. Leave the swapped value in Rax and return ORBIS_GEN2_OK as the dispatch status instead, matching how the value-returning exports in this file (e.g. sceNetPoolCreate) already work. Add a NetExports test suite covering the swaps, the 16-bit width masking, an htonl/ntohl round-trip, and a regression guard that a non-zero input never converts to 0.
This commit is contained in:
@@ -344,8 +344,10 @@ public static class NetExports
|
|||||||
public static int NetHtonl(CpuContext ctx)
|
public static int NetHtonl(CpuContext ctx)
|
||||||
{
|
{
|
||||||
var value = unchecked((uint)ctx[CpuRegister.Rdi]);
|
var value = unchecked((uint)ctx[CpuRegister.Rdi]);
|
||||||
|
// The byte-swapped result is the return value and already lives in Rax; return OK as the
|
||||||
|
// dispatch status without going through SetReturn, which would overwrite Rax with 0.
|
||||||
ctx[CpuRegister.Rax] = BinaryPrimitives.ReverseEndianness(value);
|
ctx[CpuRegister.Rax] = BinaryPrimitives.ReverseEndianness(value);
|
||||||
return ctx.SetReturn(0);
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
@@ -356,8 +358,10 @@ public static class NetExports
|
|||||||
public static int NetHtons(CpuContext ctx)
|
public static int NetHtons(CpuContext ctx)
|
||||||
{
|
{
|
||||||
var value = unchecked((ushort)ctx[CpuRegister.Rdi]);
|
var value = unchecked((ushort)ctx[CpuRegister.Rdi]);
|
||||||
|
// The byte-swapped result is the return value and already lives in Rax; return OK as the
|
||||||
|
// dispatch status without going through SetReturn, which would overwrite Rax with 0.
|
||||||
ctx[CpuRegister.Rax] = BinaryPrimitives.ReverseEndianness(value);
|
ctx[CpuRegister.Rax] = BinaryPrimitives.ReverseEndianness(value);
|
||||||
return ctx.SetReturn(0);
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
@@ -368,8 +372,10 @@ public static class NetExports
|
|||||||
public static int NetNtohl(CpuContext ctx)
|
public static int NetNtohl(CpuContext ctx)
|
||||||
{
|
{
|
||||||
var value = unchecked((uint)ctx[CpuRegister.Rdi]);
|
var value = unchecked((uint)ctx[CpuRegister.Rdi]);
|
||||||
|
// The byte-swapped result is the return value and already lives in Rax; return OK as the
|
||||||
|
// dispatch status without going through SetReturn, which would overwrite Rax with 0.
|
||||||
ctx[CpuRegister.Rax] = BinaryPrimitives.ReverseEndianness(value);
|
ctx[CpuRegister.Rax] = BinaryPrimitives.ReverseEndianness(value);
|
||||||
return ctx.SetReturn(0);
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
@@ -380,8 +386,10 @@ public static class NetExports
|
|||||||
public static int NetNtohs(CpuContext ctx)
|
public static int NetNtohs(CpuContext ctx)
|
||||||
{
|
{
|
||||||
var value = unchecked((ushort)ctx[CpuRegister.Rdi]);
|
var value = unchecked((ushort)ctx[CpuRegister.Rdi]);
|
||||||
|
// The byte-swapped result is the return value and already lives in Rax; return OK as the
|
||||||
|
// dispatch status without going through SetReturn, which would overwrite Rax with 0.
|
||||||
ctx[CpuRegister.Rax] = BinaryPrimitives.ReverseEndianness(value);
|
ctx[CpuRegister.Rax] = BinaryPrimitives.ReverseEndianness(value);
|
||||||
return ctx.SetReturn(0);
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
using SharpEmu.HLE;
|
||||||
|
using SharpEmu.Libs.Network;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace SharpEmu.Libs.Tests.Network;
|
||||||
|
|
||||||
|
// The libSceNet byte-order helpers take their operand in Rdi and return the converted value in
|
||||||
|
// Rax. They swap endianness unconditionally, which is correct on the little-endian hosts (and
|
||||||
|
// little-endian guest) the emulator targets, so network (big-endian) order is always a byte swap.
|
||||||
|
public sealed class NetExportsTests
|
||||||
|
{
|
||||||
|
private readonly CpuContext _ctx = new(new FakeCpuMemory(0x1_0000_0000, 0x1000), Generation.Gen5);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Htonl_SwapsAllFourBytes()
|
||||||
|
{
|
||||||
|
_ctx[CpuRegister.Rdi] = 0x01020304;
|
||||||
|
|
||||||
|
Assert.Equal(0, NetExports.NetHtonl(_ctx));
|
||||||
|
Assert.Equal(0x04030201UL, _ctx[CpuRegister.Rax]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Ntohl_SwapsAllFourBytes()
|
||||||
|
{
|
||||||
|
_ctx[CpuRegister.Rdi] = 0x01020304;
|
||||||
|
|
||||||
|
Assert.Equal(0, NetExports.NetNtohl(_ctx));
|
||||||
|
Assert.Equal(0x04030201UL, _ctx[CpuRegister.Rax]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Htons_SwapsLowTwoBytesOnly()
|
||||||
|
{
|
||||||
|
// High bits above the 16-bit short must be ignored, not folded into the result.
|
||||||
|
_ctx[CpuRegister.Rdi] = 0xFFFF_0102;
|
||||||
|
|
||||||
|
Assert.Equal(0, NetExports.NetHtons(_ctx));
|
||||||
|
Assert.Equal(0x0201UL, _ctx[CpuRegister.Rax]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Ntohs_SwapsLowTwoBytesOnly()
|
||||||
|
{
|
||||||
|
_ctx[CpuRegister.Rdi] = 0xFFFF_0102;
|
||||||
|
|
||||||
|
Assert.Equal(0, NetExports.NetNtohs(_ctx));
|
||||||
|
Assert.Equal(0x0201UL, _ctx[CpuRegister.Rax]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Htonl_IgnoresBitsAboveThe32BitWord()
|
||||||
|
{
|
||||||
|
_ctx[CpuRegister.Rdi] = 0xDEADBEEF_01020304;
|
||||||
|
|
||||||
|
Assert.Equal(0, NetExports.NetHtonl(_ctx));
|
||||||
|
Assert.Equal(0x04030201UL, _ctx[CpuRegister.Rax]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0xDEADBEEFUL)]
|
||||||
|
[InlineData(0x00000000UL)]
|
||||||
|
[InlineData(0xFFFFFFFFUL)]
|
||||||
|
[InlineData(0x00000001UL)]
|
||||||
|
public void HtonlThenNtohl_RoundTripsToOriginal(ulong value)
|
||||||
|
{
|
||||||
|
_ctx[CpuRegister.Rdi] = value;
|
||||||
|
NetExports.NetHtonl(_ctx);
|
||||||
|
|
||||||
|
_ctx[CpuRegister.Rdi] = _ctx[CpuRegister.Rax];
|
||||||
|
NetExports.NetNtohl(_ctx);
|
||||||
|
|
||||||
|
Assert.Equal(value, _ctx[CpuRegister.Rax]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regression guard: a non-palindromic value must not come back as 0. The functions previously
|
||||||
|
// computed the swap into Rax and then called SetReturn(0), which overwrote Rax, so every
|
||||||
|
// sceNetHtonl/Htons/Ntohl/Ntohs call returned 0 regardless of input.
|
||||||
|
[Fact]
|
||||||
|
public void ByteOrderConversions_DoNotReturnZeroForNonZeroInput()
|
||||||
|
{
|
||||||
|
_ctx[CpuRegister.Rdi] = 0x01020304;
|
||||||
|
NetExports.NetHtonl(_ctx);
|
||||||
|
Assert.NotEqual(0UL, _ctx[CpuRegister.Rax]);
|
||||||
|
|
||||||
|
_ctx[CpuRegister.Rax] = 0;
|
||||||
|
_ctx[CpuRegister.Rdi] = 0x0102;
|
||||||
|
NetExports.NetHtons(_ctx);
|
||||||
|
Assert.NotEqual(0UL, _ctx[CpuRegister.Rax]);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user