Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static linking does work on master, even when using the 1.9 ABI and with -DBUILD_STATIC_LIBOBJC=true set in cmake #85

Closed
minusbat opened this issue Feb 21, 2019 · 16 comments

Comments

@minusbat
Copy link

No description provided.

@minusbat
Copy link
Author

I get this when trying to use static linking:

ld: error: undefined symbol: __start___objc_classes

referenced by arc.m
arc.m.o:(.objc_init) in archive /usr/local/lib/libobjc.a

ld: error: undefined symbol: __stop___objc_classes

referenced by arc.m
arc.m.o:(.objc_init) in archive /usr/local/lib/libobjc.a
cc: error: linker command failed with exit code 1 (use -v to see invocation)
*** Error code 1

Works fine dynamically. I am compiling all my files with -fobjc-runtime=gnustep-1.9 so am not suing the 2.0 ABI as yet. Though the aim is to move to it in the end. Code still works fine if I checkout the 1.9 branch.

System here is FreeBSD 12-STABLE, which is the latest one and thus has clang 7.0.1 as the native compiler.

@minusbat
Copy link
Author

Note it also links fine if I enable the 2.0 ABI, (though then it doesn't run properly, but I think thats my fault)

@davidchisnall
Copy link
Member

That's quite odd. It looks as if you've compiled libobjc2 with a compiler that supports the new ABI, so it's using the new ABI for the libobjc2 itself. Do you have a minimal reproduction case? I've tried a clean build using:

$ cmake .. -G Ninja -DBUILD_STATIC_LIBOBJC=ON
$ ninja

I've also tried setting CMAKE_C[XX]_COMPILER to clang[++]70 (from packages - the base system has 6.0.1). When I try compiling a simple Objective-C file:

#include <objc/runtime.h>
#include <stdio.h>
@interface Cls
{
        id isa;
}
@end

@implementation Cls @end
int main()
{
        fprintf(stderr, "%p\n", class_createInstance(objc_getClass("Cls"), 0));
}

With:

$ cc a.m libobjc.a

It all seems to work correctly. The same is true if I use clang70 instead of cc and if I explicitly set -fobjc-runtime=gnustep-1.9 or 2.0.

The __start and __stop symbols should be automatically created by the linker. There was an issue with programs that didn't contain any classes, but that should be fixed in the version of clang in packages. Can you check which version of clang you used to build libobjc2, and make sure that it's the latest version of the llvm70 package if it's 7.something?

@minusbat
Copy link
Author

If you update to the latest stable then you get clang 7 in the base (but its very recent - http://www.freshbsd.org/commit/freebsd/src/344212 ), which is what I was truing. Will give it a try with llvm70 from ports and see what I get....

@minusbat
Copy link
Author

Well, that interesting :-) I rebuilt both libobjc2 and my code with clang70 from ports, and it links, but running it gives this:

Version 2 Objective-C ABI may not be mixed with earlier versions.
Abort trap (core dumped)

Have I made a wrong assumption here in that the libobjc2 should be useable with both 1.9 and 2.0 ABI ? Thanks for taking a look by the ay, I realise I am a bit of an edge case in the user base!

@minusbat
Copy link
Author

Did another little test and compile the library with clang60, as that doesn't support the 2.0 ABI, and the result there fails 'gmake test' like this:

The following tests FAILED:
57 - PropertyAttributeTest (Child aborted)
58 - PropertyAttributeTest_optimised (Child aborted)
59 - PropertyAttributeTest_legacy (Child aborted)
60 - PropertyAttributeTest_legacy_optimised (Child aborted)
61 - ProtocolExtendedProperties (Child aborted)
62 - ProtocolExtendedProperties_optimised (Child aborted)
149 - category_properties (Child aborted)
150 - category_properties_optimised (Child aborted)

Haven't tried linking that against the rest of the code as it fails the test.

@minusbat
Copy link
Author

Also, I tried your 'a.m' file above. As you say it seems to work fine if not states, but if I add '-static' then I get the same result:

[pete@skerry ~]$ cc -static -I/usr/local/include a.m -L/usr/local/lib -lobjc
a.m:3:12: warning: class 'Cls' defined without specifying a base class
[-Wobjc-root-class]
@interface Cls
^
a.m:3:15: note: add a super class to fix this problem
@interface Cls
^
1 warning generated.
ld: error: undefined symbol: __start___objc_classes

referenced by arc.m
arc.m.o:(.objc_init) in archive /usr/local/lib/libobjc.a

ld: error: undefined symbol: __stop___objc_classes

referenced by arc.m
arc.m.o:(.objc_init) in archive /usr/local/lib/libobjc.a
cc: error: linker command failed with exit code 1 (use -v to see invocation)
[pete@skerry ~]$

@davidchisnall
Copy link
Member

The following works for me (from the static build directory):

$ clang70 a.m -L. -lobjc -static  -I ../ -fobjc-runtime=gnustep-1.9
a.m:3:12: warning: class 'Cls' defined without specifying a base class [-Wobjc-root-class]
@interface Cls
           ^
a.m:3:15: note: add a super class to fix this problem
@interface Cls
              ^
1 warning generated.
0x802090018
$ file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (FreeBSD), statically linked, for FreeBSD 12.0 (1200501), FreeBSD-style, with debug_info, not stripped
$ ./a.out
0x802090018

I suspect that you're hitting a known bug in the clang 7 release, which is fixed by a back-ported fix in the port, where it doesn't emit a placeholder symbol in the __objc_classes section for compilation units that don't contain any classes and so ends up failing. This then meant that the linker wouldn't generate the start and stop symbols for the section if the program didn't contain any classes. I'm not sure how this would be triggered with the example above though, unless it's to do with the order on the linker command line (though invoking lld directly and moving the position of -lobjc in the command line doesn't seem to trigger it for me).

