In iOS development, static library conflicts are often encountered, xcode error keywords are duplicate symbol XXX; There are two common causes of conflict

  1. The same static library with different names was introduced in the project

Taking Openssl as an example, the same source code package generates two static libraries libopenssl.a and openssl.a, when introduced into the project, an error will be reported

The same static library with a different name can cause conflicts.

In this case, the compiler optimizes static library links with the same name to prevent collisions. The optimization idea is that if a static library is already bound to a symbolic link when the compiler makes a symbolic link, the subsequent static library with the same name will not be linked. This issue can be affected if other Linker Flags under Xcode Build Setting is changed

  1. Different SDKS such as moduleA and moduleB are introduced, where moduleA and moduleB reference the same static library

According to Xcode error message, we know that the nature of static library conflict is actually a Symbol conflict, and is caused by the global Symbol conflict; The global symbol of the MachO file can be viewed by LLVM nm-guaj xx.o

The way to solve the static library conflict is to solve the symbol conflict, we can start from the following aspects:

  • Delete conflicting symbols:

We all know that static libraries are essentially collections of compiled.o object files, so conflicts can be resolved by removing the.o file where the conflicting symbol resides

1. View the schema supported by the. A static library. You can run the lipo or file command

lipo -info libssl.a
file libssl.a
Copy the code

2. Split a single architecture based on the architecture where the conflicting symbols reside. Take ARM64 as an example

lipo -thin arm64 libssl.a -output  libssl_arm64.a
Copy the code

If an error is reported, please check that the static library is of a single architecture. Download file type or format. That’s ok


ar -x libssl_arm64.a 

#If only a small number of target files are deleted, 'ar-d xx.o' can be used to delete them individually without repackaging
Copy the code

4. After removing conflicting symbols, repackage to generate a single-architecture static library using libtool or AR

libtool -static -o libssl_arm64_new.a *.o
ar -rcs libssl_arm64_new.a *.o
Copy the code

5. Merge multiple architectural static libraries into one. The first parameter is the name of the synthesized static library, followed by the library to be merged

lipo -creata -output libssl_new.a libssl_arm64_new.a libssl_armv7_new.a
Copy the code
  • Conflicting symbol renaming
    • If you have the source code, you can modify the symbol name of the source code directly to resolve the conflict, no difficulty, just a matter of work
    • LLVM objCopy — re-define — syms can be used to change the symbol name. Rename_symbols stores the changed symbol and the new symbol name. The format requirements can be viewed by running the man LLVM objCopy command

 /usr/local/opt/llvm/bin/llvm-objcopy --redefine-syms rename_symbols xxx.o

Copy the code

**error: llvm-objCopy error: llvm-objCopy error: llvm-objCopy error: llvm-objCopy Unsupported load command (CMD =0x2d) ** Load command: LC_LINKER_OPTION However, if it is changed to.mm, it can be normally modified.

If you know, please contact me, welcome to comment.

  • Modifies the types of conflicting symbols

There are two problems to be solved: how to change the conflicting symbol type from Global to Local, and how to link the symbol to other object files in the static library after the symbol is changed

The solution is to link the whole static library into an object file through ld command, so as not to cause the problem that the internal link of the static library cannot be linked to the symbol after hiding the global symbol.

The symbol type can be set using ld parameters. By default, all non-exported symbols are local and all exported symbols are global

Unexported_symbols_list parameter sets a list of unexported symbols

Exported_symbols_list Sets the exported_symbols_list,

You can use shell scripts to handle the procedure


ARCH_LIST=("armv7" "arm64" "x86_64" "i386")

ARCH_COUNT=${#ARCH_LIST[@]}



do_fix()

{

ARCH=$1;

echo "fix for ${ARCH}..."



rm -rf ${ARCH}

mkdir ${ARCH}



#Architecture extract

lipo -thin ${ARCH} libcrypto.a -output "./${ARCH}/${ARCH}.a"



#Extract the static library. O object file

cd "./${ARCH}"

mkdir "objs"

ar -x "${ARCH}.a"

`mv *.o objs`

mv "__.SYMDEF SORTED" "objs"



#The target file is linked in advance, which can solve the problem that the internal link cannot be linked after the symbol is hidden, and the link has ended before the hidingcd "objs" `ld -r *.o -o combine.o` cp "combine.o" .. /
#Output global symbols as the source of hidden symbols
cd ..
nm -g -j combine.o > hidden_symbols

#Removes global symbols from specified files (symbols that need to be exposed to the outside world)`cat .. /global_symbols > tmp_symbols` `cat "\n" >> tmp_symbols` `cat hidden_symbols >> tmp_symbols` `sort tmp_symbols|uniq -u >  real_hidden_symbols`
#Reconnect the hidden symbol
ld -x -r -unexported_symbols_list real_hidden_symbols combine.o -o hidden.o
nm -g hidden.o > exits_symbols

#Generate a new package
ar -rv "fix.a" hidden.o
cd ..
}

for ((i=0; i < ${ARCH_COUNT}; i++))
    do
    do_fix ${ARCH_LIST[i]} 
done

LIB_PATHS=( ${ARCH_LIST[@]/#/} )
LIB_PATHS=( ${LIB_PATHS[@]/%//fix.a} )
lipo ${LIB_PATHS[@]} -create -output fix_libcrypto.a
Copy the code
  • Other linker flag is set to the other Linker flag so that static libraries will be linked (unused static libraries will not be linked by default). Implemented in the console during static linking. Class XXX is implemented in both when implemented again. Implemented in the console during static linking

Advantages and disadvantages of ** analysis: **

  • The advantage of deleting static library conflicting files is that it can be achieved without source code, but the disadvantage is also obvious. The library containing the deleted object file cannot be used alone, and the problem of undefine Symbols may also be caused if the conflicting versions are inconsistent

  • Change symbol name: The disadvantage of unification is that the call needs to be changed to a new symbol

    • In the case of source code to modify the name of the conflicting symbols, the general workload is relatively large, need to modify the symbol definition and symbol call, the subsequent version of the static library upgrade will also cause a certain amount of work
    • If the symbol table in the static library is directly modified, the subsequent version upgrade also needs to rename the operation each time (can be implemented through scripts), the conflicting libraries can be used at the same time, applicable to the symbol compatibility of different versions of the library.
  • The advantage of hiding global symbols is that the conflicting libraries can exist at the same time, and the upgrade does not involve symbol name modification. The disadvantage is that the static library is integrated into an object file, and the compiler cannot optimize the on-demand links of the static library, resulting in volume increase.

  • The dynamic library wrapping method, which involves the modification of the library introduction method, is friendly to different versions of the conflicting libraries.

According to the analysis of static library conflicts, most of them are caused by the introduction of the same static library in different libraries. Therefore, when we make SDK, if we need to rely on the third-party static library, we are required not to integrate directly into the SDK, but to prompt the business side to actively integrate the dependent static library by means of integration documents

Most of the well-known third-party SDKS currently do the same

reference

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

An objcopy equivalent for Mac / iPhone?

llvm objcopy