libtool: warning: 'Foo.o' has no symbols on Xcode 26.4

I've recently installed Xcode 26.4, and it seems like all of our libraries now give multiple linker error when building of the format libtool: warning: 'Foo.o' has no symbols

Most of these errors come from extensions over objc types written in objc. (for instance we have some extensions over NSArray in the file NSArray+NSSet.mm)

Is that a new feature of some stricter libtool invocation? or a bug? should I do anything about it?

Answered by DTS Engineer in 885263022

Thanks for confirming. And it turns out that this is easy to reproduce:

  1. Using Xcode 26.4, create a new project from the macOS > Library template, and choose to create a Cocoa static library.

  2. In the resulting .h file, remove the whole @interface block.

  3. And in the .m file, convert the @implementation block to a category. For example:

    @implementation NSUUID (Foo)
    
    - (void)test {
    }
    
    @end
    
  4. Choose Product > Build. This issues the following warning:

libtool: warning: 'Foo.o' has no symbols

This actually makes some degree of sense:

  • Xcode has run libtool to convert the various object (.o) files in the target into a static library (.a).
  • There’s only one object file.
  • It only contains a category.
  • Objective-C categories don’t publish any symbols.

Consider this:

% nm …/Foo.o
0000000000000000 t -[NSUUID(Foo) test]
                 U _OBJC_CLASS_$_NSUUID
0000000000000028 s __OBJC_$_CATEGORY_INSTANCE_METHODS_NSUUID_$_Foo
0000000000000048 s __OBJC_$_CATEGORY_NSUUID_$_Foo
0000000000000014 s l_OBJC_CLASS_NAME_
0000000000000088 s l_OBJC_LABEL_CATEGORY_$
0000000000000018 s l_OBJC_METH_VAR_NAME_
000000000000001d s l_OBJC_METH_VAR_TYPE_
0000000000000000 t ltmp0
0000000000000014 s ltmp1
0000000000000018 s ltmp2
000000000000001d s ltmp3
0000000000000028 s ltmp4
0000000000000088 s ltmp5
0000000000000090 s ltmp6
0000000000000630 s ltmp7

All the symbols are private (so t rather than T), except _OBJC_CLASS_$_NSUUID, which is an import.

This lack of symbols is the reason why the static linker has the -ObjC flag, to force it to include Objective-C stuff even though it doesn’t look like it’s being referenced anywhere.

Looking at the libtool man page, I see it has a -no_warning_for_no_symbols option. Adding that to the the target’s Other Librarian Flags build setting eliminates this warning.

So, this warning is reasonable enough — under normal circumstances a .o file that exports no symbols is nonsense — but in the case of an Objective-C static library that contains just categories it makes sense to suppress it.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

it seems like all of our libraries

These are static libraries, right?

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi, I'm having the same issue. Yes, mine are in a static library.

Thanks for confirming. And it turns out that this is easy to reproduce:

  1. Using Xcode 26.4, create a new project from the macOS > Library template, and choose to create a Cocoa static library.

  2. In the resulting .h file, remove the whole @interface block.

  3. And in the .m file, convert the @implementation block to a category. For example:

    @implementation NSUUID (Foo)
    
    - (void)test {
    }
    
    @end
    
  4. Choose Product > Build. This issues the following warning:

libtool: warning: 'Foo.o' has no symbols

This actually makes some degree of sense:

  • Xcode has run libtool to convert the various object (.o) files in the target into a static library (.a).
  • There’s only one object file.
  • It only contains a category.
  • Objective-C categories don’t publish any symbols.

Consider this:

% nm …/Foo.o
0000000000000000 t -[NSUUID(Foo) test]
                 U _OBJC_CLASS_$_NSUUID
0000000000000028 s __OBJC_$_CATEGORY_INSTANCE_METHODS_NSUUID_$_Foo
0000000000000048 s __OBJC_$_CATEGORY_NSUUID_$_Foo
0000000000000014 s l_OBJC_CLASS_NAME_
0000000000000088 s l_OBJC_LABEL_CATEGORY_$
0000000000000018 s l_OBJC_METH_VAR_NAME_
000000000000001d s l_OBJC_METH_VAR_TYPE_
0000000000000000 t ltmp0
0000000000000014 s ltmp1
0000000000000018 s ltmp2
000000000000001d s ltmp3
0000000000000028 s ltmp4
0000000000000088 s ltmp5
0000000000000090 s ltmp6
0000000000000630 s ltmp7

All the symbols are private (so t rather than T), except _OBJC_CLASS_$_NSUUID, which is an import.

This lack of symbols is the reason why the static linker has the -ObjC flag, to force it to include Objective-C stuff even though it doesn’t look like it’s being referenced anywhere.

Looking at the libtool man page, I see it has a -no_warning_for_no_symbols option. Adding that to the the target’s Other Librarian Flags build setting eliminates this warning.

So, this warning is reasonable enough — under normal circumstances a .o file that exports no symbols is nonsense — but in the case of an Objective-C static library that contains just categories it makes sense to suppress it.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hello Quinn

I run into the same problem but above solution doesnt fixe the issue for me. I have other code which links to this static library and makes use of these categories and can not find the code and fails at runtime like this:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSTaggedPointerString intergerValueSupportingHex]: unrecognized selector sent to instance 0xa6bf563335312a11'

in this case intergerValueSupportingHex is the category I defined in the universal library (ulib) which I use from another library.

This is not a problem when building with dynamic libraries, only with static ones. And given the categories are code which is linked to the project, that code should have been found and corresponding symbols should be exported. Hence the supression of the warning will not solve the problem. The issue is not that we have a warning, the issue is that we have code which is not being found at runtime and thats very puzzling.