Skip to content

Conversation

@luoliwoshang
Copy link
Member

Summary

  • Add support for CompileGroup to output .o files instead of .a archives
  • Enable unconditional linking of object files (.o files are linked directly, not on-demand like .a)
  • Add libsemihost.o for ESP32-C3 QEMU semihosting support

Background

Static libraries (.a) use on-demand linking - object files are only pulled in when they resolve undefined symbols. This causes issues when a strong symbol needs to override a weak symbol in another library.

For example, syscalls.c contains a weak _exit that uses asm("unimp"), which crashes in QEMU. The semihosting _exit from semihost-sys_exit.c uses proper semihosting protocol, but when compiled into a .a, it won't be linked because the weak _exit is already satisfied.

By compiling to .o and linking directly, the strong _exit is unconditionally included and overrides the weak version.

Changes

  • compile.go: Support .o output (single file compilation, no ar packaging)
  • crosscompile.go: Link .o files directly as path instead of using -l flag
  • newlibesp.go: Add libsemihost.o group for ESP32-C3 RISC-V semihosting

Test plan

  • Build ESP32-C3 target with QEMU emulator
  • Verify _exit uses semihosting implementation (not unimp)
  • Run existing crosscompile tests

🤖 Generated with Claude Code

@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 a significant enhancement to the compilation and linking process by allowing the generation and direct linking of individual object files. This change addresses a common problem in embedded development where weak symbols in standard libraries can prevent stronger, custom implementations from being linked. By enabling unconditional linking of object files, the system gains more control over symbol resolution, specifically demonstrated by fixing a QEMU semihosting issue for ESP32-C3 targets.

Highlights

  • Object File Compilation Support: The CompileGroup now supports compiling source files directly into a single object file (.o) instead of always creating a static library archive (.a). This is enabled by checking the OutputFileName extension.
  • Unconditional Linking for Object Files: The linking process has been updated to treat .o files differently from .a archives. Object files are now linked directly by their path, ensuring they are unconditionally included in the final binary, which is crucial for overriding weak symbols.
  • ESP32-C3 Semihosting Fix: A new CompileGroup for libsemihost.o has been added for ESP32-C3 RISC-V targets. This specifically compiles semihost-sys_exit.c into an object file to guarantee that the strong semihosting _exit implementation overrides the weak _exit in syscalls.c, resolving issues in QEMU emulation.

🧠 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.

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 introduces support for compiling source files directly into object files (.o) in addition to static archives (.a). This is a valuable feature for enabling unconditional linking, which is crucial for scenarios like overriding weak symbols, as demonstrated with the semihosting _exit function for ESP32-C3 QEMU support. The changes are well-structured across compile.go, crosscompile.go, and newlibesp.go, and the implementation correctly handles the different linking requirements for object files versus static libraries. My review includes one suggestion to improve code maintainability by reducing duplication.

Comment on lines 65 to 68
lang := "c"
if filepath.Ext(file) == ".S" {
lang = "assembler-with-cpp"
}

Choose a reason for hiding this comment

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

medium

This logic to determine the language from the file extension is also used for the .a archive compilation path later in this function (lines 87-90). To improve maintainability and avoid code duplication, consider extracting this logic into a helper function.

For example:

func getLangFromFile(file string) string {
    if filepath.Ext(file) == ".S" {
        return "assembler-with-cpp"
    }
    return "c"
}

You could then call this function in both places: lang := getLangFromFile(file).

