Skip to content

Dev - #1658

Merged
xwings merged 37 commits into
masterfrom
dev
Sep 5, 2026
Merged

Dev#1658
xwings merged 37 commits into
masterfrom
dev

Conversation

@xwings

@xwings xwings commented Sep 5, 2026

Copy link
Copy Markdown
Member

Checklist

Which kind of PR do you create?

  • This PR only contains minor fixes.
  • This PR contains major feature update.
  • This PR introduces a new function/api for Qiling Framework.

Coding convention?

  • The new code conforms to Qiling Framework naming convention.
  • The imports are arranged properly.
  • Essential comments are added.
  • The reference of the new code is pointed out.

Extra tests?

  • No extra tests are needed for this PR.
  • I have added enough tests for this PR.
  • Tests will be added after some discussion and review.

Changelog?

  • This PR doesn't need to update Changelog.
  • Changelog will be updated after some proper review.
  • Changelog has been updated in my PR.

Target branch?

  • The target branch is dev branch.

One last thing


xwings and others added 30 commits April 28, 2026 16:02
getdents64 wrote linux_dirent64 records with d_reclen = header + name,
without rounding up to the alignment of the leading u64 d_ino. The next
record's d_ino then lands unaligned, and a strict-alignment guest (e.g.
MIPS/MIPS64) faults with UC_ERR_READ_UNALIGNED when walking the buffer.
x86 tolerates the unaligned load, which is why this was never caught.

