Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create static library for iOS without making all symbols public

This question has been asked before, but digging into the documentation for the various development tools it seems like this is possible, just not obvious.

Motivation: Making a static library for use by other iOS developers. Some symbols in the library will cause problems if exported so I wish to make them internal-only symbols. With a dynamic library this is easy, just use -exported_symbols_list libtool (ld) argument and list the ones you want public. libtool documentation will not allow this argument for static libraries.

Library has several ObjectiveC .m files that use code from each other. Only one class in the group needs to be made public to users of the final .a static library file.

Tried libtool -exported_symbols_list publicsymbols.exp but that argument to libtool is not supported with -static for static libraries.

Can't make the symbols private with attributes (if that'd even work) because they are needed by the other .m files in the group.

looks like ld can take several .o files and link them together into a new .o file (via the -r argument) and it doesn't have the "dynamic only" disclaimer for the -exported_symbols_list argument (which could just be unclear documentation...).

just as a test I build my project with Xcode so I have all the .o files made, and then try to call ld on the command line, like so:

ld -r -x -all_load -static -arch armv6 -syslibroot {path} 
   -filelist /Users/Dad/ABCsdk/iphone-ABClib/build/ABCLib.build/Distribution-iphoneos/ABCLib-device.build/Objects-normal/armv6/ABCsdk.LinkFileList 
   -exported_symbols_list {exp file path} -o outputfile.o

where the {path} type things have long paths to the appropriate places in there.

but I get errors like the following:

/usr/bin/ld_classic: /Users/Dad/ABCsdk/iphone-ABClib/build/ABCLib.build/Distribution-iphoneos/ABCLib-device.build/Objects-normal/armv6/ABCmain.o incompatible, file contains unsupported type of section 3 (_TEXT,_picsymbolstub4) in load command 0 (must specify "-dynamic" to be used)

so something seems wrong there...

Anyone know a clever way to make this work? Thanks.

like image 358
Dad Avatar asked Aug 01 '11 04:08

Dad


People also ask

How can I use an .a static library in Swift?

Yes, you can use static libraries in Swift. Go to your Build Phases and under "Link Binary With Libraries" and add them there. Alternatively, you can go under Build Settings and in "Search Paths" append the "Library Search Paths" value to include the path to the folder that your . a file is in.

What creates static libraries?

Static Linking and Static Libraries is the result of the linker making copy of all used library functions to the executable file. Static Linking creates larger binary files, and need more space on disk and main memory. Examples of static libraries (libraries which are statically linked) are, .

What is static library in IOS?

Static libraries and frameworks are different kinds of things. A static library is a collection of compiled object files combined into a single library file, that may be linked into an app or framework target just like single object files are linked.

Where do I put my static library?

Static libraries belong next to their corresponding dynamic libraries, and in accordance with the FHS. Keep in mind that static libraries are usually only needed to build software, not run it.


2 Answers

This is really not possible, I'm sorry to say. It has to do with the way static libraries work. A static library is little more than a bunch of object *.o files bundled together, but a dynamic library is a loadable binary image, just like an executable.

Suppose you have four files,

  • common.c defines common, which is "private"
  • fn1.c defines fn1, which calls common.
  • fn2.c defines fn2, which calls common.
  • other.c defines other.

In a dynamic library, the linker bundles everything up into one big chunk of code. The library exports other, fn1, and fn2. You have to load the entire library or none of it, but two programs can both load it without putting multiple copies in memory. The entry point to common is simply missing from the symbol table — you can't call it from outside the library because the linker can't find it.

Note that an application and a shared library have essentially the same format: an application is basically a shared library that only exports one symbol, main. (This is not exactly true, but close.)

In a static library, the linker never runs. The files all get compiled into *.o files and put into a *.a library archive. Internal references will be unresolved.

Suppose your application calls fn1. The linker sees an unresolved call to fn1, and then looks through the libraries. It finds a definition for fn1 in fn1.o. Then the linker notices an unresolved call to common, so it looks it up in common.o. This program won't get the code from fn2.c or other.c, because it doesn't use the definitions from those files.

Static libraries are very old, and they do not have any of the features of dynamic libraries. You can think of a static library as basically a zip file full of compiled source code, unlike a dynamic library which is linked together. Nobody ever bothered to extend the archive format to add symbol visibility. When you link with a static library, you get the same result as if you had added the library's source code to your program.

