c3c icon indicating copy to clipboard operation
c3c copied to clipboard

Static libraries do not load dynamic functions and ignore initializer/finalizers

Open cbuttner opened this issue 1 year ago • 3 comments

This happens on macOS.

Compile a C3 static library with

fn void test_c3() @export("test_c3") {
  Allocator allocator = allocator::heap();
  (void)allocator::new_array(allocator, int, 4);
}

Compile a C executable with

extern void test_c3();

int main(int argc, char** argv) {
  test_c3();
  return 0;
}

and link against the C3 library. The program crashes, seemingly because the allocator interface is broken.

cbuttner avatar May 24 '24 13:05 cbuttner

For now -Wl,-all_load before linking the static library works.

lerno avatar May 25 '24 21:05 lerno

If you do this with a C library, using the attribute((constructor)) I think you see the same thing (the constructor isn't called)

lerno avatar Jun 15 '24 17:06 lerno

The problem here is that if you include a static library, then unless the symbol can be traced it is deleted. While enforcing load works, this is not ideal.

We have the following situations:

  1. C3 static library with C main
  2. C3 static library x 2 with C main
  3. C3 static library with C3 main
  4. C3 static library x 2 with C3 main

On MacOS the constructors and destructors are placed in a special section. This will merge them. So this section is all that is needed to be retained in the 3/4 situation.

In 1/2 we also need to retain the loader of them.

On Linux/Wasm/Windows we rely on initializers. Here the problem is actually somewhat harder to fix.

For Objective-C, Clang requires a special -ObjC directive to make the methods in Objective-C classes be retained despite no direct dependency. This problem is similar to the same problem.

There are two ways to keep something from a static library:

  1. retrain a symbol that in turn links to it.
  2. take everything from the static lib

If we only dealt with a single static library, then we could create a $c3load function which would reference everything.

We would then use -symbol $c3load and it would be loaded.

However, if we have multiple static libraries this symbol would be overlapping. Can this be made to work? Or is a unique name needed, eg -symbol mylib$load?

Another alternative would be a linker script. This is a worse user experience I think, but if embedded in a linker, then perhaps it's possible.

Googling there seems to be no simple solution, but perhaps it's possible to do something clever.

lerno avatar Sep 14 '24 14:09 lerno

This should now work if you build a static lib as it will build everything in a single module.

lerno avatar Nov 30 '24 14:11 lerno

I think this should work so I close it, and reopen it if it is a problem again.

lerno avatar Dec 15 '24 19:12 lerno