gnustep / libobjc2

Objective-C runtime library intended for use with Clang.
http://www.gnustep.org/
MIT License
434 stars 118 forks source link

Core dump with libobjc2-2.0 from the ports on FreeBSD 12.0-RELEASE-p3 #82

Closed cyclaero closed 5 years ago

cyclaero commented 5 years ago

Compiling and executing the following code on FreeBSD 12.0-RELEASE-p3 (x86_64) results in a core dump:

#import <stdio.h>
#import <objc/Object.h>

__attribute__((objc_root_class))
@interface A
{
   Class       isa;
   long double x;
}
@end

@implementation A
@end

int main(int argc, const char *argv[])
{
   printf("Hello, World!\n");
   return 0;
}

When I replace long double x with double x the above program prints „Hello, World!“.

bt in lldb shows the following:

* thread #1, name = 'hello', stop reason = signal SIGABRT
  * frame #0: 0x000000080043745a
    frame #1: 0x00000008003a8079
    frame #2: 0x000000080026468d
    frame #3: 0x0000000800263fc1
    frame #4: 0x0000000800272081
    frame #5: 0x0000000800271c69
    frame #6: 0x00000008002661a6
    frame #7: 0x00000000002013a2 hello`.objc_load_function + 18
    frame #8: 0x000000080020d0fb
    frame #9: 0x000000080020bf4c
davidchisnall commented 5 years ago

Thanks for the report. I'll bump the port to use the fixed version, but in the meantime this should only be triggered when targeting the old ABI - adding -fobjc-runtime=gnustep-2.0 to your compiler flags should make things work again. This should be added by default for anything built with the latest gnustep-make port.

cyclaero commented 5 years ago

The fixed version did resolve my issue:

I don’t use anything of GNUstep, instead I set up a bare minimum root class which is basically a wrapper to the objc-runtime - see: https://github.com/cyclaero/ContentCGI/blob/master/plugins/CyObject.h https://github.com/cyclaero/ContentCGI/blob/master/plugins/CyObject.m

Adding the flag -fobjc-runtime=gnustep-2.0 did not change anything. Neither for the previous version (it still crashes) nor for the present fixed version, which works well with or without that flag.

I tried also -fobjc-nonfragile-abi but this did not change anything either.

My programs work well on the latest macOS as well as on the latest FreeBSD (x86-32, x86-64, arm7). Do I really need any of these flags, and if yes, which would you recommend?

Thank you very much for resolving the issue so quickly.

Best regards

Rolf

davidchisnall commented 5 years ago

The code path with the crash was only in the old-ABI upgrade path, so I'm surprised you hit it with the new ABI. Are you using clang70 or newer? The new ABI was introduced in clang 7 and the FreeBSD port has a bug fix back ported that may be needed (it shouldn't be triggered in real code, but it was in the GNUstep-base configure checks).

In terms of flags, don't use -fobjc-nonfragile-abi. It was deprecated years ago and should be killed off. On macOS, XCode automatically adds something like -fobjc-runtime=macos-10.8. When targeting this runtime, you want -fobjc-runtime=gnustep-{version}. I'd recommend switching to the 2.0 ABI, because that gives you better reflection metadata and smaller binaries. Please report any issues you might have. One caveat with the new ABI: It changed the representation of NSConstantString, so if you're providing an implementation of this for non-Apple platforms then you'll need to update it. You can see the representation for full objects here: https://github.com/gnustep/libobjc2/blob/master/Test/Test.h

Strings less than 8 7-bit ASCII characters long will be encoded in the pointer value and so won't require allocation. The layout will be (assuming a sane interpretation for C bitfields, don't actually rely on this):

  struct nstinystring
  {
    uintptr_t char0  :7;
    uintptr_t char1  :7;
    uintptr_t char2  :7;
    uintptr_t char3  :7;
    uintptr_t char4  :7;
    uintptr_t char5  :7;
    uintptr_t char6  :7;
    uintptr_t char7  :7;
    uintptr_t length :5;
    uintptr_t tag    :3;
  }; 