The short version: A dynamic library has one symbol table of all of the exported symbols, but none of the private symbols. In the same way, an object file has a list of all of its extern symbols but none of the static ones. But a static library has no symbol table, it is just an archive. So there is no mechanism to make code private to a static library (other than defining objects static, but that doesn't work for Objective-C classes).

If we knew why you were trying to do this, perhaps we could give you a suggestion. (Is it for security? Name clashes? All of these questions have solutions.)

like image 55
Dietrich Epp Avatar answered Oct 03 '22 03:10

Dietrich Epp


This is possible! As Dietrich said, all exported symbols in .o files in a static library are public, and if a file needs to refer to a symbol in another .o file it needs to be exported from that file (and therefore public). But there's an easy workaround - pre-link all of your .o files into a single one. Then you only need to export the public symbols.

This is apparently called "Single Object Prelinking", and there's an option to do it in XCode that treert mentioned. But you can do it with just standard command line tools (example repo here):

Check it out (this is on Mac).

First lets create some test files

$ cat private.c
int internal_private_function() {
    return 5;
}
$ cat public.c
extern int internal_private_function();

int public_function() {
    return internal_private_function();
}

Compile them

$ clang -c private.c -o private.o
$ clang -c public.c -o public.o

Add them to a static library (it's basically a zip file, but in a decades old format).

$ ar -r libeverything_public.a public.o private.o

Check what symbols are in it.

$ objdump -t libeverything_public.a

libeverything_public.a(private.o):  file format Mach-O 64-bit x86-64

SYMBOL TABLE:
0000000000000000 g     F __TEXT,__text _internal_private_function

libeverything_public.a(public.o):   file format Mach-O 64-bit x86-64

SYMBOL TABLE:
0000000000000000 g     F __TEXT,__text _public_function
0000000000000000         *UND* _internal_private_function

Ok as you can see both functions are visible, and both symbols are g which means global.

Now lets prelink into a single file and then put that on its own in a static library.

$ ld -r -o prelinked.o private.o public.o
$ ar -r libeverything_public_prelinked.a prelinked.o
$ objdump -t libeverything_public_prelinked.a

libeverything_public_prelinked.a(prelinked.o):  file format Mach-O 64-bit x86-64

SYMBOL TABLE:
0000000000000020 l     O __TEXT,__eh_frame EH_Frame1
0000000000000038 l     O __TEXT,__eh_frame func.eh
0000000000000060 l     O __TEXT,__eh_frame EH_Frame1
0000000000000078 l     O __TEXT,__eh_frame func.eh
0000000000000000 g     F __TEXT,__text _internal_private_function
0000000000000010 g     F __TEXT,__text _public_function

Similar result - they're in one file but both still present and global. Finally let's filter them out (this is Mac specific). We need a list of symbols to export:

$ cat exported_symbols_osx.lds
_public_function

Then use the -exported_symbols_list option.

$ ld -r -exported_symbols_list exported_symbols_osx.lds -o prelinked_filtered.o private.o public.o
$ ar -r libfiltered_prelinked.a prelinked_filtered.o
ar: creating archive libfiltered_prelinked.a
$ objdump -t libfiltered_prelinked.a

libfiltered_prelinked.a(prelinked_filtered.o):  file format Mach-O 64-bit x86-64

SYMBOL TABLE:
0000000000000000 l     F __TEXT,__text _internal_private_function
0000000000000020 l     O __TEXT,__eh_frame EH_Frame1
0000000000000038 l     O __TEXT,__eh_frame func.eh
0000000000000060 l     O __TEXT,__eh_frame EH_Frame1
0000000000000078 l     O __TEXT,__eh_frame func.eh
0000000000000010 g     F __TEXT,__text _public_function

Tada! _internal_private_function is now a local symbol. You can add the -x option (or alternatively run strip -x) to change the name to a random meaningless value (here l001).

$ ld -r -x -exported_symbols_list exported_symbols_osx.lds -o prelinked_filtered.o private.o public.o
$ objdump -t prelinked_filtered.o

prelinked_filtered.o:   file format Mach-O 64-bit x86-64

SYMBOL TABLE:
0000000000000000 l     F __TEXT,__text l001
0000000000000020 l     O __TEXT,__eh_frame EH_Frame1
0000000000000038 l     O __TEXT,__eh_frame func.eh
0000000000000060 l     O __TEXT,__eh_frame EH_Frame1
0000000000000078 l     O __TEXT,__eh_frame func.eh
0000000000000010 g     F __TEXT,__text _public_function

Here's what Apple's linker has to say about -x:

Do not put non-global symbols in the output file's symbol table. Non-global symbols are useful when debugging and getting symbol names in back traces, but are not used at runtime. If -x is used with -r non-global symbol names are not removed, but instead replaced with a unique, dummy name that will be automatically removed when linked into a final linked image. This allows dead code stripping, which uses symbols to break up code and data, to work properly and provides the security of having source symbol names removed.

All of this is the same on Linux except -exported_symbols_list. On Linux I think you have to use --version-script with a file like this:

V0 {
  global:
    _public_function;
  local:
    *;
};

But I haven't tested this yet. Both this file and the exported_symbols_list files support wildcards.

like image 36
Timmmm Avatar answered Oct 03 '22 02:10

Timmmm