The mixing ABIs issue is also a little bit surprising, but indicates that it may be an issue with linker ordering. Looking at the code in loader.c, that can happen if you load some old ABI classes and then load the Protocol class from the runtime (which is always new ABI, but doesn't do anything where mixing the ABIs would be unsafe). Can you try this patch and see if that fixes things for you?

diff --git a/loader.c b/loader.c
index 7022469..3f5f15f 100644
--- a/loader.c
+++ b/loader.c
@@ -197,8 +197,28 @@ OBJC_PUBLIC void __objc_load(struct objc_init *init)
        switch (CurrentABI)
        {
                case LegacyABI:
+               {
+                       BOOL isRuntimeLib = NO;
+                       for (Class *cls = init->cls_begin ; cls < init->cls_end ; cls++)
+                       {
+                               if (*cls == NULL)
+                               {
+                                       continue;
+                               }
+                               // As a special case, allow using legacy ABI code with a new runtime.
+                               if (strcmp((*cls)->name, "Protocol"))
+                               {
+                                       isRuntimeLib = YES;
+                                       break;
+                               }
+                       }
+                       if (isRuntimeLib)
+                       {
+                               break;
+                       }
                        fprintf(stderr, "Version 2 Objective-C ABI may not be mixed with earlier versions.\n");
                        abort();
+               }
                case UnknownABI:
                        isFirstLoad = YES;
                        CurrentABI = NewABI;

@minusbat
Copy link
Author

Thanks for the patch, and the comment about the llvm70 in ports. So what I am not doing is building everything with the ports compiler. I applied your patch, recompiled all of libobjc and then tried the a.m file. It does indeed link, but running it gives the

pete@skerry ~/libobjc2/build]$ ./a.out 
Version 2 Objective-C ABI may not be mixed with earlier versions.
Abort trap (core dumped)

That we had earlier. In the example you quote above where it runs, which compiler did you use to build the library ?

All very puzzling....

@minusbat
Copy link
Author

I can get a.o to run with the 2.0 ABI though. Maybe I should just switch to getting my code to run under the 2.0 ABI using the ports compiler instead ? Will give that a shot in a bit....

@davidchisnall
Copy link
Member

Okay, it looks as if there was a bug in the strcmp usage, which should now be fixed in head. I'm quite surprised that this didn't trigger more failures, but it looks as if the most common failure mode is to allow more ABI mismatch than it should and your use is the only one where it was allowing less. Hopefully with this you won't need the patch above, but if you do the strcmp in it needs to have an == 0 added to the end.

@minusbat
Copy link
Author

Thanks - l I just pulled master and I still get the ABI mismatch output using the 'a.m' file unfortunately. I then tried the above patch, with the addition of the '== 0' to the strcmp line, and even with that it still gives me the ABI mismatch output.

@minusbat
Copy link
Author

Aha, the above patch does nothing because that code is never run - that function is only ever called with an ABI type of 'Unknown'. The line with is actually printing the message about mixing version and aborting is not there, its the test at the top of '__objc_exec_class'

@minusbat
Copy link
Author

So, I tried compiling the library with '-fobjc-runtime=gnustep-1.9' set, and it still says that you can't mix ABI's, despite there now being no code at all in the setup whtihc has been compiled without that flag set.

I don't have much more time to look at this today, but am fairly keen to get it fixed now - I doubt I will be able to move to 2.0 in the foreseeable future because of the UTF-16 behaviour, but I don't want to be stuck on the 1.9 branch forever, as I don't want to its any improvements which go into master. Will try and find time to get back to this next week though. Thanks for the help...

@davidchisnall
Copy link
Member

Okay, I understand the issue. We detect that we should reset the ABI check to unknown by checking if it's loading the Protocol class, but when you do static linking if you don't reference Protocol then the Protocol class is now dropped entirely and not linked. This is probably hiding other bugs, because we also do depend on being able to compare things to the protocol class.

@minusbat
Copy link
Author

I am just revisiting all of this, and unfortunately, even with the fix, the original problem still exists. I still get the 'Version 2 Objective-C ABI may not be mixed with earlier versions.' when static linkinging, despite your commit which always links the protocol classes.

I haven't done any further digging - I came to this because I had another issue with protocols in the 2.0 ABI - but can the be re-opened as an issue ? Sorry....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants