Mach-O

Preceding article

  1. Multi-environment configuration
  2. Mach-o and XCode compilation environment configuration

Continuing with the previous article, this article continues with some xConfig file writing

xconfig

$(inherited) = $(inherited) = $(inherited

CMD = objdump --macho --syms ${MACH_PATH}
CMD = $(inherited) | grep LC
Copy the code

Eventually the CMD = objdump, macho, syms ${MACH_PATH} | grep LC, when set other link flag, the link may be multiple libraries, can be in this way to set OTHER_LDFLAGS.

View the Mach -o commands

Objdump --macho -private-header ${MACH_PATH} // Check mach-header otool -l ${MACH_PATH} // Check mach-header objdump --macho -d Objdump --macho --exports-trie ${MACH_PATH Objdump --macho --indirect-symbols ${MACH_PATH} objdump --macho --reloc ${MACH_PATH} otool -r ${MACH_PATH} // View the relocation symbol tableCopy the code

To further understand the structure of mach-o files,

Compilation process

The compiler takes the source code.cpp .mm .mCompiled into.oFile (object file), the connector then links all.oFiles are generated.outFile (executable file). Let’s get a feel for the whole compilation process. The following source files are availablemain.m Run the following command to generate.ofile

clang -c main.m
Copy the code

After executing the above command, you get a main.o file in the sibling directory. To see the nature of the file, run the file command.

As you can see, thismain.oThe file is based onx86_64Instruction setMach-OFiles, of course, can also be passedobjdump --machoTo see the contents of each section. The generated object file.oThe connector is not executed, and the object file does not contain the inclusion information necessary for the UNix program to be executed and loaded. What to make of this paragraph above?

  • Compiled into.oThe process of turning what can be turned into machine code into machine code
  • Classify all symbols and place the external symbol table in the relocation symbol table

generate.oAfter file, executeobjdump --macho --reloc main.o As you’ll see, there are a lot of relocation symbols. The following figure shows the meaning of each fieldGave us an idea that we could pass the test.oFile to see how an API is being used.

symbol

The symbol table:

  • Symbol table: a table of symbols, containing the name and address of the Symbol
  • String table: save symbol name
  • Indirect Symbol table: An Indirect Symbol table that stores the external symbols used

Global symbols and local symbols

Define the following variables in main.mAfter buildobjdump --macho --syms ${MACH_PATH} We can see thatstaticThe opening variables become local symbols, and the essential difference between global and local symbols is the difference in visibility. The default visibility of a symbol is what it is defined to be, like a global variable, that’s a global symbol, but at the same time we can change the visibility of that symbol, right

-fvisibility: clang parameter default: symbols defined with it will be exported. Hidden: Symbols defined with this will not be exported. int hidden_y __attribute__ ((visibility("hidden"))) = 1; Double default_y __visibility ((" limit "))) = 2;Copy the code

The best way to hide global symbols is to make them static. You can also use the __attribute__ ((visibility(“hidden”)) method to hide.

Import and export symbols

Enter the following code in main.mperformobjdump --macho --exports-trie ${MACH_PATH}View the export symbol tableDoes a global export symbol have to be a global symbol? We can control it through the linker. For a dynamic library, we know that it is added at runtime, so for external users of the dynamic library, the function of the dynamic library is to provide symbols. The indirect symbol table, mentioned earlier, is the external symbol table that holds the current Mach-o file. The same code runsobjdump --macho --indirect-symbols ${MACH_PATH}To view the indirection symbol tableYou can seeNSLogIt’s an indirect sign. It’s made up ofFundationThis dynamic library provides.

Through the above analysis, the following conclusions can be drawn:

  • Global symbols can become export symbols and can be used by the outside world
  • When a strip symbol is used, the indirect symbol table cannot be deleted, and the exported symbol of the dynamic library cannot be stripped.

So for oc’s code (class…) What does symbol visibility look like? Compile the addLGOneObjectThis classperformobjdump --macho --exports-trie ${MACH_PATH}View the export symbol table as followsWe find that oc code is exported by default, which means it is a global symbol. So when we’re defining lots of classes and lots of global symbols, the exported symbol table is going to be very large, and it’s going to take up a lot of space.

When defining an OC dynamic library, we can strip out symbols that we don’t need to expose. We can use connectors to make the symbols that we don’t need to expose not exported

OTHER_LDFLAGS=$(inherited) -Xlinker -unexported_symbol -Xlinker _OBJC_CLASS_$_LGOneObject
OTHER_LDFLAGS=$(inherited) -Xlinker -unexported_symbol -Xlinker _OBJC_METACLASS_$_LGOneObject
Copy the code

Add symbols that do not need to be exported to the xconfig file. After build, executeobjdump --macho --exports-trie ${MACH_PATH}View the export symbol table as followsYou can see that the export symbol is gone_OBJC_CLASS_$_LGOneObjectand_OBJC_METACLASS_$_LGOneObjectIf you strip out the non-global symbols, you can reduce the size of the dynamic library. The linker can also use other parameters to see what libraries are used and what all the symbols are. Add the following code to the xConfig file

OTHER_LDFLAGS=$(inherited) -Xlinker -S -Xlinker -map -Xlinker /Users/meilinli/Downloads/Symbol.text
Copy the code

After the build, open the symbol. Text file as shown below:This file lists how many object files were generated and what libraries were used

weak symbol

  • Weak Reference symbol: Indicates that this undefined symbol is a Weak reference. If the dynamic linker cannot find a definition for this symbol, it sets it to 0. The linker sets this symbol to the link flag
  • Weak Definition symbol: Indicates that this symbol is a Weak definition symbol. If the static linker or dynamic linker finds another (non-weak) definition for this symbol, the Weak definition is ignored and only symbols in the merge section are marked as Weak definitions. A weak definition symbol and a weak reference symbol are declared as follows
Void weak_import_function(void) __atrribute__((weak_import))// Weak application void weak_function(void) __Attribute__ ((weak)) weak definitionCopy the code

Declaration as a weakly defined symbol does not affect whether it is exported or not.

inWeakSymbol.hDeclare a weakly defined symbol inWeakSymbol.mIn the implementationThen import in MainWeakSumbol.hThis header file is redefined in main.mweak_function()This functionIf theWeakSymbol.hIn theweak_function()If it’s not weakly defined, you get an error, but if it’s weakly defined, you don’t get an error.

For weak application notation, inWeakImportSymbol.hDefined in thewark_import_function()Then use this symbol in main.m (do not compile at this timeWeakImportSymbol.mThe code is shown belowIf there is a compile error while compiling, then we can pass a-UThe (undefined) option tells the connector that the symbol is dynamically searched and that it is not needed.

OTHER_LDFLAGS=$(inherited) -Xlinker -U -Xlinker _weak_import_function
Copy the code

No more errors will be reported when compiling. So what does this do? When using a dynamic library, we can make the whole dynamic library weak reference, so that when we use the dynamic library, we will not be unable to find the error.

Common symbols

  • Common symbol: Global symbol that is not initialized when defined
  • Connector Settings:
    • -d: Specifies the mandatory common symbol
    • – Commons: Specifies how to respond to the common symbol

Enter the following code in main.m,

The second is the global symbol that is not defined. It is the common symbol. The common symbol is used to delete the undefined symbol when the definition is found.

Re-export (re-export)

The NSLog symbol from the Fundation library is used in the main.m file above, so can we make the current Mach-o file also use the NSLog symbol? Introduce a concept to re-export. When the symbol is exported again, the symbol is placed in the mach-o exported symbol table generated by main.m. A symbol can be used as an export symbol by giving it an alias.

OTHER_LDFLAGS=$(inherited) -Xlinker -alias -Xlinker _NSLog -Xlinker ML_NSLog
Copy the code

Note that you can only alias external symbols. After compiling, look at the exported symbol tableYou can seeML_NSLogAs a re-exported symbol, it is placed in the exported symbol table. What are the benefits of this? When one dynamic library A links to another dynamic library B, if we re-export dynamic library B to dynamic library A, then programs using dynamic library A will also see the symbols of dynamic library B.

Swift symbol

Look at the exported symbol table when you add a SWIFT file to your code and compile itYou can see that there are a lot more symbols, so once you know the symbols, you can strip the symbols to reduce the size of the library

strip

  • Global symbols –> export symbols. When the dynamic library is stripped, you can strip all symbols that are not global symbols
  • Apps generally don’t want people to use their symbols, so apps can strip all local and global symbols, keeping only the indirect symbol table
  • Static library: Static library = collection of objects + relocating symbol table, so static library can only be stripe debugging symbols

So in terms of symbols, when an APP uses a static library, the size will be smaller, because when an APP uses a static library, the symbol table of the static library will be placed in the symbol table of the APP. When we strip, we can strip all symbols.

dead code strip

-dead_strip: remove functions and data that are unreachable by the entry point or exported symbols
Copy the code

Dead_strip: Stripped of non-exported symbols that are not used.

Principle of strip

supplement

When we use the above commands, some commands are vague and can be passedmanCommand to view some descriptions of this command toldFor example, on the terminalman ld foundldThere are a number of options that you can put in/{command to find}See the figure belowIt’s possible to match a number of results, so pressnorshift + nLook down or look up.