Skip to content

feat(esp32): enable semihosting exit in QEMU#1549

Merged
cpunion merged 7 commits intogoplus:mainfrom
luoliwoshang:feat/esp32-semihost-exit
Mar 4, 2026
Merged

feat(esp32): enable semihosting exit in QEMU#1549
cpunion merged 7 commits intogoplus:mainfrom
luoliwoshang:feat/esp32-semihost-exit

Conversation

@luoliwoshang
Copy link
Member

@luoliwoshang luoliwoshang commented Jan 13, 2026

Summary

This PR enables reliable QEMU exit for the existing esp32 target (Xtensa) by switching to newlib's exit-only semihosting path.

Goal: keep normal ESP32 runtime behavior for non-exit syscalls, but let llgo run -target=esp32 -emulator terminate QEMU cleanly for test automation.

Motivation

Before this change, ESP32 emulator runs could end via an illegal-instruction panic path without a deterministic semihosting exit signal, which made emulator-based comparison tests unstable.

At the same time, enabling full semihosting is not desired here because it can reroute regular I/O behavior. For ESP32 we only need semihosting on _exit.

What Changed

1) newlib integration for Xtensa (ESP32)

  • Bump LLGO's ESP32 newlib dependency:
    • from esp-4.3.0_20250211-patch5
    • to esp-4.3.0_20250211-patch6
  • Update libc config tests accordingly.

In LLGO's Xtensa newlib build config:

  • Compile libgloss/xtensa/sim-call.S (semihost call entry support).
  • Add -DQEMU_SEMIHOSTING_EXIT_ONLY for Xtensa libgloss/syscalls build.
  • Include libgloss/libnosys/_exit.c in the build.
  • Remove -DGETREENT_PROVIDED in Xtensa newlib compile flags so newlib's own reent path is used instead of relying on external stubs.

2) ESP32 emulator command

  • Update targets/esp32.json to run QEMU with semihosting enabled:
    • add -semihosting to qemu-system-xtensa arguments.

3) CI and smoke test improvements

  • Replace test_esp32c3_startup.sh with a generalized script:
    • _demo/embed/test-esp-serial-startup.sh
  • Test flow now includes:
    • Common smoke for ESP32-C3 + ESP32 (build + emulator run)
    • ESP32-C3-specific startup regressions (__libc_init_array, .init_array in .rodata, BIN segment check)
  • Stream emulator output in real time during smoke tests (instead of only post-capture).
  • Update embed dependency setup to install and verify both:
    • qemu-system-riscv32
    • qemu-system-xtensa

Newlib Behavior Clarification

With patch6 + QEMU_SEMIHOSTING_EXIT_ONLY in Xtensa:

  • _exit uses semihosting to notify QEMU and terminate emulator runs cleanly.
  • Non-exit paths (e.g. regular output path) stay on the existing board/syscall flow.

So this is intentionally not full semihosting; it is exit-only semihosting.

Validation

Local checks performed:

  • llgo run -a -target=esp32 -emulator .
  • bash _demo/embed/test-esp-serial-startup.sh
  • Verified QEMU tool install script provides both Xtensa and RISC-V executables.

If local cache still points to an older newlib bundle, clear and rebuild once:

rm -rf ~/Library/Caches/llgo/crosscompile/newlib-*

Scope / Compatibility

  • No new target introduced; this applies to the existing esp32 target.
  • Change is focused on emulator termination correctness while keeping non-exit behavior aligned with prior ESP32 expectations.

@gemini-code-assist
Copy link

Summary of Changes

Hello @luoliwoshang, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces crucial enhancements for emulating ESP32 and ESP32-C3 targets in QEMU by enabling clean program exits via semihosting. It involves updating the underlying newlib library, adjusting QEMU launch parameters, and refining firmware image generation to correctly support both Xtensa and RISC-V architectures. Additionally, a new testing framework has been added to ensure the reliability and correctness of emulator interactions, providing a more robust development and testing environment for embedded applications.

Highlights

  • Semihosting Exit for ESP32 (Xtensa): Programs running on ESP32 (Xtensa) under QEMU can now perform clean exits by leveraging newlib's semihosting feature. This involves adding the -semihosting flag to the QEMU emulator command and configuring newlib to use semihost-sys_exit.c.
  • Semihosting Exit for ESP32-C3 (RISC-V): Similar semihosting exit capabilities have been enabled for ESP32-C3 (RISC-V) targets in QEMU, ensuring consistent behavior across different ESP32 architectures.
  • Newlib Update and Configuration: The newlib library for ESP32 targets has been updated to esp-4.3.0_20250211-patch5. This update includes modifications to the build configurations for both RISC-V and Xtensa, specifically introducing a dedicated libsemihost group for RISC-V and adding -DQEMU_SEMIHOSTING to Xtensa's libcrt0 CFlags.
  • Conditional Firmware Image Padding: The firmware image creation logic has been refined to conditionally pad 4KB of zeros only for ESP32 (Xtensa) chips, as their bootloader starts at 0x1000. ESP32-C3 (RISC-V) bootloaders start at 0x0, requiring no such padding, which ensures correct QEMU emulation for both.
  • Enhanced Emulator Testing Framework: A new Go testing framework (cl/embed_test.go) has been introduced to automate the execution and validation of embedded programs within the QEMU emulator. This includes utilities for filtering QEMU output and comparing program results against expected outputs, significantly improving test coverage for embedded targets.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Ignored Files
  • Ignored by pattern: .github/workflows/** (2)
    • .github/workflows/go.yml
    • .github/workflows/install-esp-qemu.sh
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enables semihosting for ESP32 (Xtensa) and ESP32-C3 (RISC-V) targets in QEMU, allowing programs to exit cleanly. This is achieved by updating the newlib build configurations, adding the -semihosting flag to QEMU commands, and introducing a new test framework to verify the emulator's behavior. The refactoring of esp32c3.json to inherit from a new esp32c3-basic.json is a good improvement for maintainability.

The PR title and summary currently focus on ESP32 (Xtensa). It would be beneficial to update them to also reflect the significant changes made for ESP32-C3 (RISC-V) to provide a more complete picture of the PR's scope.

I have a couple of minor suggestions for improvement in the new test files.

@xgopilot
Copy link
Contributor

xgopilot bot commented Jan 13, 2026

Code Review Summary

This PR successfully enables ESP32 semihosting exit functionality for clean QEMU emulation. The implementation is solid with good test coverage. I've identified a few areas for improvement:

Key Findings:

  • ✅ Clean architecture with dedicated semihosting library for RISC-V
  • ✅ Comprehensive test suite with output filtering
  • ⚠️ Security: Missing checksum verification in QEMU download script
  • ⚠️ Code quality: Potential file descriptor leak in test helper
  • 💡 Performance: Regex recompiled unnecessarily on each call

Priority Recommendations:

  1. Add SHA256 checksum verification to install-esp-qemu.sh to prevent supply chain attacks
  2. Fix potential FD leak with proper defer cleanup in runEmulatorTest
  3. Move regex compilation to package level for better performance

The changes are well-structured and the new test framework provides excellent coverage for embedded targets. Great work!

@codecov
Copy link

codecov bot commented Jan 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.65%. Comparing base (e8e1faf) to head (ce96119).
⚠️ Report is 26 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1549      +/-   ##
==========================================
+ Coverage   91.35%   91.65%   +0.29%     
==========================================
  Files          47       47              
  Lines       12681    13135     +454     
==========================================
+ Hits        11585    12039     +454     
  Misses        906      906              
  Partials      190      190              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@luoliwoshang luoliwoshang force-pushed the feat/esp32-semihost-exit branch 5 times, most recently from a43eb49 to bc1014a Compare January 13, 2026 09:01
@luoliwoshang luoliwoshang marked this pull request as draft January 13, 2026 13:07
Switch ESP32 libcrt0 build flag from QEMU_SEMIHOSTING to QEMU_SEMIHOSTING_EXIT_ONLY so only _exit uses semihosting while regular syscall behavior (like _write) stays non-semihosting.
@luoliwoshang luoliwoshang force-pushed the feat/esp32-semihost-exit branch from f6d39c9 to e114e99 Compare February 27, 2026 06:25
Upgrade newlib-esp32 source from esp-4.3.0_20250211-patch5 to esp-4.3.0_20250211-patch6, including URL/version/resource directory and related unit test expectations.
@luoliwoshang luoliwoshang marked this pull request as ready for review February 27, 2026 07:30
@xgopilot
Copy link
Contributor

xgopilot bot commented Feb 27, 2026

Good refactoring to generalize the ESP test script for both targets, and the set -euo pipefail upgrade is a solid improvement. The semihosting exit approach is reasonable for QEMU clean-exit support.

Key concerns: (1) CI only installs qemu-system-riscv32 — the new ESP32 emulator smoke test (qemu-system-xtensa) will fail in CI. (2) sim-call.S and -DQEMU_SEMIHOSTING_EXIT_ONLY are compiled unconditionally into all xtensa builds including hardware board targets — consider documenting the safety implications. (3) Each target is built twice due to llgo run -a after llgo build.

@luoliwoshang luoliwoshang force-pushed the feat/esp32-semihost-exit branch 3 times, most recently from fe61012 to b669815 Compare February 27, 2026 08:36
Comment on lines -235 to -250
echo ""
echo "=== Test 4: Verify QEMU output ==="

# Ignore emulator boot logs and validate the last non-empty line.
RUN_OUT=$(llgo run -a -target=esp32c3-basic -emulator . 2>&1)
LAST_LINE=$(printf "%s\n" "$RUN_OUT" | tr -d '\r' | awk 'NF{line=$0} END{print line}')
if [ "$LAST_LINE" = "Hello World" ]; then
echo "✓ PASS: QEMU output ends with Hello World"
else
echo "✗ FAIL: QEMU output mismatch"
echo "Last line: $LAST_LINE"
echo ""
echo "Full output:"
echo "$RUN_OUT"
exit 1
fi
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@luoliwoshang luoliwoshang force-pushed the feat/esp32-semihost-exit branch from b669815 to ca4a824 Compare February 27, 2026 08:56
Copy link
Contributor

@zhouguangyuan0718 zhouguangyuan0718 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@cpunion cpunion merged commit 6a745b7 into goplus:main Mar 4, 2026
37 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.

4 participants