Skip to content

Implement buildmode=c-archive#5316

Open
hectorchu wants to merge 17 commits intotinygo-org:devfrom
hectorchu:c-archive
Open

Implement buildmode=c-archive#5316
hectorchu wants to merge 17 commits intotinygo-org:devfrom
hectorchu:c-archive

Conversation

@hectorchu
Copy link
Copy Markdown

This is - not fully polished - but is opened here in the hope that suggestions and improvements can be made. Fixes #254

It does work - pretty well actually - for Xtensa. There, a component can be made with a simple:

idf_component_register()

add_prebuilt_library(foo "foo.a")

target_link_libraries(${COMPONENT_LIB} INTERFACE foo)

@hectorchu
Copy link
Copy Markdown
Author

Minimum app.c supporting code:

#include <stdlib.h>

void tinygo_init(void *p, size_t n, void *bss, void *bss_end, void *sp);
int _globals_start, _globals_end, _stack_top;
extern uint8_t _bss_start, _bss_end;

void app_main(void)
{
    int heap = 200000;
    tinygo_init(malloc(heap), heap, &_bss_start, &_bss_end, &heap);
    // ready to call CGo exported functions
}

@aykevl
Copy link
Copy Markdown
Member

aykevl commented Apr 17, 2026

In my opinion this looks a little bit too specific to one particular platform at the moment (Xtensa), while there are many other possible platforms. For example, Linux. The design will likely need significant changes to work more universally.

@hectorchu
Copy link
Copy Markdown
Author

In my opinion this looks a little bit too specific to one particular platform at the moment (Xtensa), while there are many other possible platforms. For example, Linux. The design will likely need significant changes to work more universally.

It's more universal than you might think, though. I first developed c-archive to build a static lib for macOS, I confirmed it worked by linking that .a to a test.c program. My real target is esp32 which is why there's xtensa-specific workarounds in there. But for Linux I think it would mostly "just-work".

Copy link
Copy Markdown
Member

@aykevl aykevl left a comment

Choose a reason for hiding this comment

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

In my opinion this is a bit too messy to really review and form an opinion on. See the comments below.

Some things I definitely want to see improved before reviewing again:

  • It needs tests. There currently are none. Without tests, it will break eventually. In particular, it needs tests to show CGo still works correctly (or alternatively CGo support needs to be disabled - this might be difficult since we rely on CGo for a bunch of features).
  • It should not build the C library and should not add C header files to the C include path, since in that case we'll be mixing two C libraries which will break in "interesting" ways.

Comment thread builder/build.go
ctx := llvm.NewContext()
mod = ctx.NewModule("main")
for _, dependency := range job.dependencies {
if strings.HasSuffix(dependency.description, ".S") ||
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

"description" is for humans, not computers. Don't use it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I do realize this, but there was no other way to tell if the .bc file was a product of a .S. You see, some of those .bc files are actually .o files, but with a .bc extension. So I can't just filter by .bc extension.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

In other words there is a separate bug to do with how the dependency results are named.

Comment thread builder/build.go
fn2.SetName(name)
}
}
if config.Triple() == "xtensa" {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please make this a bit more generic.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I tried running this code block for macOS target and the linker failed, can't remember the error message, but that's why its scoped to xtensa.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Oops sorry, got this mixed up with the second xtensa block. This one, is all about putting functions into their own sections (basically doing -ffunction-sections - generates a separate ELF section for each function in the source file). The reason this is needed is because the literal sections need to be as close to the code referring to them as possible - or else you get l32r dangerous relocation linker errors from esp-idf (specifically literal target out of range).

Comment thread builder/build.go
}
if config.Triple() == "xtensa" {
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
if strings.HasPrefix(fn.Name(), "__atomic_") ||
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why is this needed? It is somewhat unexpected, so please add a comment what it does and why it is needed.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

These atomic and sync functions are also defined in esp-idf. So this fix avoids multiple definitions.

Comment thread src/runtime/baremetal.go
)

//export tinygo_init
func tinygo_init(p, n, bss, bssEnd, sp uintptr) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This needs documentation.

Comment thread compileopts/target.go Outdated
switch options.GOOS {
case "darwin":
spec.GC = "boehm"
if options.GC == "boehm" {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

At one point you limit it to Xtensa, but here you modify macOS related code.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This "fix" is because, when I was developing this feature for macOS target, it assumes boehm and task_thread.c must be linked even if gc=conservative and scheduler=none. Probably the same errors would happen when just building regular binaries.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

So probably I'd be happy to remove these changes - it's a separate issue.

@hectorchu
Copy link
Copy Markdown
Author

It should not build the C library and should not add C header files to the C include path, since in that case we'll be mixing two C libraries which will break in "interesting" ways.

About this, the reason I still build the libc but don't link it, is because my CGo modules won't compile unless it can find the header files. And in the case of xtensa, the libc is picolibc, which is also available in esp-idf. So the same libc can be used.

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.

Shared object / DLL support

2 participants