References
  1. To improve maintainability, code should not be duplicated. Instead, it should be extracted into a reusable function. This is known as the Don't Repeat Yourself (DRY) principle.

}
file := g.Files[0]
lang := "c"
if filepath.Ext(file) == ".S" {
Copy link

Choose a reason for hiding this comment

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

This language detection logic is duplicated on lines 87-90. Consider extracting it to a helper method to maintain DRY principle:

func getFileLanguage(file string) string {
    if filepath.Ext(file) == ".S" {
        return "assembler-with-cpp"
    }
    return "c"
}

}
if filepath.Ext(group.OutputFileName) == ".o" {
continue
// .o files are linked directly (unconditionally)
Copy link

Choose a reason for hiding this comment

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

Consider adding a comment explaining the "why" behind this distinction. The rationale from newlibesp.go:52 about overriding weak symbols would help future maintainers understand the importance of unconditional linking:

// Object files (.o) are linked directly (unconditionally) to ensure symbols
// override weak symbols in other libraries (e.g., semihost _exit overrides weak _exit).
// Archive files (.a) use the -l flag for on-demand linking by the linker.

@xgopilot
Copy link

xgopilot bot commented Jan 8, 2026

Code Review Summary

This PR successfully implements .o (object file) compilation support for unconditional linking, which is essential for ensuring strong symbols override weak symbols in embedded development (particularly for ESP32-C3 semihosting support).

Key Strengths

Performance Optimization: The fast path for .o files eliminates temp directory creation and archive operations
Clear Intent: The implementation correctly distinguishes between unconditional .o linking and on-demand .a linking
Validation: Proper error handling for single-file .o compilation constraint
No Security Issues: Safe file path handling and command execution throughout

Minor Improvements Suggested

I've left inline comments for three areas where documentation could be improved:

  1. Update the IsCompiled() function comment to reflect it handles both .a and .o files
  2. Extract duplicated language detection logic into a helper function
  3. Enhance the linking distinction comment to explain the symbol override behavior

The code is production-ready. These are all low-priority documentation improvements that would help with long-term maintainability.

Add support for CompileGroup to output .o files instead of .a archives.
This enables unconditional linking of object files, which is needed when
a strong symbol must override a weak symbol in another library.

Changes:
- compile.go: Support .o output (single file compilation)
- crosscompile.go: Link .o files directly instead of using -l flag
- newlibesp.go: Add libsemihost.o for ESP32-C3 QEMU semihosting support

The semihosting _exit implementation is compiled as .o to ensure it
overrides the weak _exit in syscalls.c, enabling proper exit handling
in QEMU emulator.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@luoliwoshang luoliwoshang force-pushed the feat/crosscompile-object-file-support branch from ebd6954 to 4416d4e Compare January 8, 2026 11:30
@luoliwoshang luoliwoshang marked this pull request as draft January 8, 2026 12:22
luoliwoshang added a commit to luoliwoshang/llgo that referenced this pull request Jan 8, 2026
Remove conflicting _exit implementations from libnosys and libgloss to
ensure the semihosting version (from semihost-sys_exit.c, added as .o
in goplus#1537) is used for ESP32-C3 QEMU emulator.

Changes:
- Comment out libnosys/_exit.c
- Comment out libgloss/riscv/sys_exit.c (3 occurrences)

This allows proper exit handling in QEMU emulator via semihosting
protocol instead of using the `unimp` instruction which causes crashes.

Related: goplus#1537

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
luoliwoshang added a commit to luoliwoshang/llgo that referenced this pull request Jan 9, 2026
    Remove conflicting _exit implementations from libnosys and libgloss to
    ensure the semihosting version (from semihost-sys_exit.c, added as .o
    in goplus#1537) is used for ESP32-C3 QEMU emulator.

    Changes:
    - Comment out libnosys/_exit.c
    - Comment out libgloss/riscv/sys_exit.c (3 occurrences)

    This allows proper exit handling in QEMU emulator via semihosting
    protocol instead of using the  instruction which causes crashes.

    Related: goplus#1537

    🤖 Generated with [Claude Code](https://claude.com/claude-code)

    Co-Authored-By: Claude Opus 4.5 <[email protected]>
luoliwoshang added a commit to luoliwoshang/llgo that referenced this pull request Jan 9, 2026
    Remove conflicting _exit implementations from libnosys and libgloss to
    ensure the semihosting version (from semihost-sys_exit.c, added as .o
    in goplus#1537) is used for ESP32-C3 QEMU emulator.

    Changes:
    - Comment out libnosys/_exit.c
    - Comment out libgloss/riscv/sys_exit.c (3 occurrences)

    This allows proper exit handling in QEMU emulator via semihosting
    protocol instead of using the  instruction which causes crashes.

    Related: goplus#1537

    🤖 Generated with [Claude Code](https://claude.com/claude-code)

    Co-Authored-By: Claude Opus 4.5 <[email protected]>
luoliwoshang added a commit to luoliwoshang/llgo that referenced this pull request Jan 13, 2026
    Remove conflicting _exit implementations from libnosys and libgloss to
    ensure the semihosting version (from semihost-sys_exit.c, added as .o
    in goplus#1537) is used for ESP32-C3 QEMU emulator.

    Changes:
    - Comment out libnosys/_exit.c
    - Comment out libgloss/riscv/sys_exit.c (3 occurrences)

    This allows proper exit handling in QEMU emulator via semihosting
    protocol instead of using the  instruction which causes crashes.

    Related: goplus#1537

    🤖 Generated with [Claude Code](https://claude.com/claude-code)

    Co-Authored-By: Claude Opus 4.5 <[email protected]>
luoliwoshang added a commit to luoliwoshang/llgo that referenced this pull request Jan 15, 2026
    Remove conflicting _exit implementations from libnosys and libgloss to
    ensure the semihosting version (from semihost-sys_exit.c, added as .o
    in goplus#1537) is used for ESP32-C3 QEMU emulator.

    Changes:
    - Comment out libnosys/_exit.c
    - Comment out libgloss/riscv/sys_exit.c (3 occurrences)

    This allows proper exit handling in QEMU emulator via semihosting
    protocol instead of using the  instruction which causes crashes.

    Related: goplus#1537

    🤖 Generated with [Claude Code](https://claude.com/claude-code)

    Co-Authored-By: Claude Opus 4.5 <[email protected]>
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.

1 participant