The value of the tag (low 3 bits) will be 4 (GNUstep uses other values for NSNumber, but the compiler doesn't currently encode them). Any characters after the end will be 0, so you can always compare short strings for equality by a simple pointer comparison.

cyclaero commented 5 years ago

I am using clang from base of FreeBSD 12.0-RELEASE-p3, which happens to be: FreeBSD clang version 6.0.1 (tags/RELEASE_601/final 335540) (based on LLVM 6.0.1)

In order to trigger the crash, nothing of GNUstep needs to be involved. Actually, the code snippet, which demonstrates the issue even doesn’t instantiate any object from the dummy class A. The crash happens before the entry point of main() is hit.

My projects which are targeted to FreeBSD need only a small subset of NSObject's functionality, no Foundation and nothing else, and yesterday I removed all -fobjc-xxx flags, since everything is now working well without those flags and with the fixed version of libobjc2-2.0.

I don’t have any clang 7 installed here, and I would prefer to stay with the compilers in the base system, because this facilitates deployment of my programs on the various systems which I maintain.

I tried to build libobjc2-2.0 with OLDABI_COMPAT flag set to OFF in the CMake configuration using said clang 6.0.1, and this errors out. In addition, I needed to remove -fno-common from the CFLAGS when building some of my Objective-C based code, since with that flag in place, the linker complained about error: duplicate symbol: objc_method_cache_version.

Clang 7 will come with FreeBSD 13, and until then I will stay with the present fixed version of libobjc2-2.0 and the old ABI which it happens to provide for clang 6.

Thank you very much once again for taking your time on this.

davidchisnall commented 5 years ago

Note that everything in ports has now moved to the 2.0 ABI (which is a breaking change, so you can't mix 1.x and 2.x ABI code in the same library). I expect to toggle the OLDABI_COMPAT switch to OFF by default in the packaged version at some point in the next year, so I would strongly recommend moving to clang 7.x and the new ABI. Please can you open another issue about the duplicate symbol? I expect that we're missing an extern in a header...

cyclaero commented 5 years ago

I understand that „everything in ports“ actually means „everything GNUstep in ports“ and „so you can’t mix ...“ refers to the general you but not to me, since I don’t use anything from GNUstep.

That said, I am fine with your plans to dropping the old ABI at some point next year, since most probably FreeBSD 13 should then have been released already and it will come with clang >= 7 or even 8, and therefore libobjc2 won’t be dependent on a compiler which is not in the base system. I will do the move to clang >= 7 by then.

Concerning the duplicate symbol error, I will try to set up a reduced test case which demonstrates the issue. In the moment this happens when linking 2 objc object files dynamically against libobjc2 together into a custom library. More than 10 thousand lines of code are involved, and I must not remove the part which triggers the error. I will file the issue report, once I can reproduce it with a test case.

davidchisnall commented 5 years ago

I understand that „everything in ports“ actually means „everything GNUstep in ports“ and „so you can’t mix ...“ refers to the general you but not to me, since I don’t use anything from GNUstep.

Everything in ports means 'everything that is built in the FreeBSD ports collection and depends on the GNUstep Objective-C Runtime (a.k.a. libobjc2)'. 'You can't mix' means 'if you attempt to mix within the same process, libobjc2 will refuse to load your code'.

That said, I am fine with your plans to dropping the old ABI at some point next year, since most probably FreeBSD 13 should then have been released already and it will come with clang >= 7 or even 8, and therefore libobjc2 won’t be dependent on a compiler which is not in the base system. I will do the move to clang >= 7 by then.

Major FreeBSD releases have a rough cadence of once every 2 years, so I imagine that there will be at least a year in which you're supporting

Concerning the duplicate symbol error, I will try to set up a reduced test case which demonstrates the issue. In the moment this happens when linking 2 objc object files dynamically against libobjc2 together into a custom library. More than 10 thousand lines of code are involved, and I must not remove the part which triggers the error. I will file the issue report, once I can reproduce it with a test case.

I believe this is now fixed.

cyclaero commented 5 years ago

Be assured that I appreciate your efforts very much. I guess, I understood the point very well, and I’ll see whether I need to move forward to a newer clang before FreeBSD does.

Concerning the duplicate symbol issue, I verified that PUBLIC is a non-empty #define only on Windows, and said issue is not resolved on FreeBSD. When I replace line 8 of the file /usr/local/include/objc/objc-visibility.h by #define PUBLIC extern then my issue has gone. However, I can’t tell whether this would open a bunch of other issues in the 107 ports which depend on libobjc2 and which I don’t u... - ehmmm forget it :-D

davidchisnall commented 5 years ago

Ooops, sorry. Should actually be fixed now.