Round d_reclen up to the d_ino alignment, matching the kernel. The rounding
is scoped to the getdents64 (is_64) branch only: getdents64 places d_type
before d_name so the trailing pad bytes are inert, whereas the legacy
getdents layout stores d_type at offset d_reclen-1 and would break if padded
the same way -- that is what got the earlier blanket fix (PR #1419) reverted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Calls ql_syscall_getdents64 on a directory and walks the returned
linux_dirent64 records, asserting every record (and thus its leading u64
d_ino) starts on an 8-byte boundary. Before the fix d_reclen was not
rounded up, so records were misaligned and a strict-alignment guest (e.g.
MIPS) faulted with an unaligned load. Self-contained; runs on stock unicorn.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Modern glibc (recent toolchains) issues clone3 from pthread_create rather
than clone, so thread creation fails on any guest built against it: Qiling
does not return -ENOSYS for unimplemented syscalls (it logs a warning and
leaves the return register untouched), so glibc's clone3->clone fallback
never fires.

Add ql_syscall_clone3, which unpacks struct clone_args and delegates to the
existing clone() handler. Translations: child_stack = stack + stack_size
(clone3 passes the stack base plus a size; legacy clone wants the highest
address), exit_signal folded into the flags' CSIGNAL byte, and an x8664
pre-swap that cancels ql_syscall_clone's arch-specific newtls<->child_tidptr
swap.

Add a self-contained regression test (test_clone3_translates_to_clone) that
drives ql_syscall_clone3 directly and asserts the translation for both the
generic path and the x8664 swap. Runs on stock unicorn; no clone3 binary
needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extend the record alignment to the legacy getdents path. The kernel rounds
both getdents and getdents64 records up to the alignment of their leading
d_ino (ALIGN(reclen, sizeof(long))); without it a strict-alignment guest
(MIPS) faults walking the buffer, e.g. older glibc busybox 'ls' (which uses
the legacy getdents syscall) crashes on directory listings.

Legacy linux_dirent stores d_type in the record's last byte (offset
d_reclen-1), so the alignment padding goes between d_name and d_type to keep
d_type there -- padding without relocating d_type is what got the earlier
blanket attempt (PR #1419) reverted. getdents64 (d_type before d_name) is
unchanged.

Add test_linux_getdents_alignment: walks legacy getdents records on a
big-endian MIPS guest and asserts each is aligned, tiles the buffer, and
keeps d_type (DT_DIR for '.'/'..'). Runs on stock unicorn.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
linux_mips_socket_options used generic/incorrect values. MIPS has its own
SO_* numbering (arch/mips/include/uapi/asm/socket.h) with the buffer/timeout/
type options in the 0x1000 range. The table listed SO_SNDBUF=0x01,
SO_RCVBUF=0x02 (should be 0x1001/0x1002), SO_SND/RCVLOWAT and SO_SND/RCVTIMEO_OLD
all wrong, SO_RCVLOWAT=0x04 silently collided with SO_REUSEADDR (Enum alias),
and SO_OOBINLINE/SO_REUSEPORT were 0x00.

A guest setsockopt(SOL_SOCKET, SO_RCVBUF, ...) (e.g. busybox ping) therefore
raised 'Could not convert emulated socket option 4098'. Correct all values to
the MIPS uapi and add the missing SO_TYPE/SO_ERROR/SO_ACCEPTCONN/SO_PROTOCOL/
SO_DOMAIN.

Add test_elf.ELFTest.test_setsockopt_mips_so_rcvbuf: opens a socket on a MIPS
guest and asserts setsockopt(SOL_SOCKET, SO_RCVBUF) succeeds. Self-contained,
runs on stock unicorn; fails on dev, passes with the fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add AGENT documentation.
call_dll_entrypoint() caught the DllMain UcError and restored registers but
left ql._state at STARTED, since emu_start() only clears it on success. The
'emu_state is not STARTED' gate then silently skipped DllMain for every DLL
loaded afterward. Reset the state to STOPPED on the exception path.
FiberManager.free/set/get set `self.last_error` on the FiberManager
instance, but nothing ever reads that attribute. The Windows last-error
that GetLastError returns lives on `ql.os.last_error`, so FlsFree,
FlsSetValue and FlsGetValue never actually reported ERROR_INVALID_PARAMETER
for an invalid FLS index. Route the three writes to `self.ql.os.last_error`,
consistent with every other kernel32 hook.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Jvle <keke.oerv@isrc.iscas.ac.cn>
Signed-off-by: Jvle <keke.oerv@isrc.iscas.ac.cn>
Signed-off-by: Jvle <keke.oerv@isrc.iscas.ac.cn>
Signed-off-by: Jvle <keke.oerv@isrc.iscas.ac.cn>
The first line reads `from future import __annotations__`, with the
module name and the imported symbol swapped. The intent was clearly the
PEP 563 future statement `from __future__ import annotations`.

As written, the line fails in both possible situations:

- without the third-party `future` package installed (the common case),
  the import raises `ModuleNotFoundError`, so `QILING_IDA` is never
  defined;
- with that package installed, the import silently succeeds but binds
  the module's empty `__annotations__` dict, so PEP 563 is *not*
  enabled — while the annotations in this file (`ql: Qiling`) rely on
  it, `Qiling` being imported under `TYPE_CHECKING` only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pru9dwqPVrDakERYZwzjUx
handle_s answered every single-step ('s') with a SIGTERM stop-reply
whenever ql.emu_state was QL_STATE.STOPPED. But emu_start always leaves
the state STOPPED after running the requested step count, so the guard
was true on every step and gdb clients saw a spurious termination signal,
disconnecting mid-debug (issues #1377, #1538).

Give handle_s the same exit-vs-trap discrimination handle_c already uses:
a step reports SIGTRAP unless it carried pc to the emulation exit point,
in which case the guest has actually terminated and we reply W{exit_code}.
Also wrap the step in the same UcError/KeyboardInterrupt handling as
handle_c so a fault while stepping maps to a signal instead of crashing
the stub, and hoist the shared uc-error->signal map to a module constant.

Add a regression test that single-steps over the gdb stub and asserts the
stop-reply is 'S05' (SIGTRAP), which fails as 'S0f' (SIGTERM) without the fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The stub drove continue by calling emu_start synchronously and only read
the socket again once the target stopped on its own, so the bare \x03 break
byte a client sends to pause a running target was never seen. A guest that
free-runs (e.g. an idle/event loop) could not be interrupted at all --
gdb/Ghidra reported 'Cannot execute this command while the target is running'.

Poll the client socket for the break byte from the per-instruction run hook
(dbg_hook), which does run on the emulation thread during emu_start:

- GdbSerialConn.poll_interrupt(): non-blocking select+recv, True on \x03.
- QlGdbUtils.dbg_hook: throttled (every INTR_POLL_INTERVAL insns) check of an
  installed check_interrupt callback; on a break, stop emulation and record it.
- handle_c: reply SIGINT when the stop was an interrupt rather than a
  breakpoint or normal exit.

Add a regression test that lets an infinite-loop guest free-run, sends the
bare break byte and asserts the stop-reply is 'S02' (SIGINT). Without the
fix no reply ever arrives, so the test stops the guest itself and fails
rather than hanging the run.

Also verified against a free-running MIPS64 BE guest: \x03 -> S02 in <1ms.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A client that resumes while a signal is pending sends a vCont action of the
form 'C<sig>' (continue and deliver) or 'S<sig>' (step and deliver), e.g.
'vCont;S0f:pa410.1996;c:pa410.-1' -- the exact packet reported in issue
#1377. handle_v matched only 'c'/'C05' and 's'/'S05', so any other signal
fell through to an empty reply and clients aborted the session with
'Invalid remote reply:'.

We do not deliver host signals to the guest, so the signal value carries no
meaning for us: accept any of them and carry the action out as a plain
resume or step, which is what the client asked for. This matters more now
that the stub can stop with SIGINT on an async interrupt, since a client
may well resume from such a stop with 'C02'.

Also stop assuming os.exit_code exists when reporting termination: bare-metal
os layers (QlOsMcu) do not define it, which turns the exit path into an
AttributeError (seen in issue #1276). The underlying MCU interrupt handling
of #1276 is out of scope here.

Add a regression test asserting the '#1377' packet is answered with a
SIGTRAP stop-reply and that a signalled continue runs the guest to
termination; both replies are empty without the fix.

Fixes #1377

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gdb: fix stop-replies for step, signalled vCont resume, and ctrl-c interrupt
update new ARCHITECTURE docs
Fix broken __future__ import in IDA plugin custom_script
Fix FLS FiberManager writing last_error to a dead attribute
Align getdents and getdents64 records to fix unaligned access on MIPS
…state

Reset emu state after a failing DllMain
@xwings
xwings merged commit 86c927c into master Sep 5, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants