linx: support clean qemu-user glibc execution#32
Conversation
There was a problem hiding this comment.
Code Review
This pull request cleans up debug variables, updates syscall register mappings to xA7, replaces target-specific signal constants with generic QEMU ones, and implements several new instructions including long immediate arithmetic and store pair immediate operations. Additionally, it fixes conditional select operand ordering and adds setret padding detection. Feedback highlights two critical issues: first, regenerating exceptions on -QEMU_ERESTARTSYS in cpu_loop.c without rewinding env->pc can bypass signal handlers and corrupt state; second, speculative read-ahead in has_head_setret_padding can cross page boundaries and trigger spurious page faults, requiring a boundary guard.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if (ret == -QEMU_ERESTARTSYS) { | ||
| /* | ||
| * Regenerate the exception so that cpu_exec can exit before | ||
| * executing the instruction. | ||
| */ | ||
| cs->exception_index = LINX_EXCP_SCALL; | ||
| } else { | ||
| if (ret != -TARGET_QEMU_ESIGRETURN) | ||
| if (ret != -QEMU_ESIGRETURN) | ||
| env->gpr[xA0] = ret; | ||
| } |
There was a problem hiding this comment.
Regenerating the exception on -QEMU_ERESTARTSYS without rewinding env->pc is incorrect and leads to severe bugs. When a signal is pending, cpu_exec will exit immediately and process_pending_signals will set up the signal frame and modify env->pc to point to the signal handler. However, because cs->exception_index is still set to LINX_EXCP_SCALL, the loop will immediately execute case LINX_EXCP_SCALL again, overwriting env->pc with env->next_bpc. This completely bypasses the signal handler and corrupts the execution state.
Instead, we should rewind env->pc to the original syscall instruction (which is 4 bytes before env->next_bpc) and let the normal QEMU signal delivery mechanism handle the restart without regenerating the exception, matching the standard behavior of other QEMU targets like RISC-V.
if (ret == -QEMU_ERESTARTSYS) {
env->pc -= 4;
} else {
if (ret != -QEMU_ESIGRETURN)
env->gpr[xA0] = ret;
}| static bool has_head_setret_padding(DisasContext *ctx, int head_size) | ||
| { | ||
| CPULINXState *env = ctx->cs->env_ptr; | ||
| uint16_t pad = cpu_lduw_code(env, ctx->base.pc_next + head_size); | ||
| uint32_t next = cpu_ldl_code(env, ctx->base.pc_next + head_size + 2); | ||
|
|
||
| return pad == 0xfffe && is_blk_setret_32(next); | ||
| } |
There was a problem hiding this comment.
Speculative read-ahead using cpu_lduw_code and cpu_ldl_code can cross page boundaries if the current instruction is near the end of a page. If the next page is not mapped or not executable, this will trigger a spurious translation-time page fault (SIGSEGV), causing valid guest programs to crash. We should add a guard to ensure we do not perform the lookahead if it crosses a page boundary.
static bool has_head_setret_padding(DisasContext *ctx, int head_size)
{
target_ulong pc = ctx->base.pc_next + head_size;
if ((pc & TARGET_PAGE_MASK) != ((pc + 5) & TARGET_PAGE_MASK)) {
return false;
}
CPULINXState *env = ctx->cs->env_ptr;
uint16_t pad = cpu_lduw_code(env, pc);
uint32_t next = cpu_ldl_code(env, pc + 2);
return pad == 0xfffe && is_blk_setret_32(next);
}
This PR improves Linx qemu-user execution enough to run a dynamically linked glibc printf program.
Changes:
Validation:
Hello from Linx glibc printf: value=42 status=ok