From 74cfe461234fcf76aadb30ed686f281f06b555cd Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 21 Mar 2008 02:13:50 +0000 Subject: [PATCH] ld64-62.1.tar.gz --- ChangeLog | 184 +- doc/man/man1/ld.1 | 1842 +++++++++++++++++ ld64.xcodeproj/project.pbxproj | 19 +- src/MachOReaderDylib.hpp | 2 +- src/MachOReaderRelocatable.hpp | 273 +-- src/MachOWriterExecutable.hpp | 191 +- src/ObjectDump.cpp | 2 +- src/ObjectFile.h | 9 +- src/Options.cpp | 46 +- src/Options.h | 4 +- src/SectCreate.cpp | 2 +- src/ld.cpp | 491 +++-- src/machochecker.cpp | 12 +- src/rebase.cpp | 2 + unit-tests/test-cases/align-modulus/Makefile | 43 + unit-tests/test-cases/align-modulus/align.s | 36 + unit-tests/test-cases/align-modulus/foo.c | 32 + unit-tests/test-cases/align-modulus/foo.exp | 1 + .../test-cases/dead_strip-archive/Makefile | 43 + .../test-cases/dead_strip-archive/foo.c | 7 + .../test-cases/dead_strip-archive/main.c | 37 + .../test-cases/dwarf-debug-notes-r/Makefile | 2 +- .../dwarf-debug-notes-r/expected-stabs | 16 +- unit-tests/test-cases/indirect-dylib/Makefile | 46 + unit-tests/test-cases/indirect-dylib/bar.c | 31 + unit-tests/test-cases/indirect-dylib/foo.c | 31 + unit-tests/test-cases/indirect-dylib/main.c | 33 + unit-tests/test-cases/relocs-asm/relocs-asm.s | 12 +- unit-tests/test-cases/relocs-objc/test.m | 11 +- 29 files changed, 2944 insertions(+), 516 deletions(-) create mode 100644 doc/man/man1/ld.1 create mode 100644 unit-tests/test-cases/align-modulus/Makefile create mode 100644 unit-tests/test-cases/align-modulus/align.s create mode 100644 unit-tests/test-cases/align-modulus/foo.c create mode 100644 unit-tests/test-cases/align-modulus/foo.exp create mode 100644 unit-tests/test-cases/dead_strip-archive/Makefile create mode 100644 unit-tests/test-cases/dead_strip-archive/foo.c create mode 100644 unit-tests/test-cases/dead_strip-archive/main.c create mode 100644 unit-tests/test-cases/indirect-dylib/Makefile create mode 100644 unit-tests/test-cases/indirect-dylib/bar.c create mode 100644 unit-tests/test-cases/indirect-dylib/foo.c create mode 100644 unit-tests/test-cases/indirect-dylib/main.c diff --git a/ChangeLog b/ChangeLog index 7436b2b..ce470b0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,30 +1,133 @@ ------ Tagged ld64-59.4 +2006-09-22 Nick Kledzik + + ld64.xcodeproj/project.pbxproj: change install name back to ld64 + src/Options.cpp: remove support for rolling over to ld_classic + src/ld.cpp: remove support for rolling over to ld_classic + + +----- Tagged ld64-62 + +2006-08-15 Nick Kledzik + + wrong error message when symbol is found in unused indirect library + src/ld.cpp: remove indirect libraries if they are not re-exported + unit-tests/test-cases/indirect-dylib: added test case + + +2006-08-15 Nick Kledzik + + alignment needs to be richer + src/ObjectFile.h: define ObjectFile::Alignment class for tracking rich alignment info + src/ld.cpp: modify SymbolTable::add() to work with new Alignment type + src/MachOReaderRelocatable.hpp: use new Alignment type. Remove alignAtLeast() and handleAnonymousNonLazyPointers() + src/MachOWriterExecutable.hpp: update for new Alignment type, use modulus when calculating layout address + src/ObjectDump.cpp: print richer Alignment info + unit-tests/test-cases/align-modulus: added test case + + +2006-08-11 Nick Kledzik + + remove OPEN_SOURCE conditionals around x86_64 support + + +2006-07-31 Nick Kledzik + + ld64 while linking cc1 [ when dead_strip is ON] + src/ld.cpp: Add ivar fAtomsWithUnresolvedReferences to track atoms not initially resolvable + unit-tests/test-cases/dead_strip-archive: added test case + + +2006-07-31 Nick Kledzik + + x86_64: instructions with immediate and rip-relative operands need to use new relocation types + src/MachOWriterExecutable.hpp: generate new reloc types in -r mode + src/MachOReaderRelocatable.hpp: parse new reloc types + unit-tests/test-cases/relocs-asm/relocs-asm.s: add test cases for new reloc type + + +2006-07-18 Nick Kledzik + + src/MachOReaderRelocatable.hpp: suppress warning about dwarf info parsing for one benign no-op case + the compiler emits when there are not functions in the __text section + + +2006-07-17 Nick Kledzik + + faster debug note generateion + src/ld.cpp: rework collectDebugInfo() to produce all debug notes in one pass, intead of a + pass per .o file. Added timing info for collectDebugInfo() to -print_statistics + unit-tests/test-cases/dwarf-debug-notes-r/Makefile: add expliced -arch to ld -r + unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs: alter for new debug notes order + + +2006-07-17 Nick Kledzik + + ld64 VSIZE is 1.18GB when building Finder ppc64 + src/ld.cpp: fixed typo in createReader() that prevented dylibs from being unmapped + +----- Tagged ld64-61.1 2006-07-11 Nick Kledzik + + ld64-61: gcc DejaGnu tests failing due to -arch followed by unknown architecture name + src/Options.cpp: map ppc750, ppc7400, ppc7450, and ppc970 to ppc. Improve error message - remove conditionals around x86_64 support +2006-07-11 Nick Kledzik ------ Tagged ld64-59.3 + If -arch is missing, rollover to ld_classic does not happen + src/Options.h: make gotoClassicLinker() public + src/ld.cpp: call gotoClassicLinker() if the inferred architecture is ppc or i386 -2006-07-02 Nick Kledzik +----- Tagged ld64-61 - x86_64: instructions with immediate and rip-relative operands need to use new relocation types - back port fix for 4656617 to Inca branch +2006-06-29 Nick Kledzik ------ Tagged ld64-59.2 + ld64 should be renamed to ld + src/Options.cpp: exec() ld_classic if -arch ppc or -arch i386 is seen + src/ld.cpp: alter version string + ld64.xcodeproj/project.pbxproj: change install location to /usr/bin/ld, add symlink from /usr/bin/ld64 + doc/man/man1/ld.1: added + +----- Tagged ld64-60 + +2006-06-28 Nick Kledzik + + Can't link large ppc64 program: ld64 says "bl out of range" + MachOWriterExecutable.hpp: fix branch island generation to work for weak_import functions + and properly chain together branch islands + MachOReaderRelocatable.hpp: improve performance of huge .o file reading by sorted references + only when done 2006-06-28 Nick Kledzik MySQL-36 fails to build with ld64-59 - back port fix to Inca branch + src/MachOReaderRelocatable.hpp: back out fix for 4585335 + src/MachOWriterExecutable.hpp: back out fix for 4585335 + +2006-06-27 Nick Kledzik + + src/MachOReaderRelocatable.hpp: handle N_GSYM without ending :G() since that is how + dwarf debug notes are formed. + +2006-06-23 Nick Kledzik + + + + ld64 doesn't support variant linking -framework fw,_debug + src/Options.cpp: enhance findFramework() to support suffixes ----- Tagged ld64-59 2006-06-22 Nick Kledzik ld64 lost DWARF debug notes - MachOReaderRelocatable.hpp: add fHasUUID so kDebugInfoStabsUUID can be set later + src/MachOReaderRelocatable.hpp: add fHasUUID so kDebugInfoStabsUUID can be set later unit-tests/test-cases/dwarf-debug-notes-r: added test case 2006-06-21 Nick Kledzik @@ -35,7 +138,7 @@ 2006-06-21 Nick Kledzik ld64 seems to offset things incorrectly when using -r - src/MachOWriterExecutable.hpp: in -r mode, virtual sections don't increment address + src/MachOWriterExecutable.hpp: in -r mode, virtual sections should not increment address ----- Tagged ld64-58 @@ -66,7 +169,7 @@ 2006-06-15 Nick Kledzik ld64 bogus duplicate symbol name linking GNU libobjc - src/MachOReaderRelocatable.hpp: only special case Apple's objc runtime objc classes + src/MachOReaderRelocatable.hpp: only special case Apple objc runtime objc classes 2006-06-15 Nick Kledzik @@ -77,7 +180,7 @@ jump table into middle of weak symbol causes error src/MachOReaderRelocatable.hpp: create direct references to the interior of weak symbols - src/MachOWriterExecutable.hpp: don't error on absolute references to interior of weak symbols + src/MachOWriterExecutable.hpp: do not error on absolute references to interior of weak symbols 2006-06-13 Nick Kledzik @@ -284,10 +387,6 @@ * src/MachOReaderRelocatable.cpp: mark non-lazy-pointer atoms as scopeTranslationUnit if targetting a static symbol -2006-04-27 Nick Kledzik - - dyld crashes ungracefully on x86_64 when there is an internal exception - * src/MachOWriterExecutable.cpp: allow non-zero PCRELGOT addends (used by C++ eh frames) 2006-04-21 Nick Kledzik @@ -331,9 +430,6 @@ ----- Tagged ld64-50 -2006-03-29 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix x86_64 addends when -multi_module forces an external relocation 2006-03-29 Nick Kledzik @@ -385,54 +481,18 @@ * src/Options.cpp: setup ReaderOptions.fForFinalLinkedImage * src/MachOReaderRelocatable.hpp: mark .eh symbols kSymbolTableNotIn when building final linked image -2006-03-21 Nick Kledzik - - Inca ld64-45 fatal error with C++ and asm() renaming - * src/MachOReaderRelocatable.hpp: fix Reader::makeReferenceToEH(() to walk relocations to find - target of eh symbol, rather assume target name is eh symbol name less .eh - 2006-03-21 Nick Kledzik ld64 does not parse optional second argument to -filelist * unit-tests/test-cases/filelist: added * src/Options.cpp: in Options::loadFileList() handle comma option -2006-03-21 Nick Kledzik - - 32-bit pointer difference out of range for cxx eh frame - * src/MachOReaderRelocatable.hpp: x86_64 doesn't have anonymous non-lazy-pointers - * src/machochecker.cpp: fix validFile() for x86_64 - * unit-tests/run-all-unit-tests: add x86_64 - * unit-tests/include/common.makefile: don't add -g to all compilers - * unit-tests/test-cases/relocs-asm/relocs-asm.s: add x86_64 test cases - * unit-tests/test-cases/relocs-c/Makefile: fix to work with x86_64 - * src/ld.cpp: add hack to use i386 dylibs if x86_64 don't exist - -2006-03-19 Nick Kledzik - - ld64 crashes whenever I try to link with dylib1.o - * src/MachOReaderRelocatable.hpp: in Reader::addRelocReference() fix local relocations ----- Tagged ld64-47.1 -2006-03-16 Nick Kledzik - - ld64-41 in Leopard doesn't have x86_64 support - * ld64.xcodeproj/project.pbxproj: enable x86_64 for Leopard ----- Tagged ld64-47 -2006-03-14 Nick Kledzik - - * src/Architectures.hpp: redo x86_64 relocation types - * src/MachOReaderRelocatable.hpp: redo x86_64 relocation types, make some section type illegal for x86_64 - * src/MachOWriterExecutable.hpp: redo x86_64 relocation types - -2006-03-13 Nick Kledzik - - ld64 -r does not handle internal relocations for x86_64 - * src/MachOWriterExecutable.hpp: handle internal relocations in Writer::fixUpReferenceRelocatable() - and Writer::addObjectRelocs(). ----- Tagged ld64-46 @@ -466,20 +526,12 @@ ----- Tagged ld64-45 -2006-03-06 Nick Kledzik - - ld64 failed: rel32 out of range when linking a dylib - * src/MachOWriterExecutable.cpp: in Writer::fixUpReferenceFinal add (int32_t) cast 2006-03-06 Nick Kledzik LP64/9A122: ld64: hang when trying to link DiscRecording framework * src/Options.cpp: addSectionAlignment, warn on zero. Use log2() for alignment conversion -2006-03-06 Nick Kledzik - - x86_THREAD_STATE64_COUNT will change, ld64 must adapt - * src/MachOWriterExecutable.hpp: update ThreadsLoadCommandsAtom for new thread status layout ----- Tagged ld64-44 @@ -494,12 +546,6 @@ ----- Tagged ld64-43 -2006-03-03 Nick Kledzik - - RIP-relative offsets aren't handled properly when the instruction has immediate operands - * src/Architectures.hpp: add x86_64::kPCRel32_* - * src/MachOReaderRelocatable.hpp: generate x86_64::kPCRel32_* - * src/MachOWriterExecutable.hpp: process x86_64::kPCRel32_* 2006-03-02 Nick Kledzik diff --git a/doc/man/man1/ld.1 b/doc/man/man1/ld.1 new file mode 100644 index 0000000..835e69e --- /dev/null +++ b/doc/man/man1/ld.1 @@ -0,0 +1,1842 @@ +.TH LD 1 "March 18, 2006" "Apple Computer, Inc." +.SH NAME +ld \- Mach object file link editor +.SH SYNOPSIS +.B ld +[ +.I "option \&..." +] [ +.I "file \&..." +] +.SH DESCRIPTION +The +.I ld +command combines several Mach-O (Mach object) files into one by combining like sections +in like segments from all the object files, resolving external references, and +searching libraries. In the simplest case several object +.I files +are given, and +.I ld +combines them, producing an object file which can either be executed or +become the input for a further +.I ld +run. (In the latter case, the +.B \-r +option must be given to preserve the relocation information.) Unless an output +file is specified, +.I ld +produces a file named +.BR a.out . +This file is made executable only if no errors occurred during the link editing +and there are no undefined symbols. +.SH "UNIVERSAL FILE SUPPORT" +The link editor accepts ``universal'' (multiple-architecture) input files, but +always creates a ``thin'' (single-architecture), standard Mach-O output file. +The architecture is specified using the +.B \-arch +.I " arch_type" +option. If this option is not used, +.IR ld (1) +attempts to determine the output architecture by examining the first object +file encountered on the command line. If it is a ``thin'' +file, its architecture determines that of the output file. If the first input +file is a ``universal'' file, the ``best'' architecture for the host is used. +(See the explanation of the +.B \-arch +option, below.) +.PP +The compiler driver +.IR cc (1) +handles creating universal executables by calling +.IR ld (1) +multiple times and using +.IR lipo (1) +to create a ``universal'' file from the results of the +.IR ld (1) +executions. +.SH "OUTPUT FILE LAYOUT" +.PP +The object files are loaded in the order in which they are specified on the +command line. The segments and the +sections in those segments will appear in the output file in the order they are +encountered in the object files being linked. All zero fill sections will appear +after all non-zero fill sections in their segments. +.PP +Sections created from files with the +.B \-sectcreate +option will appear in the output file last. Section names for sections created +from files are not allowed to overlap with a section name in the same segment +as a section coming from an object file. Sections created from files may be in +a segment which has sections from object files and if so will be loaded at the +end of the non-zero fill sections for that segment. +.PP +If the option +.B \-seglinkedit +is specified, the segment it creates is the last segment in the output file. +.PP +The address of each segment can be specified with +.B \-segaddr, +which takes the segment's name as an argument. +The address of the first segment can alternatively be specified using +.B \-seg1addr, +in which case a segment name is not used. +Segments that do not have a specified +address will be assigned addresses in the order in which they appear +in the output file. A segment's address will be assigned +based on the ending address of the previous segment. +If the address of the +first segment has not been specified by name, +its assigned address will be +the specified (via +.BR \-seg1addr ) +or default first segment address. +If neither flag is used to specify the first segment's address, its +default address is zero +for all formats except the demand-paged executable format +.SM (MH_EXECUTE), +in which case the default first address is the value of the segment alignment. +.PP +For demand-paged executable format +.SM (MH_EXECUTE) +output files, +if none of the segments' addresses covers address zero through +the value of the segment alignment, a segment with no access protection +will be created to cover those addresses. This segment, named +.SM "``_\|_PAGEZERO''," +is created so that any attempt to dereference a NULL pointer will cause a +memory exception. +.PP +The entry point of the output file is the beginning of +the first section in the first segment (unless the +.B \-e +option is specified). +.SH STATIC ARCHIVE LIBRARIES +.PP +.I ld +supports two types of libraries: static archive libraries and dynamic shared +libraries. Searching for undefined symbols is performed differently for dynamic +shared libraries than it is for static archive libraries. The searching of +dynamic shared libraries is described later. +.PP +When a static archive library is specified as an argument to +.IR ld , +it is searched exactly once, at the +point it is encountered in the argument list. Only those members defining an unresolved external +reference, as defined by the static archive library's table of contents, +are loaded. To produce the table of contents, all static archive libraries must be processed by +.IR ranlib (1). +.PP +Generally, a static archive library does not have multiple members that define +the same symbol. For these types of libraries, the order of the members is not important, so +the table of contents can be sorted for faster link editing using the +.B \-s +option to +.IR ranlib (1). +The first member +of the static archive library is named +.SM "``\_\^\_.SYMDEF SORTED''," +which is understood to be a sorted table of contents. +.PP +If the static archive library does have multiple members that define +the same symbol, the table of contents that +.IR ranlib (1) +produces can't be sorted. Instead, it follows the order in which the members +appear in the static archive library. The link editor searches the table of +contents iteratively, loading members until no further references are +satisfied. In the unsorted case, the first member of the static archive +library is named +.SM "``\_\^\_.SYMDEF''," +which is understood to be a table of contents in +the order of the archive members. +.PP +Static archive library members can also be loaded in response to +the +.B \-ObjC +and +.B \-all_load +flags. See their descriptions below. + +.SH DYNAMIC SHARED LIBRARIES +.PP +When a dynamic shared library or an object file that was linked against a +dynamic shared library is specified as an argument to +.IR ld , +that library is placed in the dynamic shared library search list. The order of +the search list is always the same order the libraries were encountered on the +command line. When linking -flat_namespace, all dynamic libraries that the +dynamic libraries are dependent upon are added to the end of the search list. +.PP +Once the search list is constructed, the static link editor checks for undefined +symbols by simulating the way the dynamic linker will search for undefined +symbols at runtime. For each undefined symbol, the static link editor searches +each library in the search list until it finds a module that defines the symbol. +With each undefined symbol, the search starts with the first library in the +list. This is different than for static archive libraries, where each library +is searched exactly once for all undefined symbols. +.PP +The static link editor simulates dynamic linking as if all the undefined +symbols are to be bound at program launch time. The dynamic linker actually +binds undefined symbols as they are encountered during execution instead of at +program launch. However, the static link editor always produces the same linking +as the dynamic linker as long as none of the dynamic shared libraries define the +same symbol. Different linking can occur only when there is more than one +definition of a symbol and the library modules that contain the definitions for +that symbol do not define and reference exactly the same symbols. In this case, +even different executions of the same program can produce different linking +because the dynamic linker binds undefined functions as they are called, and +this affects the order in which undefined symbols are bound. Because it can +produce different dynamic linking, using dynamic shared libraries that define +the same symbols in the same program is strongly discouraged. +.PP +If a static archive library appears after a dynamic shared library on the +command line, the static library is placed in the dynamic library search list +and is searched as a dynamic library. In this way, when a dynamic library has +undefined symbols, it will cause the appropriate members of the static libraries +to be loaded into the output. Searching static libraries as dynamic libraries +can cause problems if the dynamic library later changes to reference symbols +from the static library that it did not previously reference. In this case when +the program runs, the dynamic linker will report these symbols as undefined +because the members for these symbols were not loaded into the output. + +.SH TWO-LEVEL AND FLAT NAMESPACES +.PP +Two-level and flat namespaces refer to how references to symbols in dynamic +libraries are resolved to a definition in specific dynamic library. For +two-level namespace that resolution is done at static link time when each +image (program, bundle and shared library) is built. When a program is using +images built with two-level namespace there may be different global symbols +with the same name being used by different images in the program (this is now +the default). When a program is using all flat namespace images then only one +global symbol for each global symbol name is used by all images of the program +(this was the default in MacOS X 10.0). +.PP +When creating an output file with the static link editor that links against +dynamic libraries, the references to symbols in those libraries can be recorded +at static link time to bind to a specific library definition (two-level +namespace) or left to be bound at execution time to the first library in the +search order of the program (flat namespace). A program, its dynamic libraries +and its bundles may each be either two-level or flat namespace images. The +dynamic linker will bind each image according to how it was built. +.PP +When creating an output file with the static link editor when +.B \-twolevel_namespace +is in effect (now the default) all undefined references must be satisfied at +static link time. The flags to allow undefined references, +.BI \-U symbol_name, +.BI \-undefined " warning" +and +.BI \-undefined " suppress" +can't be used. +When the environment variable +.B MACOSX_DEPLOYMENT_TARGET +is set to +.B 10.3 +or higher then +.BI \-undefined " dynamic_lookup" +can also be used. The specific library definition recorded +for each reference is the first library that has a definition as listed on the +link line. Listing an umbrella framework implies all of its sub-frameworks, +sub-umbrellas and sub-libraries. For any reference to a definition found in +an umbrella framework's sub-framework, sub-umbrella or sub-library will be +recorded as coming from the umbrella framework. Then at execution time the +dynamic linker will search that umbrella framework's sub-frameworks, +sub-umbrellas and sub-libraries for those references. +Also when two-level namespace is in effect only those frameworks listed on the +link line (and sub-frameworks, sub-umbrellas and sub-libraries of umbrella +frameworks) are searched. Other dependent libraries which are not +sub-frameworks, sub-umbrellas or sub-libraries of umbrella frameworks are not +searched. +.RS +.PP +When creating bundles (MH_BUNDLE outputs) with the static link editor when +two-level namespace is in effect (now the default) and the bundle has +references to symbols +expected to be defined in the program loading the bundle, then the +.BI \-bundle_loader " executable" +must be used. +.PP +When creating a output file with the static link editor when +.B \-flat_namespace +is in effect (the MacOS X 10.0 default) all undefined references must be +satisfied at static link time when +.BI \-undefined " error" +(the default) is used. The static +link editor checks the undefined references by searching all the libraries +listed on the link line then all dependent libraries. The undefined symbols +in the created output file are left to be resolved at execution time by the +dynamic link editor in the dynamic libraries in the search order of the program. + +.SH MULTIPLY DEFINED SYMBOLS +.PP +If there are multiply defined symbols in the object files being linked into +the output file being created this always results in a multiply defined +symbol error. +.PP +When the static link editor links symbols in from a dynamic library that result +in multiply defined symbols the handling depends on the type of name space of +output file being created and possibly the type of name space of the dynamic +library. +.PP +When the static link editor is creating a two-level namespace image and a +there is a multiply defined symbol from dynamic library then that generates a +multiply defined symbol warning (by default), where the treatment of this +warning can be changed with the +.B \-multiply_defined +flag. +.PP +When the static link editor is creating a flat namespace image and a there is +a multiply defined symbol from dynamic library, if the library is a flat +namespace image then that generates a multiply defined symbol error. If the +library is a two-level namespace image then that generates a multiply defined +symbol warning (by default), where the treatment of this warning can be changed +with the +.B \-multiply_defined +flag. + +.SH "USING THE DYNAMIC LINK EDITOR AND DYNAMIC SHARED LIBRARIES" +.PP +The option +.B \-dynamic +must be specified in order to use dynamic shared libraries (and any of the features used to implement them) and/or the dynamic link editor. +To make sure that the output is not using any features that would +require the dynamic link editor, the flag +.B \-static +can be specified. +Only one of these flags can be specified. + +.SH "LINK EDITOR DEFINED SYMBOLS" +.PP +There is a group of link editor defined symbols for the +.SM MH_EXECUTE, +.SM MH_DYLIB +and +.SM MH_PRELOAD +file types (see the header file ). Link editor symbols are +reserved; it is an error if an input object file defines such a symbol. +Only those link editor symbols that are referenced by the object file +appear in the output file's symbol table. +.PP +The link editor defined symbol `\_\^\_mh_execute_header' +(`\_mh_execute_header' in C) is reserved when the output file format is +.SM MH_EXECUTE. +This symbol is the address of the Mach header in a Mach-O executable (a +file of type +.SM MH_EXECUTE). +It does not appear in +any other Mach-O file type. It can be used to get to the addresses and +sizes of all the segments and sections in the executable. This can be done by parsing the headers +and load commands (see +.IR Mach-O (5)). +.PP +The link editor defined symbol `\_\^\_mh_dylib_header' +(`\_mh_dylib_header' in C) is reserved when the output file format is +.SM MH_DYLIB. +This symbol is the address of the Mach header in a Mach-O dynamic shared library +(a file of type +.SM MH_DYLIB) +and is a private external symbol. +It does not appear in +any other Mach-O file type. It can be used to get to the addresses and +sizes of all the segments and sections in a dynamic shared library. The +addresses, however, must have the value +.IR _dyld_get_image_vmaddr_slide (3) +added to them. +.PP +The +.SM MH_PRELOAD +file type has link editor defined symbols for the +beginning and ending of each segment, and for the +beginning and ending of each section within a segment. +These names are provided for use in a Mach-O preloaded file, +since it does not have its headers loaded as part of the first segment. +The names of the symbols for a segment's beginning and end +have the form: \_\^\_SEGNAME\_\^\_begin and \_\^\_SEGNAME\_\^\_end, +where \_\^\_SEGNAME is the name of the segment. Similarly, the symbols for +a section have the form: +\_\^\_SEGNAME\_\^\_sectname\_\^\_begin and \_\^\_SEGNAME\_\^\_sectname\_\^\_end, +where \_\^\_sectname is the name of the section in the segment \_\^\_SEGNAME. +These symbols' types are those of the section that the names refer to. +(A symbol that refers to the end of a section actually has, as its value, the beginning address of the next section, but the symbol's type is still that of the section mentioned in the symbol's name.) +.SH OPTIONS +.PP +.I Ld +understands several options. Filenames and +options that refer to libraries (such as +.B \-l +and +.BR \-framework ), +as well as options that create symbols (such as +.B \-u +and +.BR \-i ), +are position-dependent: They define the load order and affect what gets +loaded from libraries. +Some +.I ld +options overlap with compiler options. If the compiler driver +.IR cc (1) +is used to invoke +.I ld , +it maybe necessary to pass the +.IR ld (1) +options to +.IR cc (1) +using +.BR \-Wl,\-option,argument1,argument2 . + +In this release of the static link editor, 64-bit code (-arch ppc64) are processed by a separate +tool /usr/bin/ld64. Not all of the ld command line options are recognized by this tool. +The options not currently support for building 64-bit binaries are flagged +.BR "(32-bit only)" . + +The most common option is: +.TP +.BI \-o " name" +The output file is named +.IR name , +instead of +.BR a.out . + +.PP +The following flags are related to architectures: +.TP +.BI \-arch " arch_type" +Specifies the architecture, +.I arch_type, +for the output file. ``Universal'' input files that do not contain this +specified architecture are ignored. Only one +.BI \-arch " arch_type" +can be specified. See +.IR arch (3) +for the currently known +.IR arch_type s. +If +.I " arch_type" +specifies a certain implementation of an architecture (such as +.BI \-arch " m68040" +or +.BI \-arch " i486" +), the resulting object file has that specific CPU subtype, and it is an +error if any input file has a CPU subtype that will not combine to the CPU subtype +for +.IR " arch_type" . +.IP +The default output file architecture is determined by the first object file to +be linked. If it is a ``thin'' (standard Mach-O) file, or a ``universal'' file +that contains only one architecture, the output file will have the same +architecture. Otherwise, if it is a ``universal'' file +containing an architecture that would execute on the host, then the ``best'' +architecture is used, as defined by what the kernel exec(2) would select. +Otherwise, it is an error, and a +.BI \-arch " arch_type" +must be specified. +.TP +.B \-arch_multiple +This flag is used by the +.IR cc (1) +driver program when it is run with multiple +.BI \-arch " arch_type" +flags. It instructs programs like +.IR ld (1) +to precede any displayed message with a line stating +the program name, in this case +.IR ld , +and the architecture (from the +.BI \-arch " arch_type" +flag). This helps distinguish which architecture the error messages refer to. +.TP +.B \-force_cpusubtype_ALL +The +.B \-force_cpusubtype_ALL +flag causes the CPU subtype to remain the +.SM ALL +CPU subtype and not to be combined or +changed. This flag has precedence over any +.BI \-arch " arch_type" +flag for a specific implementation. +This is the default for all x86 architectures. +.PP +The following flags are related to using the dynamic link editor and/or +dynamic shared libraries (and any of the features used to implement them): +.TP +.B \-dynamic +Allows use of the features associated with dynamic link editor. The default is +.B \-dynamic. +.TP +.B \-static +Causes those features associated with dynamic link editor to be treated as +an error. (The description for the options that will cause an error if you use them in conjunction with +.B \-static +are marked with the statement "when +.B \-dynamic + is used"). +.TP +.BI \-read_only_relocs " treatment" +Specifies how relocation entries in read-only sections are to be treated when +.B \-dynamic +is used. +To get the best possible sharing, the read-only sections should not have any +relocation entries. +If they do, the dynamic linker will write on the section. +Having relocation entries appear in read-only sections is normally avoided by compiling with the option +.B \-dynamic. +But in such cases non-converted assembly code +or objects not compiled with +.B \-dynamic +relocation entries will appear in read-only sections. +The +.I treatment +can be: +.I error, +.I warning, +or +.I suppress. +Which cause the treatment of relocation entries in read-only sections as either, +errors, warnings, or suppressed messages. +The default is to treat these as errors. +.TP +.BI \-sect_diff_relocs " treatment" +Specifies how section difference relocation enries are to be treated when +.B \-dynamic +and +.B \-execute +are used. +To get the best possible code generation the compiler should not generate code +for executables (MH_EXECUTE format outputs) that have any section difference +relocation entries. The +.IR gcc (1) +compiler has the +.B \-mdynamic-no-pic +flag for generating code for executables. The default treatment is +.I suppress, +where no message is printed. The other treatments are +.I error +or +.I warning. +This option can also be specified by setting the environment variable +.SM LD_SECT_DIFF_RELOCS +to the treatment values. +.TP +.BI \-weak_reference_mismatches " treatment" +Specifies how to treat mismatches of symbol references in the the object files +being linked. Normally the all the undefined symbol references of the object +files being linked should be consistent for each undefined symbol. That is all +undefined symbols should either be weak or non-weak references. The default +treatment is +.I error, +where the link fails with an error message. The other treatments are +.I weak +or +.I non-weak, +which makes mismatched undefined symbol references either weak or non-weak +in the output, respectively. Care must be taken when using the treatment +.I weak +as the use of the non-weak symbol references in an object file may cause the +program to crash when the symbol is not present at execution time. +.TP +.B \-prebind (32-bit only) +Have the static linker, +.IR ld (1), +prebind an executable's or dynamic shared library's undefined symbols to the +addresses of the dynamic libraries it is being linked with. +This optimization can only be done if the libraries don't overlap and +no symbols are overridden. +When the resulting program is run and the same libraries are used to run the +program as when the program was linked, the dynamic linker can use the prebound +addresses. +If not, the dynamic linker undoes the prebinding and binds normally. +This option can also be specified by setting the environment variable +.SM LD_PREBIND. +If the environment variable +.SM LD_FORCE_NO_PREBIND +is set both the option +.B \-prebind +.SM LD_PREBIND +environment variable are ignore and the output is not prebound. +Or if the environment variable +.B MACOSX_DEPLOYMENT_TARGET +is set to 10.4 or greater and the output is not a split a dynamic library the +output is not prebound. +.TP +.B \-noprebind (32-bit only) +Do not have the static linker, +.IR ld (1), +prebind the output. If this is specified the environment variable +.SM LD_PREBIND +is ignored. +.TP +.B \-prebind_allow_overlap (32-bit only) +Have the static linker, +.IR ld (1), +prebind the output even if the addresses of the dynamic libraries it uses +overlap. The resulting output can then have +.IR redo_prebinding (1) +run on it to fix up the prebinding after the overlapping dynamic libraries +have been rebuilt. This option can also be specified by setting the +environment variable +.SM LD_PREBIND_ALLOW_OVERLAP. +.TP +.B \-prebind_all_twolevel_modules (32-bit only) +Have the static linker, +.IR ld (1), +mark all modules from prebound two-level namespace dynamic libraries as used +by the program even if they are not statically referenced. This can provide +improved launch time for programs like Objective-C programs that use symbols +indirectly through NIB files. This option can also be specified by setting the +environment variable +.SM LD_PREBIND_ALL_TWOLEVEL_MODULES. +.TP +.B \-noprebind_all_twolevel_modules (32-bit only) +Don't have the static linker, +.IR ld (1), +mark all modules from prebound two-level namespace dynamic libraries as used +by the program. This flag overrides the setting of the +environment variable +.SM LD_PREBIND_ALL_TWOLEVEL_MODULES. +.TP +.B \-nofixprebinding (32-bit only) +Have the static linker, +.IR ld (1), +mark the executable so that the dynamic linker will never notify the prebinding +agent if this launched and its prebinding is out of date. This is used when +building the prebinding agent itself. +.PP +The following flags are related to libraries: +.TP +.BI \-l x +This +option is an abbreviation for the library name +.RI `lib x .a', +where +.I x +is a string. +If +.B \-dynamic +is specified the abbreviation for the library name is first search as +.RI `lib x .dylib' +and then +.RI `lib x .a' +is searched for. +.I ld +searches for libraries first in any directories +specified with +.B \-L +options, then in any directories specified in the colon separated set of paths +in the environment variable LD_LIBRARY_PATH, then the standard directories +.BR /lib , +.BR /usr/lib , +and +.BR "/usr/local/lib" . +A library is searched when its name is encountered, +so the placement of the +.B \-l +flag is significant. If string +.I x +is of the form +.IR x .o, +then that file is searched for in the same places, but without prepending +`lib' or appending `.a' or `.dylib' to the filename. +.TP +.BI \-weak-l x +This is the same as the +.BI \-l x +but forces the library and all references to it to be marked as weak imports. +Care must be taken when using this as the use of the non-weak symbol references +in an object file may cause the program to crash when the symbol or library is +not present at execution time. +.TP +.BI \-weak_library " file_name_path_to_library" +This is the same as listing a file name path to a library on the link line +except that it forces the library and all references to it to be marked as +weak imports. +Care must be taken when using this as the use of the non-weak symbol references +in an object file may cause the program to crash when the symbol or library is +not present at execution time. +.TP +.BI \-L dir +Add +.I dir +to the list of directories in which to search for libraries. +Directories specified with +.B \-L +are searched before the standard directories. +.TP +.B \-Z +Do not search the standard directories when searching for libraries. +.TP +.BI "\-syslibroot " rootdir " (32-bit only)" +Prepend +.I rootdir +to the standard directories when searching for libraries or frameworks. +.TP +.B \-search_paths_first +By default when the +.B \-dynamic +flag is in effect, the +.BI \-l x +and +.BI \-weak-l x +options first search for a file of the form +.RI `lib x .dylib' +in each directory in the library search path, then a file of the form +.RI `lib x .a' +is searched for in the library search paths. +This option changes it so that in each path +.RI `lib x .dylib' +is searched for then +.RI `lib x .a' +before the next path in the library search path is searched. +.TP +.BI "\-framework " name[,suffix] +Specifies a framework to link against. Frameworks are dynamic shared libraries, +but they are stored in different locations, and therefore must be searched for +differently. When this option is specified, +.I ld +searches for framework `\fIname\fR.framework/\fIname\fR' +first in any directories +specified with the +.B \-F +option, then in the standard framework directories +.BR /Library/Frameworks , +.BR /Network/Library/Frameworks , +and +.BR "/System/Library/Frameworks" . +The placement of the +.B \-framework +option is significant, as it determines when and how the framework is searched. +If the optional suffix is specified the framework is first searched for the +name with the suffix and then without. +.TP +.BI "\-weak_framework " name[,suffix] +This is the same as the +.BI "\-framework " name[,suffix] +but forces the framework and all references to it to be marked as weak imports. +Care must be taken when using this as the use of the non-weak symbol references +in an object file may cause the program to crash when the symbol or framework is +not present at execution time. +.TP +.BI \-F dir +Add +.I dir +to the list of directories in which to search for frameworks. +Directories specified with +.B \-F +are searched before the standard framework directories. +.TP +.B \-ObjC +Loads all members of static archive libraries that define an Objective C class or a category. This option does not apply to dynamic shared libraries. +.TP +.B \-all_load +Loads all members of static archive libraries. +This option does not apply to dynamic shared +libraries. +.TP +.BI \-dylib_file " install_name:file_name" (32-bit only) +Specifies that a dynamic shared library is in a different location than its standard location. Use this option when you link with a library that is dependent on a dynamic library, and the dynamic library is in a location other than its default location. +.I install_name +specifies the path where the library normally resides. +.I file_name +specifies the path of the library you want to use instead. +For example, if you link to a library that depends upon the dynamic library libsys and you have libsys installed in a nondefault location, you would use this option: +\fB\-dylib_file /lib/libsys_s.A.dylib:/me/lib/libsys_s.A.dylib\fR. +.TP +.BI \-executable_path " path_name" (32-bit only) +Specifies that +.I path_name +is used to replace +.I @executable_path +for dependent libraries. + +.PP +The following options specify the output file format (the file type): +.TP +.B "\-execute" +Produce a Mach-O demand-paged executable format file. The headers are placed +in the first segment, and all segments are padded to the segment alignment. +This has a file type of +.SM MH_EXECUTE. +This is the default. If no segment address is specified at address zero, a +segment with no protection (no read, write, or execute permission) is created +at address zero. +This segment, whose size is that of the segment +alignment, is named +.SM ``_\|_PAGEZERO''. +This option was previously named +.BR "\-Mach" , +which will continue to be recognized. +.TP +.B \-object (32-bit only) +Produce a Mach-O file in the relocatable object file format that is +intended for execution. This differs from using the +.B \-r +option in that it defines common symbols, does not allow undefined symbols and +does not preserve relocation entries. This has a file type of +.SM MH_OBJECT. +In this format all sections are placed in one unnamed segment with all +protections (read, write, execute) allowed on that segment. This is intended +for extremely small programs that would otherwise be large due to segment +padding. In this format, and all +.SM non-MH_EXECUTE +formats, the link editor +defined symbol ``\_\^\_mh_execute_header'' is not defined since the headers are +not part of the segment. This format file can't be used with the dynamic +linker. +.TP +.B \-preload (32-bit only) +Produce a Mach-O preloaded executable format file. The headers are not placed +in any segment. All sections are placed in their proper segments and they are +padded to the segment alignment. This has a file type of +.SM MH_PRELOAD. +This option was previously +.BR "\-p" , +which will continue to be recognized. +.TP +.B "\-dylib" +Produce a Mach-O dynamically linked shared library format file. The headers are +placed in the first segment. All sections are placed in their proper segments +and they are padded to the segment alignment. This has a file type of +.SM MH_DYLIB. +This option is used by +.IR libtool (1) +when its +.B \-dynamic +option is specified. +.TP +.B "\-bundle" +Produce a Mach-O bundle format file. The headers are placed in the first +segment. All sections are placed in their proper segments +and they are padded to the segment alignment. This has a file type of +.SM MH_BUNDLE. +.TP +.B "\-dylinker" +Produces a Mach-O dynamic link editor format file. The headers are placed in the +first segment. All sections are placed in their proper segments, and they are +padded to the segment alignment. This has a file type of +.SM MH_DYLINKER. +.TP +.B \-fvmlib (32-bit only) +Produce a Mach-O fixed VM shared library format file. The headers are placed +in the first segment but the first section in that segment will be placed on +the next segment alignment boundary in that segment. All sections are placed +in their proper segments and they are padded to the segment alignment. +This has a file type of +.SM MH_FVMLIB. + +.PP +The following flags affect the contents of the output file: +.TP +.B \-r +Save the relocation information in the output file +so that it can be the subject of another +.I ld +run. The resulting file type is a Mach-O relocatable file +.SM (MH_OBJECT) +if not otherwise specified. +This flag also prevents final definitions from being +given to common symbols, +and suppresses the `undefined symbol' diagnostics. +.TP +.B \-d (32-bit only) +Force definition of common storage even if the +.B \-r +option is present. This option also forces link editor defined symbols to be defined. +This option is assumed when there is a dynamic link editor load command in the input +and +.B \-r +is not specified. + +.PP +The following flags support segment specifications: +.TP +.BI "\-segalign" " value" " (32-bit only)" +Specifies the segment alignment. +.I value +is a hexadecimal number that must be an integral power of 2. +The default is the target pagesize (currently 1000 hex for the PowerPC and +i386). +.TP +.BI "\-seg1addr" " addr" +Specifies the starting address of the first segment in the output file. +.I addr +is a hexadecimal number and must be a multiple of the segment alignment. +This option can also be specified as +.B "\-image_base." +.TP +.BI "\-segaddr" " name addr" " (32-bit only)" +Specifies the starting address of the segment named +.I name +to be +.I addr. +The address must be a hexadecimal number that is a multiple of the segment alignment. +.TP +.BI "\-segs_read_only_addr" " addr" " (32-bit only)" +Specifies the starting address of the read-only segments in a dynamic shared +library. When this option is used the dynamic shared library is built such +that the read-only and read-write segments are split into separate address +ranges. By default the read-write segments are 256meg (0x10000000) after +the read-only segments. +.I addr +is a hexadecimal number and must be a multiple of the segment alignment. +.TP +.BI "\-segs_read_write_addr" " addr" " (32-bit only)" +Specifies the starting address of the read-write segments in a dynamic shared +library. When this option is used the +.B \-segs_read_only_addr +must also be used (see above). +.I addr +is a hexadecimal number and must be a multiple of the segment alignment. +.TP +.BI "\-seg_addr_table" " filename" " (32-bit only)" +For dynamic shared libraries the +.B "\-seg1addr" +or the pair of +.B "\-segs_read_only_addr" +and +.B "\-segs_read_write_addr" +are specified by an entry in the segment address table in +.I filename +that matches the install name of the library. +The entries in the table are lines containing either a single hex address and an +install name or two hex addresses and an install name. In the first form the +single hex address is used as the +.B "\-seg1addr". +In the second form the first address is used as the +.B "\-segs_read_only_addr" +address and the second address is used as the +.B "\-segs_read_write_addr" +address. +This option can also be specified by setting the environment variable +.SM LD_SEG_ADDR_TABLE. +If the environment variable is set then any +.BR "\-seg1addr" , +.BR "\-segs_read_only_addr" , +.B "\-segs_read_write_addr" +and +.B "\-seg_addr_table" +options are ignored and a warning is printed. +.TP +.BI "\-seg_addr_table_filename" " pathname" " (32-bit only)" +Use +.B pathname +instead of the install name of the library for matching an entry in the segment +address table. +.TP +.BI "\-segprot" " name max init" " (32-bit only)" +Specifies the maximum and initial virtual memory protection of the named +segment, +.I name, +to be +.I max +and +.I init +,respectively. The values for +.I max +and +.I init +are any combination of the characters `r' (for read), `w' (for write), +`x' (for execute) and '\-' (no access). The default is `rwx' for the maximum +protection for all segments for PowerPC architecures and `rw` for the all Intel +architecures. +The default for the initial protection for all segments is `rw' unless the +segment contains a section which contains some machine instructions, in which +case the default for the initial protection is `rwx' (and for Intel +architecures it also sets the maximum protection to `rwx' in this case). +The default for the initial protection for the +.SM "``_\|_TEXT''" +segment is `rx' (not writable). +.TP +.B \-seglinkedit (32-bit only) +Create the link edit segment, named +.SM "``_\|_LINKEDIT''" +(this is the default). +This segment contains all the link edit information (relocation information, +symbol table, string table, etc.) in the object file. If the segment protection +for this segment is not specified, the initial protection is not writable. +This can only be specified when the output file type is not +.SM MH_OBJECT +and +.SM MH_PRELOAD +output file types. To get at the contents of this section, the Mach header +and load commands must be parsed from the link editor defined symbols like +`\_\^\_mh_execute_header' (see +.IR Mach-O (5)). +.TP +.B \-noseglinkedit (32-bit only) +Do not create the link edit segment (see +.B \-seglinkedit +above). +.TP +.BI "\-pagezero_size" " value" +Specifies the segment size of _\|_PAGEZERO to be of size +.IR value , +where +.I value +is a hexadecimal number rounded to the segment alignment. +The default is the target pagesize (currently, 1000 hexadecimal for the PowerPC +and for i386). +.TP +.BI "\-stack_addr" " value" +Specifies the initial address of the stack pointer +.IR value , +where +.I value +is a hexadecimal number rounded to the segment alignment. +The default segment alignment is the target pagesize (currently, 1000 +hexadecimal for the PowerPC and for i386). +If +.B \-stack_size +is specified and +.B \-stack_addr +is not, a default stack address specific for the architecture being linked will +be used and its value printed as a warning message. +This creates a segment named _\|_UNIXSTACK. Note that the initial stack address +will be either at the high address of the segment or the low address of the +segment depending on which direction the stack grows for the architecture being +linked. +.TP +.BI "\-stack_size" " value" +Specifies the size of the stack segment +.IR value , +where +.I value +is a hexadecimal number rounded to the segment alignment. +The default segment alignment is the target pagesize (currently, 1000 +hexadecimal for the PowerPC and for i386). +If +.B \-stack_addr +is specified and +.B \-stack_size +is not, a default stack size specific for the architecture being linked will be +used and its value printed as a warning message. +This creates a segment named _\|_UNIXSTACK . +.TP +.B \-allow_stack_execute +Marks executable so that all stacks in the task will be given stack execution +privilege. This includes pthread stacks. + +.PP +The following flags support section specifications: +.TP +.BI "\-sectcreate" " segname sectname file" +The section +.I sectname +in the segment +.I segname +is created from the contents of +.I file. +The combination of +.I segname +and +.I sectname +must be unique; there cannot already be a section +.I (segname,sectname) +in any input object file. +This option was previously called +.BR "\-segcreate" , +which will continue to be recognized. +.TP +.BI "\-sectalign" " segname sectname value" +The section named +.I sectname +in the segment +.I segname +will have its alignment set to +.IR value , +where +.I value +is a hexadecimal number that must be an integral power of 2. +This can be used to set the alignment of a section created from a file, or to +increase the alignment of a section from an object file, or to set the maximum +alignment of the +.SM (_\|_DATA,_\|_common) +section, where common symbols are defined +by the link editor. Setting the alignment of a literal section causes the +individual literals to be aligned on that boundary. If the section +alignment is not specified by a section header in an object file or on the +command line, it defaults to 10 (hex), indicating 16-byte alignment. +.TP +.BI "\-sectorder" " segname sectname orderfile" (32-bit only) +The section +.I sectname +in the segment +.I segname +of the input files will be broken up into blocks associated with +symbols in the section. The output section will be created by ordering +the blocks as specified by the lines in the +.I orderfile. +These blocks are aligned to the output file's section alignment for this +section. Any section can be ordered in the output file except symbol pointer and symbol stub sections. +.IP +For non-literal sections, each line of the +.I orderfile +contains an object name and a symbol name, separated by a single colon (':'). +Lines that start with # are ignored and treated as comments. +If the object file is +in an archive, the archive name, followed by a single colon, must precede the +object file name. The object file names and archive names should be exactly the +names as seen by the link editor, but if not, the link editor attempts to match +up the names the best it can. +For non-literal sections, the easiest way to generate an order file is +with the ``\f3\-jonls +\f2segname sectname\f1'' options to +.IR nm (1). +.IP +The format of the +.I orderfile +for literal sections is specific to each type of literal section. For C +string literal sections, each line of the order file contains one literal C +string, which may include ANSI C escape sequences. For four-byte literal +sections, the order file format is one 32-bit hex number with a leading 0x +per +line, with the rest of the line treated as a comment. For eight-byte literal +sections, the order file has two 32-bit hex numbers per line; each number +has a leading 0x, the two numbers are separated by white +space, and the rest of the line is treated as a comment. +For literal pointer sections, the lines in the order file represent +pointers, one per line. A literal pointer is represented by the name of +the segment that contains the literal being pointed to, followed by the +section name, followed by the literal. These three strings are separated +by colons with no extra white space. +For all the literal sections, each line in the the order file is simply entered +into the literal section and will appear in the output file in the same order +as in the +order file. There is no check to see whether the literal is present +in the loaded objects. +For literal sections, the easiest way to generate an order file is with +the ``\f3\-X \-v \-s \f2segname sectname\f1'' options to +.IR otool (1). +.TP +.B \-sectorder_detail (32-bit only) +When using the +.B \-sectorder +option, any pairs of object file names and symbol names that are found in +the loaded objects, but not specified in the +.IR orderfile , +are placed last in the output file's section. These pairs are ordered by +object file (as the filenames appear +on the command line), with the different symbols from a given object +file being ordered by +increasing symbol address (that is, the order +in which the symbols occurred in the object file, +not their order in the symbol table). By default, the link editor displays a summary +that simply shows the number +of symbol names found in the loaded objects but not in the +.IR orderfile , +as well as the number of symbol names listed in the +.I orderfile +but not found in the loaded objects. (The summary is omitted if both values +are zero.) To instead produce a detailed list of these symbols, use the +.B \-sectorder_detail +flag. If an object file-symbol name pair is listed multiple times, a +warning is generated, and the first occurrence is used. +.TP +.BI "\-sectobjectsymbols" " segname sectname" " (32-bit only)" +This causes the link editor to generate local symbols in the section +.I sectname +in the segment +.IR segname . +Each object file that has one of these sections will have a local +symbol created +whose name is that of the object file, or of the member of the archive. +The symbol's value will be the first address where that object file's section +was loaded. The symbol has the type N_SECT and its section number is the +the same as that of the section +.I (segname,sectname) +in the output file. +This symbol will be placed in the symbol table just before all other local +symbols +for the object file. This feature is typically used where the section is +.SM (\_\^\_TEXT,\_\^\_text), +in order to help the debugger debug object files produced by old versions of +the compiler or by non-Apple compilers. + +.PP +The following flags are related to name spaces: +.TP +.B \-twolevel_namespace +Specifies the output to be built as a two-level namespace image. +This option can also be specified by setting the environment variable +.SM LD_TWOLEVEL_NAMESPACE. +This is the default. +.TP +.B \-flat_namespace +Specifies the output to be built as a flat namespace image. +This is not the default (but was the default in MacOS X 10.0). +.TP +.B \-force_flat_namespace +Specifies the executable output to be built and executed treating all its +dynamic libraries as flat namespace images. This marks the executable so that +the dynamic link editor treats all dynamic libraries as flat namespace +images when the program is executed. +.TP +.BI \-bundle_loader " executable" " (32-bit only)" +This specifies the +.I executable +that will be loading the bundle output file being linked. Undefined symbols +from the bundle are checked against the specified executable like it was one of +the dynamic libraries the bundle was linked with. If the bundle being created +with +.B \-twolevel_namespace +in effect then the searching of the executable for +symbols is based on the placement of the +.B \-bundle_loader +flag relative to the dynamic libraries. If the the bundle being created with +.B \-flat_namespace +then the searching of the executable is done before all dynamic libraries. +.TP +.B \-private_bundle (32-bit only) +This allows symbols defined in the output to also be defined in executable in +the +.B \-bundle_loader +argument +when +.B \-flat_namespace +is in effect. +This implies that the bundle output file being created is going to be loaded by +the executable with the +.B NSLINKMODULE_OPTION_PRIVATE +option to +.IR NSLinkModule (3). +.TP +.B \-twolevel_namespace_hints (32-bit only) +Specifies to create the output with the two-level namespace hints table to be +used by the dynamic linker. This is the default except when the +.B \-bundle +flag is specified. If this is used when the +.B \-bundle +flag is specified the bundle will fail to load on a MacOS X 10.0 system with a +malformed object error. +.TP +.BI \-multiply_defined " treatment" " (32-bit only)" +Specifies how multiply defined symbols in dynamic libraries when +.B \-twolevel_namespace +is in effect are to be treated. +.I treatment +can be: +.I error, +.I warning, +or +.I suppress. +Which cause the treatment of multiply defined symbols in dynamic libraries +as either, errors, warnings, or suppresses the checking of multiply symbols +from dynamic libraries when +.B \-twolevel_namespace +is in effect. +The default is to treat multiply defined symbols in dynamic libraries as +warnings when +.B \-twolevel_namespace +is in effect. +.TP +.BI \-multiply_defined_unused " treatment" " (32-bit only)" +Specifies how unused multiply defined symbols in dynamic libraries when +.B \-twolevel_namespace +is in effect are to be treated. +An unused multiply defined symbol is one in which there is a symbol defined in +the output that is also defined in the dynamic libraries the output is linked +with but the symbol in the dynamic library is not used by any reference in the +output. +.I treatment +can be: +.I error, +.I warning, +or +.I suppress. +The default for unused multiply defined symbols is to suppress these messages. +.TP +.B -nomultidefs (32-bit only) +specifying this flag marks the umbrella being created such that the dynamic +linker is guaranteed that no multiple definitions of symbols in the umbrella's +sub-images will ever exist. This allows the dynamic linker to always use the +two-level namespace lookup hints even if the timestamps of the sub-images +do not match. This flag implies +.BI \-multiply_defined " error". + +.PP +The following flags are related to symbols. These flags' arguments +are external symbols whose names have `_' prepended to the C, +.SM FORTRAN, +or Pascal variable name. +.TP +.BI \-y sym " (32-bit only)" +Display each file in which +.I sym +appears, its type, and whether the file defines or references it. Any +multiply defined symbols are automatically +traced. Like most of the other symbol-related flags, +.B \-y +takes only one argument; the flag may be specified more than once in the +command line to trace more than one symbol. +.TP +.BI \-Y " number" " (32-bit only)" +For the first +.I number +undefined symbols, displays each file in which the symbol appears, its type and whether the file defines or references it (that is, the same style of output produced by the +.B \-y +option). To keep the output manageable, this option displays at most +.I number +references. +.TP +.B \-keep_private_externs +Don't turn private external symbols into static symbols, but rather leave them +as private external in the resulting output file. +.TP +.B \-m (32-bit only) +Don't treat multiply defined symbols from the linked objects as a hard error; +instead, simply print a warning. The first linked object defining such a symbol +is used for linking; its value is used for the symbol in the symbol table. The +code and data for all such symbols are copied into the output. The duplicate +symbols other than the first symbol may still end up being used in the resulting +output file through local references. This can still produce a resulting output +file that is in error. This flag's use is strongly discouraged! +.TP +.B \-whyload (32-bit only) +Indicate why each member of a library is loaded. In other words, indicate +which currently undefined symbol is being resolved, causing that +member to be loaded. This in combination with the above +.BI \-y sym +flag can help determine exactly why a link edit is failing due to multiply +defined symbols. +.B +.TP +.BI \-u " sym" +Enter the argument +.I sym +into the symbol table as an undefined symbol. This is useful +for loading wholly from a library, since initially the symbol +table is empty and an unresolved reference is needed +to force the loading of the first object file. +.TP +.BI \-e " sym" +The argument +.I sym +is taken to be the symbol name of the entry point of +the resulting file. By default, the entry point is the address of the +first section in the first segment. +.TP +.BI \-i definition:indirect " (32-bit only)" +Create an indirect symbol for the symbol name +.I definition +which is defined to be the same as the symbol name +.I indirect +(which is taken to be undefined). When a definition of the symbol named +.I indirect +is linked, both symbols will take on the defined type and value. +.IP +This option overlaps with a compiler option. +If you use the compiler driver +.IR cc (1) +to invoke \fIld\fR, +invoke this option in this way: +.BI \-Wl,\-i definition:indirect. + +.TP +.BI \-undefined " treatment" +Specifies how undefined symbols are to be treated. +.I treatment +can be: +.I error, +.I warning, +or +.I suppress. +Which cause the treatment of undefined symbols as either, errors, warnings, or +suppresses the checking of undefined symbols. +The default is to treat undefined symbols as errors. +When the environment variable +.B MACOSX_DEPLOYMENT_TARGET +is set to +.B 10.3 +or higher then +.BI \-undefined " dynamic_lookup" +can also be used to allow any undefined symbols to be looked up dynamically at +runtime. Use of a binary built with this flag requires a system with a dynamic +linker from Mac OS X 10.3 or later. +The flag +.BI \-undefined " define_a_way" +can also be used to cause the static linker to create a private definition for +all undefined symbols. This flag should only be used if it is known that the +undefined symbols are not referenced as any use of them may cause a crash. +.TP +.BI \-U " sym" +Allow the symbol +.I sym +to be undefined, even if the +.B \-r +flag is not given. Produce an executable file if the only undefined +symbols are those specified with +.BR \-U. +.IP +This option overlaps with a compiler option. +If you use the compiler driver +.IR cc (1) +to invoke \fIld\fR, +invoke this option in this way: +.BI \-Wl,\-U, sym. +.TP +.B \-bind_at_load +Causes the output file to be marked such that the dynamic linker will bind all +undefined references when the file is loaded or launched. +.TP +.BI \-commons " treatment" " (64-bit only)" +Specifies how common symbols (tentative defintions) from object files interact with dynamic libraries. +.I treatment +can be: +.I ignore_dylibs, +.I use_dylibs, +or +.I error. +The default is ignore_dylibs +which means the static linker will use a common defintion from an object file even if a true definition +exisits in a dynamic library. If you want your code to use a dynamic library definition, then add +the extern keyword to your tentative definition (e.g. change +.I int foo; +to +.I extern int foo; +). The treatment use_dylibs means a definition form a dynamic library should override a common symbol +in an object file. Note, the 32-bit linker always uses this treatment. +The treatment error means the linker should abort whenever if finds a common symbol in +an object file and an external definition with the same name in a dynamic library. +.TP +.B \-warn_commons (64-bit only) +Causes the static linker to write a diagnostic line about how common symbols were processed. This is +useful for debugging problems with common symbols. + +.PP +The following flags are related to stripping link edit information. +This information can also be removed by +.IR strip (1), +which uses the same options. (The +exception is the +.B \-s +flag below, but this is the same as +.IR strip (1) +with no arguments.) +The following flags are listed in decreasing level of stripping. +.TP +.B \-s (32-bit only) +Completely strip the output; that is, remove the symbol table +and relocation information. +.TP +.B \-x (32-bit only) +Strips the non-global symbols; only saves external symbols. +.IP +This option overlaps with a compiler option. +If you use the compiler driver +.IR cc (1) +to invoke \fIld\fR, +invoke this option in this way: +.B \-Wl,\-x. +.TP +.B \-S (32-bit only) +Strip debugging symbols; only save local and global symbols. +.TP +.B \-X (32-bit only) +Strip local symbols whose names begin with `L'; save all other symbols. +(The compiler and assembler currently strip these internally-generated +labels by default, so they generally do not appear in object files +seen by the link editor.) +.TP +.B \-Sp +Strip, edit and add debugging symbols so the debugger can used most of the +debugging symbols from the object files. +.TP +.B \-Si (32-bit only) +Strip duplicate debugging symbols from include files. This is +the default. +.TP +.B \-b (32-bit only) +Strip the base file's symbols from the output file. (The base file +is given as the argument to the +.B \-A +option.) +.IP +This option overlaps with a compiler option. +If you use the compiler driver +.IR cc (1) +to invoke \fIld\fR, +invoke this option in this way: +.B \-Wl,\-b. +.TP +.B \-Sn (32-bit only) +Don't strip any symbols. +.TP +.BI \-exported_symbols_list " filename" +The specified +.I filename +contains lists of global symbol names that will remain as global symbols in the +output file. All other global symbols will be treated as if they were marked as +.I __private_extern__ +and will not be global in the output file. The symbol names listed in +.I filename +must be one per line. Leading and trailing white space are not part of the +symbol name. Lines starting with # are ignored, as are lines with only white +space. +.TP +.BI \-unexported_symbols_list " filename" +The specified +.I filename +contains lists of global symbol names that will not remain as global symbols in +the output file. The symbols will be treated as if they were marked as +.I __private_extern__ +and will not be global in the output file. The symbol names listed in +.I filename +must be one per line. Leading and trailing white space are not part of the +symbol name. Lines starting with # are ignored, as are lines with only white +space. +.TP +.BI \-no_uuid +Do not emit an LC_UUID load command in the linked output file. + +.TP +.B -dead_strip (32-bit only) +Remove blocks of code and data that are unreachable by the entry point or +exported symbols. +.TP +.B -no_dead_strip_inits_and_terms (32-bit only) +When specified along with +.B -dead_strip +cause all constructors and destructors to never be dead stripped. + +.PP +The remaining options are infrequently used: +.TP +.B \-v +Print the version of the linker. +.TP +.B \-w (32-bit only) +Suppresses all warning messages. +.TP +.B \-no_arch_warnings +Suppresses warning messages about files that have the wrong architecture for the +.B \-arch +flag. +.TP +.B \-arch_errors_fatal (32-bit only) +Cause the errors having to do with files that have the wrong architecture to be +fatal and stop the link editor. +.TP +.B \-M (32-bit only) +Produce a load map, listing all the segments and sections. The list +includes the address where each input file's section appears in the +output file, as well as the section's size. +.IP +This option overlaps with a compiler option. +If you use the compiler driver +.IR cc (1) +to invoke \fIld\fR, +invoke this option in this way: +.B \-Wl,\-M. +.TP +.B \-whatsloaded (32-bit only) +Display a single line listing each object file that is +loaded. Names of objects in archives have the form libfoo.a(bar.o). +.TP +.BI \-filelist " listfile[,dirname]" +Specifies that the linker should link the files listed in +.I listfile . +This is an alternative to listing the files on the command line. The file names are listed one per line separated +only by newlines. (Spaces and tabs are assumed to be part of the file name.) +If the optional directory name, +.I dirname +is specified, it is prepended to each name in the list file. +.TP +.BI "\-headerpad" " value" +Specifies the minimum amount of space ("padding") following +the headers for the +.SM MH_EXECUTE +format and all output file types with the dynamic linker. +.I value +is a hexadecimal number. +When a segment's size is rounded up to the segment alignment, there +is extra space left over, which is placed between the headers and the sections, rather than at the end of the segment. The +.B headerpad +option specifies the minimum size of this padding, +which can be useful if the headers will be altered later. +The default value is the larger of 2 * sizeof(struct section) so the program +/usr/bin/objcunique can always add two section headers, or if the output is an +MH_EXECUTE filetype and +.B \-prebind +is specified 3 times the size of the LC_PREBOUND_DYLIB load commands. +The actual amount of pad will be as large as the amount of the first +segment's round-off. +(That is, take the total size of the first segments' +headers and non-zerofill sections, round this size +up to the segment alignment, +and use the difference between the rounded +and unrounded sizes as the minimum amount of padding.) +.TP +.B \-headerpad_max_install_names (32-bit only) +Add to the header padding enough space to allow changing all dynamic shared +library paths recorded in the output file to be changed to MAXPATHLEN in length. +.TP +.B \-t +Trace the progress of the link editor; display the name of each file +that is +loaded as it is processed in the first and second pass of the link +editor. +.TP +.BI \-A " basefile" " (32-bit only)" +Incremental loading: linking is to be done in a manner +that lets the resulting object be read into an already executing +program, the +.IR basefile . +.I basefile +is the name of a file whose symbol table will be taken as a basis +on which to define additional symbols. +Only newly linked material will be entered into the +.BR a.out +file, but the new symbol table will reflect +every symbol defined in the base file and the newly linked files. +Option(s) to specify the addresses of the segments are typically +needed, since +the default addresses tend to overlap with the +.I basefile. +The default format of the object file is +.SM MH_OBJECT. +Note: It is strongly recommended that this option NOT be used, +because the dyld package described in +.IR dyld (3) +is a much easier alternative. +.TP +.BI \-dylib_install_name " name" +For dynamic shared library files, specifies the name of the file +the library will be installed in for programs that use it. If this is not +specified, the name specified in the +.BI \-o " name" +option will be used. +This option is used as the +.IR libtool (1) +.BI \-install_name " name" +option when its +.B \-dynamic +option is specified. +.TP +.BI \-umbrella " framework_name" +Specifies this is a subframework where +.I framework_name +is the name of the umbrella framework this subframework is a part of. Where +.I framework_name +is the same as the argument to the +.BI \-framework " framework_name" +option. This subframework can then only be linked into the umbrella framework +with the same +.I framework_name +or another subframework with the same umbrella framework name. Any other +attempt to statically link this subframework directly will result in an error +stating to link with the umbrella framework instead. When building the umbrella +framework that uses this subframework no additional options are required. +However the install name of the umbrella framework, required to be specified +with +.BR \-dylib_install_name , +must have the proper format for an install name of a framework for the +.I framework_name +of the umbrella framework to be determined. +.TP +.BI \-allowable_client " client_name" " (32-bit only)" +Specifies that for this subframework the +.I client_name +can link with this subframework without error even though it is not part of +the umbrella framework that this subframework is part of. The +.I client_name +can be another framework name or a name used by bundles (see the +.BI \-client_name " client_name" +option below). +.TP +.BI \-client_name " client_name" " (32-bit only)" +Specifies the +.I client_name +of a bundle for checking of allowable clients of subframeworks (see the +.BI \-allowable_client " client_name" +option above). +.TP +.BI \-sub_umbrella " framework_name" +Specifies that the +.I framework_name +being linked by a dynamic library is to be treated as one of the +subframeworks with respect to twolevel namespace. +.TP +.BI \-sub_library " library_name" +Specifies that the +.I library_name +being linked by a dynamic library is to be treated as one of the +sublibraries with respect to twolevel namespace. For example the +.I library_name +for +.I /usr/lib/libobjc_profile.A.dylib +would be +.I libobjc. +.TP +.BI \-init " sym" +The argument +.I sym +is taken to be the symbol name of the dynamic shared library initialization +routine. If any module is used from the dynamic library the library +initialization routine is called before any symbol is used from the library +including C++ static initializers (and #pragma CALL_ON_LOAD routines). +.TP +.B \-run_init_lazily (32-bit only) +This option is obsolete. +.TP +.BI \-dylib_compatibility_version " number" +For dynamic shared library files, this specifies the compatibility version number +of the library. When a library is used by a program, the compatibility version is checked +and if the program's version is greater that the library's version, it is an error. +The format of +.I number +is +.I X[.Y[.Z]] +where +.I X +must be a positive non-zero number less than or equal to 65535, and +.I .Y +and +.I .Z +are optional and if present must be non-negative numbers less than or +equal to 255. +If the compatibility version number is not specified, it has a +value of 0 and no checking is done when the library is used. +This option is used as the +.IR libtool (1) +.BI \-compatibility_version " number" +option +when its +.B \-dynamic +option is set. +.TP +.BI \-dylib_current_version " number" +For dynamic shared library files, specifies the current version number +of the library. The current version of the library can be obtained +programmatically by the user of the library so it can determine exactly which version of the library it is using. +The format of +.I number +is +.I X[.Y[.Z]] +where +.I X +must be a positive non-zero number less than or equal to 65535, and +.I .Y +and +.I .Z +are optional and if present must be non-negative numbers less than or +equal to 255. +If the version number is not specified, it has a +value of 0. +This option is used as the +.IR libtool (1) +.BI \-current_version " number" +option when its +.B \-dynamic +option is set. +.TP +.BI \-single_module +When building a dynamic library build the library so that it contains only +one module. +.TP +.BI \-multi_module (32-bit only) +When building a dynamic library build the library so that it contains one +module for each object file linked in. This is the default. +.TP +.BI \-dylinker_install_name " name" +For dynamic link editor files, specifies the name of the file +the dynamic link editor will be installed in for programs that use it. +.TP +.BI \-macosx_version_min " version" +This overrides the +.B MACOSX_DEPLOYMENT_TARGET +environment variable (see below). Unlike other linker options, this one may +be specified multiple times; only the last occurrence is effective. +.PP +The following environment variable is used to control the use of incompatible +features in the output with respect to Mac OS X releases. +.TP +.B MACOSX_DEPLOYMENT_TARGET +This is set to indicate the oldest Mac OS X version that that the output is to +be used on. When this is set to a release that is older than the current +release features that are incompatible with that release will be disabled. If +a feature is seen in the input that can't be in the output due to this setting +a warning is issued. The current allowable values for this are +.B 10.1, +.B 10.2 +.B 10.3, +and +.B 10.4 +with the default being +.B 10.4 +for the i386 architecture and +.B 10.1 +for all other architectures. +.PP +The following environment variables are used by Apple's Build and Integration +team: +.TP +.B LD_TRACE_ARCHIVES +When this is set it causes a message of the form ``[Logging for XBS] +Used static archive: +.I filename'' +for each static archive that has members linked into the output. +.TP +.B LD_TRACE_DYLIBS +When this is set it causes a message of the form ``[Logging for XBS] +Used dynamic library: +.I filename'' +for each dynamic library linked into the output. +.TP +.B RC_TRACE_PREBINDING_DISABLED +When this is set it causes a message of the form ``[Logging for XBS +prebinding disabled for +.I filename +because +.I reason''. +Where +.I filename +is the value of the +.B \-final_output +argument if specified or the value of the +.B \-o +argument. +.TP +.BI \-final_output " filename" +The argument +.I filename +is used in the above message when RC_TRACE_PREBINDING_DISABLED is set. +.TP +.B LD_TRACE_FILE +When this is set, messages displayed due to the +.B LD_TRACE_ARCHIVES +, +.B LD_TRACE_DYLIBS +, and +.B LD_TRACE_PREBINDING_DISABLED +environment variables are printed to the file whose path is specified +by this variable instead of stdout. +.TP +.B LD_SPLITSEGS_NEW_LIBRARIES +When set and +.B MACOSX_DEPLOYMENT_TARGET +is set to 10.4 or greater and the output is a dynamic library, and if the +install name of the library is not listed the segment address table, and if the +environment variable +.B LD_UNPREBOUND_LIBRARIES +is set with a file name with a list of library install names and the install +name is not listed, then this is built as a split shared library. + +.PP +Options available in early versions of the Mach-O link editor +may no longer be supported. + +.SH FILES +.ta \w'/Network/Library/Frameworks/*.framework/*\ \ 'u +/lib/lib*.{a,dylib} libraries +.br +/usr/lib/lib*.{a,dylib} +.br +/usr/local/lib/lib*.{a,dylib} +.br +/Library/Frameworks/*.framework/* framework libraries +.br +/Network/Library/Frameworks/*.framework/* framework libraries +.br +/System/Library/Frameworks/*.framework/* framework libraries +.br +a.out output file +.SH "SEE ALSO" +as(1), ar(1), cc(1), libtool(1), ranlib(1), nm(1), otool(1) lipo(1), +arch(3), dyld(3), Mach-O(5), strip(1), redo_prebinding(1) diff --git a/ld64.xcodeproj/project.pbxproj b/ld64.xcodeproj/project.pbxproj index 3475042..a468b0d 100644 --- a/ld64.xcodeproj/project.pbxproj +++ b/ld64.xcodeproj/project.pbxproj @@ -40,13 +40,13 @@ F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9023C3F06D5A254001BBF46 /* ld.cpp */; }; F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EED706D5AD240041D381 /* ObjectDump.cpp */; }; F97288E707D277570031794D /* SectCreate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F97288E607D277570031794D /* SectCreate.cpp */; }; - F97F5029070D0BB200B9FCD7 /* ld64.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F97F5028070D0BB200B9FCD7 /* ld64.1 */; }; F9B1A2640A3A563E00DA8FAB /* rebase.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9B1A2580A3A448800DA8FAB /* rebase.1 */; }; F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C0D48A06DD1E1B001C7193 /* Options.cpp */; }; F9EA72D5097454FF008B4F1D /* machochecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EA72D4097454FF008B4F1D /* machochecker.cpp */; }; F9EA7584097882F3008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; F9EA75BC09788857008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; F9EC78060A2F8674002A3E39 /* rebase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EC78050A2F8674002A3E39 /* rebase.cpp */; }; + F9FCC3F20A54A75600CEB866 /* ld64.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9FCC3F10A54A75600CEB866 /* ld64.1 */; }; /* End PBXBuildFile section */ /* Begin PBXBuildRule section */ @@ -73,7 +73,7 @@ isa = PBXContainerItemProxy; containerPortal = F9023C3006D5A227001BBF46 /* Project object */; proxyType = 1; - remoteGlobalIDString = F9EC77ED0A2F85F6002A3E39 /* rebase */; + remoteGlobalIDString = F9EC77ED0A2F85F6002A3E39; remoteInfo = rebase; }; F96D5369094A275D008E9EE8 /* PBXContainerItemProxy */ = { @@ -120,7 +120,7 @@ dstPath = /usr/share/man/man1; dstSubfolderSpec = 0; files = ( - F97F5029070D0BB200B9FCD7 /* ld64.1 in CopyFiles */, + F9FCC3F20A54A75600CEB866 /* ld64.1 in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 1; }; @@ -153,7 +153,7 @@ F971EED706D5AD240041D381 /* ObjectDump.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ObjectDump.cpp; path = src/ObjectDump.cpp; sourceTree = ""; }; F97288E607D277570031794D /* SectCreate.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = SectCreate.cpp; path = src/SectCreate.cpp; sourceTree = ""; }; F972890007D27FD00031794D /* SectCreate.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = SectCreate.h; path = src/SectCreate.h; sourceTree = ""; }; - F97F5028070D0BB200B9FCD7 /* ld64.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld64.1; path = doc/man/man1/ld64.1; sourceTree = ""; }; + F97F5028070D0BB200B9FCD7 /* ld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld.1; path = doc/man/man1/ld.1; sourceTree = ""; }; F9B1A2580A3A448800DA8FAB /* rebase.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = rebase.1; path = doc/man/man1/rebase.1; sourceTree = ""; }; F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/Options.cpp; sourceTree = ""; }; F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/Options.h; sourceTree = ""; }; @@ -163,6 +163,7 @@ F9EA7583097882F3008B4F1D /* debugline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = debugline.h; path = src/debugline.h; sourceTree = ""; }; F9EC77EE0A2F85F6002A3E39 /* rebase */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rebase; sourceTree = BUILT_PRODUCTS_DIR; }; F9EC78050A2F8674002A3E39 /* rebase.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = rebase.cpp; path = src/rebase.cpp; sourceTree = ""; }; + F9FCC3F10A54A75600CEB866 /* ld64.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld64.1; path = doc/man/man1/ld64.1; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -220,7 +221,8 @@ F9EA72D4097454FF008B4F1D /* machochecker.cpp */, F971EED706D5AD240041D381 /* ObjectDump.cpp */, F9EC78050A2F8674002A3E39 /* rebase.cpp */, - F97F5028070D0BB200B9FCD7 /* ld64.1 */, + F97F5028070D0BB200B9FCD7 /* ld.1 */, + F9FCC3F10A54A75600CEB866 /* ld64.1 */, F9B1A2580A3A448800DA8FAB /* rebase.1 */, F9023C3A06D5A23E001BBF46 /* Products */, ); @@ -314,10 +316,13 @@ F9023C3006D5A227001BBF46 /* Project object */ = { isa = PBXProject; buildConfigurationList = F933D92309291AC90083EAC8 /* Build configuration list for PBXProject "ld64" */; + compatibilityVersion = "Xcode 2.4"; hasScannedForEncodings = 0; mainGroup = F9023C2C06D5A227001BBF46; productRefGroup = F9023C3A06D5A23E001BBF46 /* Products */; projectDirPath = ""; + projectRoot = ""; + shouldCheckCompatibility = 1; targets = ( F9B1A2670A3A567B00DA8FAB /* all */, F9023C3806D5A23E001BBF46 /* ld */, @@ -341,7 +346,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/csh; - shellScript = "# Let tests set MACOSX_DEPLOYMENT_TARGET as they need\nunsetenv MACOSX_DEPLOYMENT_TARGET\n\n# make an symlink to ld64 called ld, so that gcc will use this linker for all linking\nrm -rf $BUILT_PRODUCTS_DIR/ld\nln -s $BUILT_PRODUCTS_DIR/ld64 $BUILT_PRODUCTS_DIR/ld\n\n# run full test suite\n$SRCROOT/unit-tests/run-all-unit-tests\n\nexit 0"; + shellScript = "# Let tests set MACOSX_DEPLOYMENT_TARGET as they need\nunsetenv MACOSX_DEPLOYMENT_TARGET\n\n# always use new linker\nsetenv LD_NO_CLASSIC_LINKER\n\n# run full test suite\n$SRCROOT/unit-tests/run-all-unit-tests\n\nexit 0"; }; /* End PBXShellScriptBuildPhase section */ @@ -455,6 +460,7 @@ INSTALL_PATH = /usr/bin; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; OTHER_LDFLAGS = ""; + PREBINDING = NO; PRODUCT_NAME = ld64; SECTORDER_FLAGS = ""; VERSIONING_SYSTEM = "apple-generic"; @@ -501,6 +507,7 @@ INSTALL_PATH = /usr/bin; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; OTHER_LDFLAGS = ""; + PREBINDING = NO; PRODUCT_NAME = ld64; SECTORDER_FLAGS = ""; VALID_ARCHS = "i386 ppc"; diff --git a/src/MachOReaderDylib.hpp b/src/MachOReaderDylib.hpp index 4d1b2f0..055dd4f 100644 --- a/src/MachOReaderDylib.hpp +++ b/src/MachOReaderDylib.hpp @@ -95,7 +95,7 @@ public: virtual bool requiresFollowOnAtom() const{ return false; } virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } virtual std::vector* getLineInfo() const { return NULL; } - virtual uint8_t getAlignment() const { return 0; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } virtual void copyRawContent(uint8_t buffer[]) const {} virtual void setScope(Scope) { } diff --git a/src/MachOReaderRelocatable.hpp b/src/MachOReaderRelocatable.hpp index ea6f6de..310b561 100644 --- a/src/MachOReaderRelocatable.hpp +++ b/src/MachOReaderRelocatable.hpp @@ -66,6 +66,16 @@ namespace relocatable { +class ReferenceSorter +{ +public: + bool operator()(const ObjectFile::Reference* left, const ObjectFile::Reference* right) + { + return ( left->getFixUpOffset() < right->getFixUpOffset() ); + } +}; + + // forward reference template class Reader; template class SymbolAtomSorter; @@ -219,8 +229,8 @@ public: virtual void setSize(uint64_t size) = 0; virtual void addReference(ObjectFile::Reference* ref) = 0; + virtual void sortReferences() = 0; virtual void addLineInfo(const ObjectFile::LineInfo& info) = 0; - virtual void alignAtLeast(uint8_t align) = 0; uint32_t fStabsStartIndex; uint32_t fStabsCount; @@ -255,13 +265,13 @@ public: virtual bool requiresFollowOnAtom() const; virtual ObjectFile::Atom& getFollowOnAtom() const; virtual std::vector* getLineInfo() const { return (std::vector*)&fLineInfo; } - virtual uint8_t getAlignment() const { return fAlignment; } + virtual ObjectFile::Alignment getAlignment() const { return fAlignment; } virtual void copyRawContent(uint8_t buffer[]) const; virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } - virtual void setSize(uint64_t size); - virtual void addReference(ObjectFile::Reference* ref) { fReferences.insert(fReferences.begin(), (Reference*)ref); } + virtual void setSize(uint64_t size) { fSize = size; } + virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } + virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } virtual void addLineInfo(const ObjectFile::LineInfo& info) { fLineInfo.push_back(info); } - virtual void alignAtLeast(uint8_t align) { fAlignment = std::max(align, fAlignment); } protected: typedef typename A::P P; @@ -287,7 +297,7 @@ protected: std::vector fLineInfo; ObjectFile::Atom::Scope fScope; SymbolTableInclusion fSymbolTableInclusion; - uint8_t fAlignment; + ObjectFile::Alignment fAlignment; }; @@ -381,31 +391,8 @@ SymbolAtom::SymbolAtom(Reader& owner, const macho_nlist

* symbol, const else { fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; } -} - - -template -void SymbolAtom::setSize(uint64_t size) -{ - fSize = size; - - if ( fSection->flags() & S_ATTR_SOME_INSTRUCTIONS ) { - // For code, the aligment is based just on the section alignment and code address - if ( fAddress == 0 ) - fAlignment = fSection->align(); - else - fAlignment = std::min((uint8_t)__builtin_ctz(fAddress), (uint8_t)fSection->align()); - } - else { - // For data, compute the alignment base on the address aligned at in object file and the size - uint8_t sizeAlign = __builtin_ctz(fSize); - uint8_t sizeAndSectAlign = std::min((uint8_t)fSection->align(), sizeAlign); - // If address is zero, can't figure out better alignment than section alignment and size - if ( fAddress == 0 ) - fAlignment = sizeAndSectAlign; - else - fAlignment = std::min((uint8_t)__builtin_ctz(fAddress), sizeAndSectAlign); - } + // compute alignment + fAlignment = ObjectFile::Alignment(fSection->align(), fAddress % (1 << fSection->align())); } @@ -461,7 +448,6 @@ void SymbolAtom::copyRawContent(uint8_t buffer[]) const } } - template class SymbolAtomSorter { @@ -520,13 +506,13 @@ public: virtual bool requiresFollowOnAtom() const { return false; } virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; } virtual std::vector* getLineInfo() const { return NULL; } - virtual uint8_t getAlignment() const; + virtual ObjectFile::Alignment getAlignment() const; virtual void copyRawContent(uint8_t buffer[]) const; virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } virtual void setSize(uint64_t size) { } virtual void addReference(ObjectFile::Reference* ref) { throw "ld64: can't add references"; } + virtual void sortReferences() { } virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld64: can't add line info to tentative definition"; } - virtual void alignAtLeast(uint8_t align) { } protected: typedef typename A::P P; @@ -569,7 +555,7 @@ TentativeAtom::TentativeAtom(Reader& owner, const macho_nlist

* symbol) template -uint8_t TentativeAtom::getAlignment() const +ObjectFile::Alignment TentativeAtom::getAlignment() const { // common symbols align to their size // that is, a 4-byte common aligns to 4-bytes @@ -577,9 +563,9 @@ uint8_t TentativeAtom::getAlignment() const uint8_t alignment = (uint8_t)ceil(log2(this->getSize())); // limit alignment of extremely large commons to 2^15 bytes (8-page) if ( alignment < 15 ) - return alignment; + return ObjectFile::Alignment(alignment); else - return 15; + return ObjectFile::Alignment(15); } template @@ -615,13 +601,13 @@ public: virtual bool requiresFollowOnAtom() const; virtual ObjectFile::Atom& getFollowOnAtom() const; virtual std::vector* getLineInfo() const { return NULL; } - virtual uint8_t getAlignment() const; + virtual ObjectFile::Alignment getAlignment() const; virtual void copyRawContent(uint8_t buffer[]) const; virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } virtual void setSize(uint64_t size) { fSize = size; } - virtual void addReference(ObjectFile::Reference* ref) { fReferences.insert(fReferences.begin(), (Reference*)ref); } + virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } + virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } virtual void addLineInfo(const ObjectFile::LineInfo& info) { fprintf(stderr, "ld64: can't add line info to anonymous symbol %s from %s\n", this->getDisplayName(), this->getFile()->getPath()); } - virtual void alignAtLeast(uint8_t align) { } BaseAtom* redirectTo() { return fRedirect; } bool isWeakImportStub() { return fWeakImportStub; } @@ -648,7 +634,6 @@ protected: BaseAtom* fRedirect; bool fDontDeadStrip; bool fWeakImportStub; - bool fReallyNonLazyPointer; // HACK until compiler stops emitting anonymous non-lazy pointers ObjectFile::Atom::SymbolTableInclusion fSymbolTableInclusion; ObjectFile::Atom::Scope fScope; }; @@ -656,7 +641,7 @@ protected: template AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* section, uint32_t addr, uint32_t size) : fOwner(owner), fSynthesizedName(NULL), fSection(section), fAddress(addr), fSize(size), fSegment(NULL), fDontDeadStrip(true), - fWeakImportStub(false), fReallyNonLazyPointer(false), fSymbolTableInclusion(ObjectFile::Atom::kSymbolTableNotIn), + fWeakImportStub(false), fSymbolTableInclusion(ObjectFile::Atom::kSymbolTableNotIn), fScope(ObjectFile::Atom::scopeTranslationUnit) { fSegment = new Segment(fSection); @@ -835,17 +820,12 @@ const char* AnonymousAtom::getDisplayName() const template ObjectFile::Atom::Scope AnonymousAtom::getScope() const { - if ( fReallyNonLazyPointer ) - return ObjectFile::Atom::scopeTranslationUnit; - else - return fScope; + return fScope; } template ObjectFile::Atom::DefinitionKind AnonymousAtom::getDefinitionKind() const { - if ( fReallyNonLazyPointer ) - return ObjectFile::Atom::kRegularDefinition; // in order for literals to be coalesced they must be weak switch ( fSection->flags() & SECTION_TYPE ) { case S_CSTRING_LITERALS: @@ -880,21 +860,19 @@ const char* AnonymousAtom::getSectionName() const } template -uint8_t AnonymousAtom::getAlignment() const +ObjectFile::Alignment AnonymousAtom::getAlignment() const { - if ( fReallyNonLazyPointer ) - return (uint8_t)log2(sizeof(pint_t)); switch ( fSection->flags() & SECTION_TYPE ) { case S_4BYTE_LITERALS: - return 2; + return ObjectFile::Alignment(2); case S_8BYTE_LITERALS: - return 3; + return ObjectFile::Alignment(3); case S_16BYTE_LITERALS: - return 4; + return ObjectFile::Alignment(4); case S_NON_LAZY_SYMBOL_POINTERS: - return (uint8_t)log2(sizeof(pint_t)); + return ObjectFile::Alignment((uint8_t)log2(sizeof(pint_t))); default: - return fSection->align(); + return ObjectFile::Alignment(fSection->align(), fAddress % (1 << fSection->align())); } } @@ -985,7 +963,6 @@ private: Reference* makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect); Reference* makeReferenceToSymbol(Kinds kind, uint32_t atAddr, const macho_nlist

* toSymbol, uint32_t toOffset); void validSectionType(uint8_t type); - void handleAnonymousNonLazyPointers(const macho_section

* sect); BaseAtom* findAtomByName(const char*); @@ -1013,47 +990,6 @@ private: bool fAppleObjc; }; -// usually do nothing -template void Reader::handleAnonymousNonLazyPointers(const macho_section

* sect) { } - -// HACK for ppc64, need to split of anonymous non-lazy-pointers because they must be 8-byte aligned to work with ld instruction -template <> void -Reader::handleAnonymousNonLazyPointers(const macho_section

* dataSect) { - if ( (dataSect->size() >= sizeof(pint_t)) - && (dataSect->align() >= log2(sizeof(pint_t))) - && (strcmp(dataSect->sectname(), "__data") == 0) - && (strcmp(dataSect->segname(), "__DATA") == 0) ) { - std::set lo14targets; - const macho_section

* const sectionsStart = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); - const macho_section

* const sectionsEnd = §ionsStart[fSegment->nsects()]; - for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( strncmp(sect->sectname(), "__text", 6) == 0 ) { - const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fHeader) + sect->reloff()); - const macho_relocation_info

* relocsEnd = &relocs[sect->nreloc()]; - for (const macho_relocation_info

* r = relocs; r < relocsEnd; ++r) { - if ( (r->r_address() & R_SCATTERED) != 0 ) { - const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)r; - if ( sreloc->r_type() == PPC_RELOC_LO14_SECTDIFF ) { - lo14targets.insert(sreloc->r_value()); - } - } - } - } - } - // walk backwards so that newly created anonymous atoms do not mask misalignmented - for (std::set::reverse_iterator it=lo14targets.rbegin(); it != lo14targets.rend(); it++) { - uint32_t targetOfLO14 = *it; - AtomAndOffset found = this->findAtomAndOffset(targetOfLO14); - if ( (found.offset & 0x7) != 0 ) { - AnonymousAtom* newAtom = new AnonymousAtom(*this, dataSect, targetOfLO14, sizeof(pint_t)); - newAtom->fReallyNonLazyPointer = true; - fAtoms.push_back(newAtom); - fAddrToAtom[targetOfLO14] = newAtom; - } - } - } -} - template Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options) : fPath(strdup(path)), fModTime(modTime), fOptions(options), fHeader((const macho_header

*)fileContent), @@ -1282,8 +1218,6 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, case S_REGULAR: case S_ZEROFILL: case S_COALESCED: - // HACK until compiler stops generated anonymous non-lazy pointers rdar://problem/4513414 - handleAnonymousNonLazyPointers(sect); // if there is not an atom already at the start of this section, add an anonymous one uint32_t previousAtomAddr = 0; BaseAtom* previousAtom = NULL; @@ -1369,11 +1303,13 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, uint32_t classSize = ((12 * sizeof(pint_t)) + align-1) & (-align); for (uint32_t offset = 0; offset < sect->size(); offset += classSize) { // add by-name reference to super class - uint32_t superClassNameAddr = P::getP(*(pint_t*)(((uint8_t*)fHeader) + sect->offset() + offset + sizeof(pint_t))); - const char* superStr = (char*)(fHeader) + sect->offset() + superClassNameAddr - sect->addr(); - const char* superClassName; - asprintf((char**)&superClassName, ".objc_class_name_%s", superStr); - makeByNameReference(A::kNoFixUp, sect->addr()+offset+sizeof(pint_t), superClassName, 0); + pint_t superClassNameAddr = P::getP(*(pint_t*)(((uint8_t*)fHeader) + sect->offset() + offset + sizeof(pint_t))); + if ( superClassNameAddr != 0 ) { + const char* superStr = (char*)(fHeader) + sect->offset() + superClassNameAddr - sect->addr(); + const char* superClassName; + asprintf((char**)&superClassName, ".objc_class_name_%s", superStr); + makeByNameReference(A::kNoFixUp, sect->addr()+offset+sizeof(pint_t), superClassName, 0); + } } } else if ( (strcmp(sect->sectname(), "__cls_refs") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) { @@ -1449,6 +1385,9 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, uint32_t curAtomAddress = 0; uint32_t curAtomSize = 0; while ( line_next (lines, &result, line_stop_pc) ) { + // work around weird debug line table compiler generates if no functions in __text section + if ( (curAtom == NULL) && (result.pc == 0) && result.end_of_sequence && (result.file == 1)) + continue; // for performance, see if in next pc is in current atom if ( (curAtom != NULL) && (curAtomAddress <= result.pc) && (result.pc < (curAtomAddress+curAtomSize)) ) { curAtomOffset = result.pc - curAtomAddress; @@ -1561,6 +1500,14 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, stab.string = symString; } } + else { + // might be a debug-note without trailing :G() + currentAtom = findAtomByName(symString); + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + } if ( stab.atom == NULL ) { fprintf(stderr, "can't find atom for N_GSYM stabs %s in %s\n", symString, path); useStab = false; @@ -1673,7 +1620,12 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, } } - + // sort references in each atom + for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { + BaseAtom* atom = (BaseAtom*)(*it); + atom->sortReferences(); + } + #if 0 // special case precompiled header .o file (which has no content) to have one empty atom if ( fAtoms.size() == 0 ) { @@ -2312,10 +2264,7 @@ bool Reader::addRelocReference_powerpc(const macho_section* se } else { dstAddr = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); - Reference* ref = makeReference(A::kAbsLow14, srcAddr, dstAddr); - BaseAtom* target = ((BaseAtom*)&(ref->getTarget())); - if ( target != NULL ) - target->alignAtLeast(3); + makeReference(A::kAbsLow14, srcAddr, dstAddr); } } break; @@ -2461,10 +2410,7 @@ bool Reader::addRelocReference_powerpc(const macho_section* se instruction = BigEndian::get32(*fixUpPtr); lowBits = (instruction & 0xFFFC); displacement = (nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF); - Reference* ref = makeReferenceWithToBase(A::kPICBaseLow14, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); - BaseAtom* target = ((BaseAtom*)&(ref->getTarget())); - if ( target != NULL ) // can be NULL if target is turned into by-name reference - target->alignAtLeast(3); + makeReferenceWithToBase(A::kPICBaseLow14, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); } break; case PPC_RELOC_HA16_SECTDIFF: @@ -2678,7 +2624,7 @@ bool Reader::addRelocReference(const macho_section* sect, con uint64_t dstAddr = 0; uint64_t addend; uint32_t* fixUpPtr; - x86_64::ReferenceKinds kind; + x86_64::ReferenceKinds kind = x86_64::kNoFixUp; bool result = false; const macho_nlist

* targetSymbol = NULL; const char* targetName = NULL; @@ -2706,61 +2652,66 @@ bool Reader::addRelocReference(const macho_section* sect, con case X86_64_RELOC_SIGNED_2: case X86_64_RELOC_SIGNED_4: if ( ! reloc->r_pcrel() ) - throw "not pcrel and X86_64_RELOC_SIGNED not supported"; + throw "not pcrel and X86_64_RELOC_SIGNED* not supported"; if ( reloc->r_length() != 2 ) - throw "length != 2 and X86_64_RELOC_SIGNED not supported"; - kind = x86_64::kPCRel32; - dstAddr = (int64_t)((int32_t)(E::get32(*fixUpPtr))); - switch ( reloc->r_type() ) { - case X86_64_RELOC_SIGNED: - if ( reloc->r_extern() ) { - // Support older relocations - if ( dstAddr == (uint64_t)(-1) ) { + throw "length != 2 and X86_64_RELOC_SIGNED* not supported"; + addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); + if ( reloc->r_extern() ) { + switch ( reloc->r_type() ) { + case X86_64_RELOC_SIGNED: + kind = x86_64::kPCRel32; + // begin support for old .o files before X86_64_RELOC_SIGNED_1 was created + if ( addend == (uint64_t)(-1) ) { + addend = 0; kind = x86_64::kPCRel32_1; - dstAddr = 0; } - else if ( dstAddr == (uint64_t)(-2) ) { + else if ( addend == (uint64_t)(-2) ) { + addend = 0; kind = x86_64::kPCRel32_2; - dstAddr = 0; } - else if ( dstAddr == (uint64_t)(-4) ) { + else if ( addend == (uint64_t)(-4) ) { + addend = 0; kind = x86_64::kPCRel32_4; - dstAddr = 0; } - } - break; - case X86_64_RELOC_SIGNED_1: - if ( reloc->r_extern() ) { - dstAddr = 0; - } else { - dstAddr += 1; - } - kind = x86_64::kPCRel32_1; - break; - case X86_64_RELOC_SIGNED_2: - if ( reloc->r_extern() ) { - dstAddr = 0; - } else { - dstAddr += 2; - } - kind = x86_64::kPCRel32_2; - break; - case X86_64_RELOC_SIGNED_4: - if ( reloc->r_extern() ) { - dstAddr = 0; - } else { - dstAddr += 4; - } - kind = x86_64::kPCRel32_4; - break; - default: - break; + break; + // end support for old .o files before X86_64_RELOC_SIGNED_1 was created + case X86_64_RELOC_SIGNED_1: + kind = x86_64::kPCRel32_1; + addend += 1; + break; + case X86_64_RELOC_SIGNED_2: + kind = x86_64::kPCRel32_2; + addend += 2; + break; + case X86_64_RELOC_SIGNED_4: + kind = x86_64::kPCRel32_4; + addend += 4; + break; + } + makeReferenceToSymbol(kind, srcAddr, targetSymbol, addend); } - if ( reloc->r_extern() ) - makeReferenceToSymbol(kind, srcAddr, targetSymbol, dstAddr); else { - makeReference(kind, srcAddr, srcAddr+4+dstAddr); - } + uint64_t ripRelativeOffset = addend; + switch ( reloc->r_type() ) { + case X86_64_RELOC_SIGNED: + dstAddr = srcAddr + 4 + ripRelativeOffset; + kind = x86_64::kPCRel32; + break; + case X86_64_RELOC_SIGNED_1: + dstAddr = srcAddr + 5 + ripRelativeOffset; + kind = x86_64::kPCRel32_1; + break; + case X86_64_RELOC_SIGNED_2: + dstAddr = srcAddr + 6 + ripRelativeOffset; + kind = x86_64::kPCRel32_2; + break; + case X86_64_RELOC_SIGNED_4: + dstAddr = srcAddr + 8 + ripRelativeOffset; + kind = x86_64::kPCRel32_4; + break; + } + makeReference(kind, srcAddr, dstAddr); + } break; case X86_64_RELOC_BRANCH: if ( ! reloc->r_pcrel() ) diff --git a/src/MachOWriterExecutable.hpp b/src/MachOWriterExecutable.hpp index 0aa95b7..7ac12fe 100644 --- a/src/MachOWriterExecutable.hpp +++ b/src/MachOWriterExecutable.hpp @@ -169,7 +169,7 @@ private: void partitionIntoSections(); bool addBranchIslands(); bool addPPCBranchIslands(); - uint8_t branch24Reference(); + bool isBranch24Reference(uint8_t kind); void adjustLoadCommandsAndPadding(); void createDynamicLinkerCommand(); void createDylibCommands(); @@ -357,7 +357,7 @@ public: virtual bool requiresFollowOnAtom() const { return false; } virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } virtual std::vector* getLineInfo() const { return NULL; } - virtual uint8_t getAlignment() const { return 2; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(2); } virtual void copyRawContent(uint8_t buffer[]) const { throw "don't use copyRawContent"; } virtual void setScope(Scope) { } @@ -386,7 +386,7 @@ public: virtual bool isZeroFill() const { return true; } virtual uint64_t getSize() const { return fSize; } virtual const char* getSectionName() const { return "._zeropage"; } - virtual uint8_t getAlignment() const { return 12; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } void setSize(uint64_t size) { fSize = size; } private: using WriterAtom::fWriter; @@ -404,7 +404,7 @@ public: virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } virtual uint64_t getSize() const { return 0; } - virtual uint8_t getAlignment() const { return 12; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } virtual const char* getSectionName() const { return "._mach_header"; } virtual void copyRawContent(uint8_t buffer[]) const {} }; @@ -420,7 +420,7 @@ public: virtual ObjectFile::Atom::Scope getScope() const; virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const; virtual uint64_t getSize() const { return sizeof(macho_header); } - virtual uint8_t getAlignment() const { return 12; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } virtual const char* getSectionName() const { return "._mach_header"; } virtual void copyRawContent(uint8_t buffer[]) const; private: @@ -438,7 +438,7 @@ public: virtual bool isZeroFill() const { return true; } virtual uint64_t getSize() const { return fWriter.fOptions.customStackSize(); } virtual const char* getSectionName() const { return "._stack"; } - virtual uint8_t getAlignment() const { return 12; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } private: using WriterAtom::fWriter; typedef typename A::P P; @@ -450,6 +450,8 @@ class LoadCommandAtom : public WriterAtom { protected: LoadCommandAtom(Writer& writer, Segment& segment) : WriterAtom(writer, segment) {} + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(log2(sizeof(typename A::P::uint_t))); } + virtual const char* getSectionName() const { return "._load_commands"; } static uint64_t alignedSize(uint64_t size); }; @@ -463,8 +465,6 @@ public: { writer.fSegmentCommands = this; } virtual const char* getDisplayName() const { return "segment load commands"; } virtual uint64_t getSize() const { return fSize; } - virtual uint8_t getAlignment() const { return 2; } - virtual const char* getSectionName() const { return "._load_commands"; } virtual void copyRawContent(uint8_t buffer[]) const; void computeSize(); @@ -485,8 +485,6 @@ public: SymbolTableLoadCommandsAtom(Writer&); virtual const char* getDisplayName() const { return "symbol table load commands"; } virtual uint64_t getSize() const; - virtual uint8_t getAlignment() const { return 2; } - virtual const char* getSectionName() const { return "._load_commands"; } virtual void copyRawContent(uint8_t buffer[]) const; unsigned int commandCount(); @@ -505,8 +503,6 @@ public: : LoadCommandAtom(writer, Segment::fgTextSegment) {} virtual const char* getDisplayName() const { return "thread load commands"; } virtual uint64_t getSize() const; - virtual uint8_t getAlignment() const { return 2; } - virtual const char* getSectionName() const { return "._load_commands"; } virtual void copyRawContent(uint8_t buffer[]) const; private: using WriterAtom::fWriter; @@ -522,8 +518,6 @@ public: DyldLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} virtual const char* getDisplayName() const { return "dyld load command"; } virtual uint64_t getSize() const; - virtual uint8_t getAlignment() const { return 2; } - virtual const char* getSectionName() const { return "._load_commands"; } virtual void copyRawContent(uint8_t buffer[]) const; private: using WriterAtom::fWriter; @@ -538,8 +532,6 @@ public: LoadCommandAtom(writer, Segment::fgTextSegment), clientString(client) {} virtual const char* getDisplayName() const { return "allowable_client load command"; } virtual uint64_t getSize() const; - virtual uint8_t getAlignment() const { return 2; } - virtual const char* getSectionName() const { return "._load_commands"; } virtual void copyRawContent(uint8_t buffer[]) const; private: using WriterAtom::fWriter; @@ -555,8 +547,6 @@ public: : LoadCommandAtom(writer, Segment::fgTextSegment), fInfo(info) {} virtual const char* getDisplayName() const { return "dylib load command"; } virtual uint64_t getSize() const; - virtual uint8_t getAlignment() const { return 2; } - virtual const char* getSectionName() const { return "._load_commands"; } virtual void copyRawContent(uint8_t buffer[]) const; private: using WriterAtom::fWriter; @@ -571,8 +561,6 @@ public: DylibIDLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} virtual const char* getDisplayName() const { return "dylib ID load command"; } virtual uint64_t getSize() const; - virtual uint8_t getAlignment() const { return 2; } - virtual const char* getSectionName() const { return "._load_commands"; } virtual void copyRawContent(uint8_t buffer[]) const; private: using WriterAtom::fWriter; @@ -586,8 +574,6 @@ public: RoutinesLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} virtual const char* getDisplayName() const { return "routines load command"; } virtual uint64_t getSize() const { return sizeof(macho_routines_command); } - virtual uint8_t getAlignment() const { return 2; } - virtual const char* getSectionName() const { return "._load_commands"; } virtual void copyRawContent(uint8_t buffer[]) const; private: using WriterAtom::fWriter; @@ -602,8 +588,6 @@ public: : LoadCommandAtom(writer, Segment::fgTextSegment), fName(name) {} virtual const char* getDisplayName() const { return "sub-umbrella load command"; } virtual uint64_t getSize() const; - virtual uint8_t getAlignment() const { return 2; } - virtual const char* getSectionName() const { return "._load_commands"; } virtual void copyRawContent(uint8_t buffer[]) const; private: typedef typename A::P P; @@ -618,8 +602,6 @@ public: : LoadCommandAtom(writer, Segment::fgTextSegment), fNameStart(nameStart), fNameLength(nameLen) {} virtual const char* getDisplayName() const { return "sub-library load command"; } virtual uint64_t getSize() const; - virtual uint8_t getAlignment() const { return 2; } - virtual const char* getSectionName() const { return "._load_commands"; } virtual void copyRawContent(uint8_t buffer[]) const; private: using WriterAtom::fWriter; @@ -636,8 +618,6 @@ public: : LoadCommandAtom(writer, Segment::fgTextSegment), fName(name) {} virtual const char* getDisplayName() const { return "umbrella load command"; } virtual uint64_t getSize() const; - virtual uint8_t getAlignment() const { return 2; } - virtual const char* getSectionName() const { return "._load_commands"; } virtual void copyRawContent(uint8_t buffer[]) const; private: using WriterAtom::fWriter; @@ -653,8 +633,6 @@ public: : LoadCommandAtom(writer, Segment::fgTextSegment), fEmit(false) { ::uuid_generate_random(fUUID);} virtual const char* getDisplayName() const { return "uuid load command"; } virtual uint64_t getSize() const { return fEmit ? sizeof(macho_uuid_command) : 0; } - virtual uint8_t getAlignment() const { return 2; } - virtual const char* getSectionName() const { return "._load_commands"; } virtual void copyRawContent(uint8_t buffer[]) const; virtual void emit() { fEmit = true; } private: @@ -672,7 +650,6 @@ public: : WriterAtom(writer, Segment::fgTextSegment), fSize(0) {} virtual const char* getDisplayName() const { return "header padding"; } virtual uint64_t getSize() const { return fSize; } - virtual uint8_t getAlignment() const { return 2; } virtual const char* getSectionName() const { return "._load_cmds_pad"; } virtual void copyRawContent(uint8_t buffer[]) const; @@ -700,7 +677,7 @@ public: SectionRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } virtual const char* getDisplayName() const { return "section relocations"; } virtual uint64_t getSize() const; - virtual uint8_t getAlignment() const { return 3; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(3); } virtual const char* getSectionName() const { return "._section_relocs"; } virtual void copyRawContent(uint8_t buffer[]) const; private: @@ -715,7 +692,7 @@ public: LocalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } virtual const char* getDisplayName() const { return "local relocations"; } virtual uint64_t getSize() const; - virtual uint8_t getAlignment() const { return 3; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(3); } virtual const char* getSectionName() const { return "._local_relocs"; } virtual void copyRawContent(uint8_t buffer[]) const; private: @@ -730,7 +707,6 @@ public: SymbolTableLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } virtual const char* getDisplayName() const { return "symbol table"; } virtual uint64_t getSize() const; - virtual uint8_t getAlignment() const { return 2; } virtual const char* getSectionName() const { return "._symbol_table"; } virtual void copyRawContent(uint8_t buffer[]) const; private: @@ -745,7 +721,7 @@ public: ExternalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } virtual const char* getDisplayName() const { return "external relocations"; } virtual uint64_t getSize() const; - virtual uint8_t getAlignment() const { return 3; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(3); } virtual const char* getSectionName() const { return "._extern_relocs"; } virtual void copyRawContent(uint8_t buffer[]) const; private: @@ -765,7 +741,6 @@ public: IndirectTableLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } virtual const char* getDisplayName() const { return "indirect symbol table"; } virtual uint64_t getSize() const; - virtual uint8_t getAlignment() const { return 2; } virtual const char* getSectionName() const { return "._indirect_syms"; } virtual void copyRawContent(uint8_t buffer[]) const; @@ -789,7 +764,6 @@ public: StringsLinkEditAtom(Writer& writer); virtual const char* getDisplayName() const { return "string pool"; } virtual uint64_t getSize() const; - virtual uint8_t getAlignment() const { return 2; } virtual const char* getSectionName() const { return "._string_pool"; } virtual void copyRawContent(uint8_t buffer[]) const; @@ -856,8 +830,8 @@ public: StubAtom(Writer& writer, ObjectFile::Atom& target); virtual const char* getName() const { return fName; } virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual uint8_t getAlignment() const { return 2; } virtual uint64_t getSize() const; + virtual ObjectFile::Alignment getAlignment() const; virtual const char* getSectionName() const { return "__symbol_stub1"; } virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } virtual void copyRawContent(uint8_t buffer[]) const; @@ -878,7 +852,6 @@ public: StubHelperAtom(Writer& writer, ObjectFile::Atom& target, ObjectFile::Atom& lazyPointer); virtual const char* getName() const { return fName; } virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual uint8_t getAlignment() const { return 2; } virtual uint64_t getSize() const; virtual const char* getSectionName() const { return "__stub_helper"; } virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } @@ -989,7 +962,7 @@ struct ExportSorter template Writer::Writer(const char* path, Options& options, std::vector& dynamicLibraries) : ExecutableFile::Writer(dynamicLibraries), fFilePath(strdup(path)), fOptions(options), fLoadCommandsSection(NULL), - fLoadCommandsSegment(NULL), fPadSegmentInfo(NULL), fPageZeroAtom(NULL), fLargestAtomSize(1), + fLoadCommandsSegment(NULL), fPadSegmentInfo(NULL), fPageZeroAtom(NULL), fSymbolTableCount(0), fLargestAtomSize(1), fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(false), fSeenFollowOnReferences(false), fWritableSegmentPastFirst4GB(false), fFirstWritableSegment(NULL) { @@ -997,7 +970,7 @@ Writer::Writer(const char* path, Options& options, std::vector::scanForAbsoluteReferences() //fprintf(stderr, "found -mdyanmic-no-pic codegen in %s in %s\n", atom->getDisplayName(), atom->getFile()->getPath()); // shrink page-zero and add pad segment to compensate fPadSegmentInfo = new SegmentInfo(); - strcpy(fPadSegmentInfo->fName, "__4BGFILL"); + strcpy(fPadSegmentInfo->fName, "__4GBFILL"); fPageZeroAtom->setSize(0x1000); return; } @@ -3497,7 +3470,7 @@ void Writer::partitionIntoSections() currentSectionInfo = new SectionInfo(); strcpy(currentSectionInfo->fSectionName, atom->getSectionName()); strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName()); - currentSectionInfo->fAlignment = atom->getAlignment(); + currentSectionInfo->fAlignment = atom->getAlignment().powerOf2; currentSectionInfo->fAllZeroFill = atom->isZeroFill(); currentSectionInfo->fVirtualSection = ( (currentSectionInfo->fSectionName[0] == '.') || (oneSegmentCommand && (atom->getDefinitionKind()==ObjectFile::Atom::kTentativeDefinition)) && !fOptions.makeTentativeDefinitionsReal() ); @@ -3529,7 +3502,7 @@ void Writer::partitionIntoSections() currentSectionInfo->fAtoms.reserve(fAllAtoms->size()/4); // reduce reallocations by starting large strcpy(currentSectionInfo->fSectionName, atom->getSectionName()); strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName()); - currentSectionInfo->fAlignment = atom->getAlignment(); + currentSectionInfo->fAlignment = atom->getAlignment().powerOf2; // check for -sectalign override std::vector& alignmentOverrides = fOptions.sectionAlignments(); for(std::vector::iterator it=alignmentOverrides.begin(); it != alignmentOverrides.end(); ++it) { @@ -3572,13 +3545,20 @@ void Writer::partitionIntoSections() // change section object to be Writer's SectionInfo object atom->setSection(currentSectionInfo); // section alignment is that of a contained atom with the greatest alignment - uint8_t atomAlign = atom->getAlignment(); + uint8_t atomAlign = atom->getAlignment().powerOf2; if ( currentSectionInfo->fAlignment < atomAlign ) currentSectionInfo->fAlignment = atomAlign; // calculate section offset for this atom uint64_t offset = currentSectionInfo->fSize; uint64_t alignment = 1 << atomAlign; - offset = ( (offset+alignment-1) & (-alignment) ); + uint64_t currentModulus = (offset % alignment); + uint64_t requiredModulus = atom->getAlignment().modulus; + if ( currentModulus != requiredModulus ) { + if ( requiredModulus > currentModulus ) + offset += requiredModulus-currentModulus; + else + offset += requiredModulus+alignment-currentModulus; + } atom->setSectionOffset(offset); uint64_t curAtomSize = atom->getSize(); currentSectionInfo->fSize = offset + curAtomSize; @@ -3631,15 +3611,25 @@ bool Writer::addBranchIslands() template <> -inline uint8_t Writer::branch24Reference() +bool Writer::isBranch24Reference(uint8_t kind) { - return ppc::kBranch24; + switch (kind) { + case ppc::kBranch24: + case ppc::kBranch24WeakImport: + return true; + } + return false; } template <> -inline uint8_t Writer::branch24Reference() +bool Writer::isBranch24Reference(uint8_t kind) { - return ppc64::kBranch24; + switch (kind) { + case ppc64::kBranch24: + case ppc64::kBranch24WeakImport: + return true; + } + return false; } // @@ -3667,16 +3657,17 @@ inline uint8_t Writer::branch24Reference() template bool Writer::addPPCBranchIslands() { + bool log = false; bool result = false; // Can only possibly need branch islands if __TEXT segment > 16M if ( fLoadCommandsSegment->fSize > 16000000 ) { - //fprintf(stderr, "ld64: checking for branch islands, __TEXT segment size=%llu\n", fLoadCommandsSegment->fSize); + if ( log) fprintf(stderr, "ld64: checking for branch islands, __TEXT segment size=%llu\n", fLoadCommandsSegment->fSize); const uint32_t kBetweenRegions = 15000000; // place regions of islands every 15MB in __text section SectionInfo* textSection = NULL; for (std::vector::iterator it=fLoadCommandsSegment->fSections.begin(); it != fLoadCommandsSegment->fSections.end(); it++) { if ( strcmp((*it)->fSectionName, "__text") == 0 ) { textSection = *it; - //fprintf(stderr, "ld64: checking for branch islands, __text section size=%llu\n", textSection->fSize); + if ( log) fprintf(stderr, "ld64: checking for branch islands, __text section size=%llu\n", textSection->fSize); break; } } @@ -3692,33 +3683,68 @@ bool Writer::addPPCBranchIslands() std::vector& references = atom->getReferences(); for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { ObjectFile::Reference* ref = *rit; - if ( ref->getKind() == this->branch24Reference() ) { + if ( this->isBranch24Reference(ref->getKind()) ) { ObjectFile::Atom& target = ref->getTarget(); int64_t srcAddr = atom->getAddress() + ref->getFixUpOffset(); int64_t dstAddr = target.getAddress() + ref->getTargetOffset(); int64_t displacement = dstAddr - srcAddr; + TargetAndOffset finalTargetAndOffset = { &target, ref->getTargetOffset() }; const int64_t kFifteenMegLimit = kBetweenRegions; - if ( (displacement > kFifteenMegLimit) || (displacement < (-kFifteenMegLimit)) ) { - for (int i=0; i < kIslandRegionsCount; ++i) { - AtomToIsland* region=®ionsMap[i]; + if ( displacement > kFifteenMegLimit ) { + // create forward branch chain + ObjectFile::Atom* nextTarget = ⌖ + uint64_t nextTargetOffset = ref->getTargetOffset(); + for (int i=kIslandRegionsCount-1; i >=0 ; --i) { + AtomToIsland* region = ®ionsMap[i]; int64_t islandRegionAddr = kBetweenRegions * (i+1); - if ( ((srcAddr < islandRegionAddr) && (dstAddr > islandRegionAddr)) - ||((dstAddr < islandRegionAddr) && (srcAddr > islandRegionAddr)) ) { - TargetAndOffset islandTarget = { &target, ref->getTargetOffset() }; - AtomToIsland::iterator pos = region->find(islandTarget); + if ( (srcAddr < islandRegionAddr) && (islandRegionAddr <= dstAddr) ) { + AtomToIsland::iterator pos = region->find(finalTargetAndOffset); if ( pos == region->end() ) { - BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, target, ref->getTargetOffset()); + BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, *nextTarget, nextTargetOffset); island->setSection(textSection); - (*region)[islandTarget] = island; + (*region)[finalTargetAndOffset] = island; + if (log) fprintf(stderr, "added island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName()); regionsIslands[i].push_back(island); ++islandCount; - ref->setTarget(*island, 0); + nextTarget = island; + nextTargetOffset = 0; } else { - ref->setTarget(*(pos->second), 0); + nextTarget = pos->second; + nextTargetOffset = 0; } } } + if (log) fprintf(stderr, "using island %s for %s\n", nextTarget->getDisplayName(), atom->getDisplayName()); + ref->setTarget(*nextTarget, nextTargetOffset); + } + else if ( displacement < (-kFifteenMegLimit) ) { + // create back branching chain + ObjectFile::Atom* prevTarget = ⌖ + uint64_t prevTargetOffset = ref->getTargetOffset(); + for (int i=0; i < kIslandRegionsCount ; ++i) { + AtomToIsland* region = ®ionsMap[i]; + int64_t islandRegionAddr = kBetweenRegions * (i+1); + if ( (dstAddr <= islandRegionAddr) && (islandRegionAddr < srcAddr) ) { + AtomToIsland::iterator pos = region->find(finalTargetAndOffset); + if ( pos == region->end() ) { + BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, *prevTarget, prevTargetOffset); + island->setSection(textSection); + (*region)[finalTargetAndOffset] = island; + if (log) fprintf(stderr, "added back island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName()); + regionsIslands[i].push_back(island); + ++islandCount; + prevTarget = island; + prevTargetOffset = 0; + } + else { + prevTarget = pos->second; + prevTargetOffset = 0; + } + } + } + if (log) fprintf(stderr, "using back island %s for %s\n", prevTarget->getDisplayName(), atom->getDisplayName()); + ref->setTarget(*prevTarget, prevTargetOffset); } } } @@ -3726,32 +3752,36 @@ bool Writer::addPPCBranchIslands() // insert islands into __text section and adjust section offsets if ( islandCount > 0 ) { - //fprintf(stderr, "ld64: %u branch islands required\n", islandCount); + if ( log) fprintf(stderr, "ld64: %u branch islands required\n", islandCount); std::vector newAtomList; newAtomList.reserve(textSection->fAtoms.size()+islandCount); uint64_t islandRegionAddr = kBetweenRegions; + uint64_t textSectionAlignment = (1 << textSection->fAlignment); int regionIndex = 0; + uint64_t atomSlide = 0; uint64_t sectionOffset = 0; for (std::vector::iterator it=textSection->fAtoms.begin(); it != textSection->fAtoms.end(); it++) { ObjectFile::Atom* atom = *it; newAtomList.push_back(atom); if ( atom->getAddress() > islandRegionAddr ) { + uint64_t islandStartOffset = atom->getSectionOffset(); + sectionOffset = islandStartOffset + atomSlide; std::vector* regionIslands = ®ionsIslands[regionIndex]; for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { ObjectFile::Atom* islandAtom = *rit; newAtomList.push_back(islandAtom); - uint64_t alignment = 1 << (islandAtom->getAlignment()); + uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2); sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); islandAtom->setSectionOffset(sectionOffset); sectionOffset += islandAtom->getSize(); } ++regionIndex; islandRegionAddr += kBetweenRegions; + uint64_t islandRegionAlignmentBlocks = (sectionOffset - islandStartOffset + textSectionAlignment - 1) / textSectionAlignment; + atomSlide += (islandRegionAlignmentBlocks * textSectionAlignment); } - uint64_t alignment = 1 << (atom->getAlignment()); - sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); - atom->setSectionOffset(sectionOffset); - sectionOffset += atom->getSize(); + if ( atomSlide != 0 ) + atom->setSectionOffset(atom->getSectionOffset()+atomSlide); } // put any remaining islands at end of __text section if ( regionIndex < kIslandRegionsCount ) { @@ -3760,7 +3790,7 @@ bool Writer::addPPCBranchIslands() for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { ObjectFile::Atom* islandAtom = *rit; newAtomList.push_back(islandAtom); - uint64_t alignment = 1 << (islandAtom->getAlignment()); + uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2); sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); islandAtom->setSectionOffset(sectionOffset); sectionOffset += islandAtom->getSize(); @@ -3788,7 +3818,7 @@ void Writer::adjustLoadCommandsAndPadding() const unsigned int atomCount = loadCommandAtoms.size(); for (unsigned int i=0; i < atomCount; ++i) { ObjectFile::Atom* atom = loadCommandAtoms[i]; - uint64_t alignment = 1 << atom->getAlignment(); + uint64_t alignment = 1 << atom->getAlignment().powerOf2; offset = ( (offset+alignment-1) & (-alignment) ); atom->setSectionOffset(offset); uint32_t atomSize = atom->getSize(); @@ -4005,7 +4035,7 @@ void Writer::adjustLinkEditSections() lastSeg->fSections[i]->setBaseAddress(address); for (unsigned int j=0; j < atomCount; ++j) { ObjectFile::Atom* atom = atoms[j]; - uint64_t alignment = 1 << atom->getAlignment(); + uint64_t alignment = 1 << atom->getAlignment().powerOf2; sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); atom->setSectionOffset(sectionOffset); uint64_t size = atom->getSize(); @@ -4981,6 +5011,17 @@ bool StubAtom::pic() const return ( fWriter.fOptions.outputKind() != Options::kDynamicExecutable ); } +template <> +ObjectFile::Alignment StubAtom::getAlignment() const +{ + return 2; +} + +template <> +ObjectFile::Alignment StubAtom::getAlignment() const +{ + return 2; +} template <> StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target) @@ -5069,7 +5110,7 @@ uint64_t StubAtom::getSize() const } template <> -uint8_t StubAtom::getAlignment() const +ObjectFile::Alignment StubAtom::getAlignment() const { // special case x86 fast stubs to be byte aligned return 0; @@ -5140,7 +5181,7 @@ void StubAtom::copyRawContent(uint8_t buffer[]) const // x86_64 stubs are 7 bytes and need no alignment template <> -uint8_t StubAtom::getAlignment() const +ObjectFile::Alignment StubAtom::getAlignment() const { return 0; } diff --git a/src/ObjectDump.cpp b/src/ObjectDump.cpp index 4150fda..accdf55 100644 --- a/src/ObjectDump.cpp +++ b/src/ObjectDump.cpp @@ -208,7 +208,7 @@ static void dumpAtom(ObjectFile::Atom* atom) printf("size: 0x%012llX\n", atom->getSize()); // alignment - printf("align: %d\n", atom->getAlignment()); + printf("align: %u mod %u\n", atom->getAlignment().modulus, (1 << atom->getAlignment().powerOf2) ); // content if ( sDumpContent ) { diff --git a/src/ObjectFile.h b/src/ObjectFile.h index 98e59f4..69aeac6 100644 --- a/src/ObjectFile.h +++ b/src/ObjectFile.h @@ -154,6 +154,13 @@ protected: }; +struct Alignment +{ + Alignment(int p2, int m=0) : powerOf2(p2), modulus(m) {} + uint8_t leadingZeros() const { return (modulus==0) ? powerOf2 : __builtin_clz(modulus); } + uint16_t powerOf2; + uint16_t modulus; +}; // // An atom is the fundamental unit of linking. A C function or global variable is an atom. @@ -212,7 +219,7 @@ public: virtual bool requiresFollowOnAtom() const = 0; virtual Atom& getFollowOnAtom() const = 0; virtual std::vector* getLineInfo() const = 0; - virtual uint8_t getAlignment() const = 0; + virtual Alignment getAlignment() const = 0; virtual void copyRawContent(uint8_t buffer[]) const = 0; virtual void setScope(Scope) = 0; diff --git a/src/Options.cpp b/src/Options.cpp index 8b56436..1eb135b 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -374,8 +374,14 @@ void Options::parseArch(const char* architecture) fArchitecture = CPU_TYPE_I386; else if ( strcmp(architecture, "x86_64") == 0 ) fArchitecture = CPU_TYPE_X86_64; + // compatibility support for cpu-sub-types + else if ( (strcmp(architecture, "ppc750") == 0) + || (strcmp(architecture, "ppc7400") == 0) + || (strcmp(architecture, "ppc7450") == 0) + || (strcmp(architecture, "ppc970") == 0) ) + fArchitecture = CPU_TYPE_POWERPC; else - throw "-arch followed by unknown architecture name"; + throwf(" unknown/unsupported architecture name for: -arch %s", architecture); } bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) @@ -451,23 +457,45 @@ Options::FileInfo Options::findLibrary(const char* rootName) throwf("library not found for -l%s", rootName); } +Options::FileInfo Options::findFramework(const char* frameworkName) +{ + if ( frameworkName == NULL ) + throw "-frameowrk missing next argument"; + char temp[strlen(frameworkName)+1]; + strcpy(temp, frameworkName); + const char* name = temp; + const char* suffix = NULL; + char* comma = strchr(temp, ','); + if ( comma != NULL ) { + *comma = '\0'; + suffix = &comma[1]; + } + return findFramework(name, suffix); +} -Options::FileInfo Options::findFramework(const char* rootName) +Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) { struct stat statBuffer; - const int rootNameLen = strlen(rootName); for (std::vector::iterator it = fFrameworkSearchPaths.begin(); it != fFrameworkSearchPaths.end(); it++) { // ??? Shouldn't we be using String here and just initializing it? // ??? Use str.c_str () to pull out the string for the stat call. const char* dir = *it; - char possiblePath[strlen(dir)+2*rootNameLen+20]; + char possiblePath[PATH_MAX]; strcpy(possiblePath, dir); strcat(possiblePath, "/"); strcat(possiblePath, rootName); strcat(possiblePath, ".framework/"); strcat(possiblePath, rootName); + if ( suffix != NULL ) { + char realPath[PATH_MAX]; + // no symlink in framework to suffix variants, so follow main symlink + if ( realpath(possiblePath, realPath) != NULL ) { + strcpy(possiblePath, realPath); + strcat(possiblePath, suffix); + } + } bool found = (stat(possiblePath, &statBuffer) == 0); if ( fTraceDylibSearching ) printf("[Logging for XBS]%sfound framework: '%s'\n", @@ -480,7 +508,11 @@ Options::FileInfo Options::findFramework(const char* rootName) return result; } } - throwf("framework not found %s", rootName); + // try without suffix + if ( suffix != NULL ) + return findFramework(rootName, NULL); + else + throwf("framework not found %s", rootName); } Options::FileInfo Options::findFile(const char* path) @@ -1815,3 +1847,7 @@ void Options::checkIllegalOptionCombinations() throw "-r and -dead_strip cannot be used together\n"; } + + + + diff --git a/src/Options.h b/src/Options.h index dcedf8e..ad2e45b 100644 --- a/src/Options.h +++ b/src/Options.h @@ -172,7 +172,8 @@ private: void buildSearchPaths(int argc, const char* argv[]); void parseArch(const char* architecture); FileInfo findLibrary(const char* rootName); - FileInfo findFramework(const char* rootName); + FileInfo findFramework(const char* frameworkName); + FileInfo findFramework(const char* rootName, const char* suffix); bool checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result); uint32_t parseVersionNumber(const char*); @@ -194,7 +195,6 @@ private: void reconfigureDefaults(); - ObjectFile::ReaderOptions fReaderOptions; const char* fOutputFile; std::vector fInputFiles; diff --git a/src/SectCreate.cpp b/src/SectCreate.cpp index a77a787..5677c32 100644 --- a/src/SectCreate.cpp +++ b/src/SectCreate.cpp @@ -80,7 +80,7 @@ public: virtual bool requiresFollowOnAtom() const{ return false; } virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } virtual std::vector* getLineInfo() const { return NULL; } - virtual uint8_t getAlignment() const { return 4; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(4); } virtual void copyRawContent(uint8_t buffer[]) const; virtual void setScope(Scope) { } diff --git a/src/ld.cpp b/src/ld.cpp index e26c581..d088f9a 100644 --- a/src/ld.cpp +++ b/src/ld.cpp @@ -38,6 +38,7 @@ #include +#include #include #include #include @@ -318,14 +319,14 @@ private: void writeDotOutput(); static bool minimizeStab(ObjectFile::Reader::Stab& stab); static const char* truncateStabString(const char* str); - void collectStabs(); + void collectDebugInfo(); void writeOutput(); ObjectFile::Atom* entryPoint(); ObjectFile::Atom* dyldHelper(); const char* assureFullPath(const char* path); void markLive(ObjectFile::Atom& atom, Linker::WhyLiveBackChain* previous); - void collectStabs(ObjectFile::Reader* reader, std::map& atomOrdinals); - void synthesizeStabs(ObjectFile::Reader* reader); + void collectStabs(ObjectFile::Reader* reader, std::map& atomOrdinals); + void synthesizeDebugNotes(std::vector& allAtomsByReader); void printStatistics(); void printTime(const char* msg, uint64_t partTime, uint64_t totalTime); char* commatize(uint64_t in, char* out); @@ -392,6 +393,7 @@ private: std::set fLiveAtoms; std::set fLiveRootAtoms; std::vector fStabs; + std::vector fAtomsWithUnresolvedReferences; bool fCreateUUID; SectionOrder fSectionOrder; unsigned int fNextSortOrder; @@ -408,6 +410,7 @@ private: uint64_t fStartLoadUndefinesTime; uint64_t fStartResolveTime; uint64_t fStartSortTime; + uint64_t fStartDebugTime; uint64_t fStartWriteTime; uint64_t fEndTime; uint64_t fTotalObjectSize; @@ -570,7 +573,7 @@ void Linker::link() this->sortAtoms(); this->tweakLayout(); this->writeDotOutput(); - this->collectStabs(); + this->collectDebugInfo(); this->writeOutput(); this->printStatistics(); @@ -645,7 +648,8 @@ void Linker::printStatistics() printTime(" build atom list", fStartLoadUndefinesTime - fStartBuildAtomsTime, totalTime); printTime(" load undefines", fStartResolveTime - fStartLoadUndefinesTime, totalTime); printTime(" resolve references", fStartSortTime - fStartResolveTime, totalTime); - printTime(" sort output", fStartWriteTime - fStartSortTime, totalTime); + printTime(" sort output", fStartDebugTime - fStartSortTime, totalTime); + printTime(" process debug info", fStartWriteTime - fStartDebugTime, totalTime); printTime(" write output", fEndTime - fStartWriteTime, totalTime); fprintf(stderr, "pageins=%u, pageouts=%u, faults=%u\n", endVMInfo.pageins-fStartVMInfo.pageins, endVMInfo.pageouts-fStartVMInfo.pageouts, endVMInfo.faults-fStartVMInfo.faults); @@ -873,7 +877,8 @@ void Linker::addJustInTimeAtoms(const char* name) // give indirect readers a chance for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { ObjectFile::Reader* reader = it->reader; - if ( reader != NULL ) { + // for two-level namespace, only search re-exported indirect libraries + if ( (reader != NULL) && ((it->reExportedViaDirectLibrary != NULL) || (fOptions.nameSpace() != Options::kTwoLevelNameSpace)) ) { std::vector* atoms = reader->getJustInTimeAtomsFor(name); if ( atoms != NULL ) { this->addAtoms(*atoms); @@ -1006,6 +1011,7 @@ void Linker::markLive(ObjectFile::Atom& atom, struct Linker::WhyLiveBackChain* p } else { // mark as undefined, for later error processing + fAtomsWithUnresolvedReferences.push_back(&atom); fGlobalSymbolTable.require(targetName); } } @@ -1089,6 +1095,39 @@ void Linker::deadStripResolve() markLive(**it, &rootChain); } + // it is possible that there are unresolved references that can be resolved now + // this can happen if the first reference to a common symbol in an archive. + // common symbols are not in the archive TOC, but the .o could have been pulled in later. + // ld64 while linking cc1 [ when dead_strip is ON] + for (std::vector::iterator it=fAtomsWithUnresolvedReferences.begin(); it != fAtomsWithUnresolvedReferences.end(); it++) { + std::vector& references = (*it)->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* reference = *rit; + if ( reference->isTargetUnbound() ) { + ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getTargetName()); + if ( target != NULL ) { + reference->setTarget(*target, reference->getTargetOffset()); + fLiveAtoms.insert(target); + // by just adding this atom to fLiveAtoms set, we are assuming it has no + // references, which is true for commons. + if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) + fprintf(stderr, "warning: ld64 internal error %s is not a tentative definition\n", target->getDisplayName()); + } + } + if ( reference->hasFromTarget() && reference->isFromTargetUnbound() ) { + ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getFromTargetName()); + if ( target != NULL ) { + reference->setFromTarget(*target); + fLiveAtoms.insert(target); + // by just adding this atom to fLiveAtoms set, we are assuming it has no + // references, which is true for commons. + if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) + fprintf(stderr, "warning: ld64 internal error %s is not a tentative definition\n", target->getDisplayName()); + } + } + } + } + // now remove all non-live atoms from fAllAtoms fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), NotLive(fLiveAtoms)), fAllAtoms.end()); } @@ -1105,7 +1144,6 @@ void Linker::sortAtoms() } - // make sure given addresses are within reach of branches, etc void Linker::tweakLayout() { @@ -1325,7 +1363,7 @@ typedef __gnu_cxx::hash_map, __gnu_cxx::hash< static PathToSums sKnownBINCLs; -void Linker::collectStabs(ObjectFile::Reader* reader, std::map& atomOrdinals) +void Linker::collectStabs(ObjectFile::Reader* reader, std::map& atomOrdinals) { bool log = false; bool minimal = ( fOptions.readerOptions().fDebugInfoStripping == ObjectFile::ReaderOptions::kDebugInfoMinimal ); @@ -1374,7 +1412,7 @@ void Linker::collectStabs(ObjectFile::Reader* reader, std::map::iterator pos = atomOrdinals.find(it->atom); + std::map::iterator pos = atomOrdinals.find(it->atom); if ( pos != atomOrdinals.end() ) { uint32_t ordinal = pos->second; if ( ordinal > highestOrdinal ) { @@ -1550,166 +1588,208 @@ void Linker::collectStabs(ObjectFile::Reader* reader, std::map& readersWithDwarfOrdinals) + : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals) {} + + bool operator()(const ObjectFile::Atom* atom) const { + if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) + return true; + if ( atom->getName() == NULL ) + return true; + if ( fReadersWithDwarfOrdinals.find(atom->getFile()) == fReadersWithDwarfOrdinals.end() ) + return true; + return false; + } + +private: + const std::map& fReadersWithDwarfOrdinals; +}; + +// used to sort atoms with debug notes +class ReadersWithDwarfSorter +{ +public: + ReadersWithDwarfSorter(const std::map& readersWithDwarfOrdinals, + const std::map& atomOrdinals) + : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals), fAtomOrdinals(atomOrdinals) {} + + bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) const + { + // first sort by reader + unsigned int leftReaderIndex = fReadersWithDwarfOrdinals.find(left->getFile())->second; + unsigned int rightReaderIndex = fReadersWithDwarfOrdinals.find(right->getFile())->second; + if ( leftReaderIndex != rightReaderIndex ) + return (leftReaderIndex < rightReaderIndex); + + // then sort by atom ordinal + unsigned int leftAtomIndex = fAtomOrdinals.find(left)->second; + unsigned int rightAtomIndex = fAtomOrdinals.find(right)->second; + return leftAtomIndex < rightAtomIndex; + } + +private: + const std::map& fReadersWithDwarfOrdinals; + const std::map& fAtomOrdinals; +}; + + + + + +void Linker::synthesizeDebugNotes(std::vector& allAtomsByReader) { // synthesize "debug notes" and add them to master stabs vector const char* dirPath = NULL; const char* filename = NULL; bool wroteStartSO = false; - std::vector seenFiles; - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); ++it) { + __gnu_cxx::hash_set, CStringEquals> seenFiles; + for (std::vector::iterator it=allAtomsByReader.begin(); it != allAtomsByReader.end(); it++) { ObjectFile::Atom* atom = *it; - if ( atom->getFile() == reader ) { - const char* name = atom->getName(); - if ( (name != NULL) && (atom->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) ) { - const char* newDirPath; - const char* newFilename; - if ( atom->getTranslationUnitSource(&newDirPath, &newFilename) ) { - // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/' - if ( (newDirPath != NULL) && (strlen(newDirPath) > 1 ) && (newDirPath[strlen(newDirPath)-1] != '/') ) - asprintf((char**)&newDirPath, "%s/", newDirPath); - // need SO's whenever the translation unit source file changes - if ( newFilename != filename ) { - if ( filename != NULL ) { - // translation unit change, emit ending SO - ObjectFile::Reader::Stab endFileStab; - endFileStab.atom = NULL; - endFileStab.type = N_SO; - endFileStab.other = 1; - endFileStab.desc = 0; - endFileStab.value = 0; - endFileStab.string = ""; - fStabs.push_back(endFileStab); - } - // new translation unit, emit start SO's - ObjectFile::Reader::Stab dirPathStab; - dirPathStab.atom = NULL; - dirPathStab.type = N_SO; - dirPathStab.other = 0; - dirPathStab.desc = 0; - dirPathStab.value = 0; - dirPathStab.string = newDirPath; - fStabs.push_back(dirPathStab); - ObjectFile::Reader::Stab fileStab; - fileStab.atom = NULL; - fileStab.type = N_SO; - fileStab.other = 0; - fileStab.desc = 0; - fileStab.value = 0; - fileStab.string = newFilename; - fStabs.push_back(fileStab); - // Synthesize OSO for start of file - ObjectFile::Reader::Stab objStab; - objStab.atom = NULL; - objStab.type = N_OSO; - objStab.other = 0; - objStab.desc = 1; - objStab.value = reader->getModificationTime(); - objStab.string = assureFullPath(reader->getPath()); - fStabs.push_back(objStab); - wroteStartSO = true; - } - filename = newFilename; - dirPath = newDirPath; - seenFiles.push_back(filename); - if ( atom->getSegment().isContentExecutable() && (strncmp(atom->getSectionName(), "__text", 6) == 0) ) { - // Synthesize BNSYM and start FUN stabs - ObjectFile::Reader::Stab beginSym; - beginSym.atom = atom; - beginSym.type = N_BNSYM; - beginSym.other = 1; - beginSym.desc = 0; - beginSym.value = 0; - beginSym.string = ""; - fStabs.push_back(beginSym); - ObjectFile::Reader::Stab startFun; - startFun.atom = atom; - startFun.type = N_FUN; - startFun.other = 1; - startFun.desc = 0; - startFun.value = 0; - startFun.string = name; - fStabs.push_back(startFun); - // Synthesize any SOL stabs needed - std::vector* lineInfo = atom->getLineInfo(); - if ( lineInfo != NULL ) { - // might be nice to set the source file path to seenFiles so it does not show up in SOLs - const char* curFile = NULL; - for (std::vector::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) { - if ( it->fileName != curFile ) { - bool alreadySeen = false; - for (std::vector::iterator sit = seenFiles.begin(); sit != seenFiles.end(); ++sit) { - if ( strcmp(it->fileName, *sit) == 0 ) { - alreadySeen = true; - break; - } - } - if ( ! alreadySeen ) { - seenFiles.push_back(it->fileName); - ObjectFile::Reader::Stab sol; - sol.atom = 0; - sol.type = N_SOL; - sol.other = 0; - sol.desc = 0; - sol.value = 0; - sol.string = it->fileName; - fStabs.push_back(sol); - } - curFile = it->fileName; - } + const char* newDirPath; + const char* newFilename; + //fprintf(stderr, "debug note for %s\n", atom->getDisplayName()); + if ( atom->getTranslationUnitSource(&newDirPath, &newFilename) ) { + // need SO's whenever the translation unit source file changes + if ( newFilename != filename ) { + // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/' + if ( (newDirPath != NULL) && (strlen(newDirPath) > 1 ) && (newDirPath[strlen(newDirPath)-1] != '/') ) + asprintf((char**)&newDirPath, "%s/", newDirPath); + if ( filename != NULL ) { + // translation unit change, emit ending SO + ObjectFile::Reader::Stab endFileStab; + endFileStab.atom = NULL; + endFileStab.type = N_SO; + endFileStab.other = 1; + endFileStab.desc = 0; + endFileStab.value = 0; + endFileStab.string = ""; + fStabs.push_back(endFileStab); + } + // new translation unit, emit start SO's + ObjectFile::Reader::Stab dirPathStab; + dirPathStab.atom = NULL; + dirPathStab.type = N_SO; + dirPathStab.other = 0; + dirPathStab.desc = 0; + dirPathStab.value = 0; + dirPathStab.string = newDirPath; + fStabs.push_back(dirPathStab); + ObjectFile::Reader::Stab fileStab; + fileStab.atom = NULL; + fileStab.type = N_SO; + fileStab.other = 0; + fileStab.desc = 0; + fileStab.value = 0; + fileStab.string = newFilename; + fStabs.push_back(fileStab); + // Synthesize OSO for start of file + ObjectFile::Reader::Stab objStab; + objStab.atom = NULL; + objStab.type = N_OSO; + objStab.other = 0; + objStab.desc = 1; + objStab.value = atom->getFile()->getModificationTime(); + objStab.string = assureFullPath(atom->getFile()->getPath()); + fStabs.push_back(objStab); + wroteStartSO = true; + // add the source file path to seenFiles so it does not show up in SOLs + seenFiles.insert(newFilename); + } + filename = newFilename; + dirPath = newDirPath; + if ( atom->getSegment().isContentExecutable() && (strncmp(atom->getSectionName(), "__text", 6) == 0) ) { + // Synthesize BNSYM and start FUN stabs + ObjectFile::Reader::Stab beginSym; + beginSym.atom = atom; + beginSym.type = N_BNSYM; + beginSym.other = 1; + beginSym.desc = 0; + beginSym.value = 0; + beginSym.string = ""; + fStabs.push_back(beginSym); + ObjectFile::Reader::Stab startFun; + startFun.atom = atom; + startFun.type = N_FUN; + startFun.other = 1; + startFun.desc = 0; + startFun.value = 0; + startFun.string = atom->getName(); + fStabs.push_back(startFun); + // Synthesize any SOL stabs needed + std::vector* lineInfo = atom->getLineInfo(); + if ( lineInfo != NULL ) { + const char* curFile = NULL; + for (std::vector::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) { + if ( it->fileName != curFile ) { + if ( seenFiles.count(it->fileName) == 0 ) { + seenFiles.insert(it->fileName); + ObjectFile::Reader::Stab sol; + sol.atom = 0; + sol.type = N_SOL; + sol.other = 0; + sol.desc = 0; + sol.value = 0; + sol.string = it->fileName; + fStabs.push_back(sol); } + curFile = it->fileName; } - // Synthesize end FUN and ENSYM stabs - ObjectFile::Reader::Stab endFun; - endFun.atom = atom; - endFun.type = N_FUN; - endFun.other = 0; - endFun.desc = 0; - endFun.value = 0; - endFun.string = ""; - fStabs.push_back(endFun); - ObjectFile::Reader::Stab endSym; - endSym.atom = atom; - endSym.type = N_ENSYM; - endSym.other = 1; - endSym.desc = 0; - endSym.value = 0; - endSym.string = ""; - fStabs.push_back(endSym); } - else { - ObjectFile::Reader::Stab globalsStab; - if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { - // Synthesize STSYM stab for statics - const char* name = atom->getName(); - if ( name[0] == '_' ) { - globalsStab.atom = atom; - globalsStab.type = N_STSYM; - globalsStab.other = 1; - globalsStab.desc = 0; - globalsStab.value = 0; - globalsStab.string = name; - fStabs.push_back(globalsStab); - } - } - else { - // Synthesize GSYM stab for other globals (but not .eh exception frame symbols) - const char* name = atom->getName(); - if ( (name[0] == '_') && (strcmp(atom->getSectionName(), "__eh_frame") != 0) ) { - globalsStab.atom = atom; - globalsStab.type = N_GSYM; - globalsStab.other = 1; - globalsStab.desc = 0; - globalsStab.value = 0; - globalsStab.string = name; - fStabs.push_back(globalsStab); - } - } + } + // Synthesize end FUN and ENSYM stabs + ObjectFile::Reader::Stab endFun; + endFun.atom = atom; + endFun.type = N_FUN; + endFun.other = 0; + endFun.desc = 0; + endFun.value = 0; + endFun.string = ""; + fStabs.push_back(endFun); + ObjectFile::Reader::Stab endSym; + endSym.atom = atom; + endSym.type = N_ENSYM; + endSym.other = 1; + endSym.desc = 0; + endSym.value = 0; + endSym.string = ""; + fStabs.push_back(endSym); + } + else { + ObjectFile::Reader::Stab globalsStab; + if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { + // Synthesize STSYM stab for statics + const char* name = atom->getName(); + if ( name[0] == '_' ) { + globalsStab.atom = atom; + globalsStab.type = N_STSYM; + globalsStab.other = 1; + globalsStab.desc = 0; + globalsStab.value = 0; + globalsStab.string = name; + fStabs.push_back(globalsStab); + } + } + else { + // Synthesize GSYM stab for other globals (but not .eh exception frame symbols) + const char* name = atom->getName(); + if ( (name[0] == '_') && (strcmp(atom->getSectionName(), "__eh_frame") != 0) ) { + globalsStab.atom = atom; + globalsStab.type = N_GSYM; + globalsStab.other = 1; + globalsStab.desc = 0; + globalsStab.value = 0; + globalsStab.string = name; + fStabs.push_back(globalsStab); } } } } } + if ( wroteStartSO ) { // emit ending SO ObjectFile::Reader::Stab endFileStab; @@ -1723,19 +1803,18 @@ void Linker::synthesizeStabs(ObjectFile::Reader* reader) } } -void Linker::collectStabs() + + + +void Linker::collectDebugInfo() { + std::map atomOrdinals; + fStartDebugTime = mach_absolute_time(); if ( fOptions.readerOptions().fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone ) { - // make mapping from atoms to ordinal - std::map atomOrdinals; - uint32_t ordinal = 1; - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - atomOrdinals[*it] = ordinal++; - } - - fStabs.reserve(1024); // try to minimize re-allocations - // get stabs from each reader, in command line order + // determine mixture of stabs and dwarf + bool someStabs = false; + bool someDwarf = false; for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); it != fReadersThatHaveSuppliedAtoms.end(); it++) { @@ -1743,17 +1822,16 @@ void Linker::collectStabs() if ( reader != NULL ) { switch ( reader->getDebugInfoKind() ) { case ObjectFile::Reader::kDebugInfoNone: - // do nothing break; case ObjectFile::Reader::kDebugInfoStabs: - collectStabs(reader, atomOrdinals); + someStabs = true; break; case ObjectFile::Reader::kDebugInfoDwarf: - synthesizeStabs(reader); + someDwarf = true; fCreateUUID = true; break; case ObjectFile::Reader::kDebugInfoStabsUUID: - collectStabs(reader, atomOrdinals); + someStabs = true; fCreateUUID = true; break; default: @@ -1761,10 +1839,70 @@ void Linker::collectStabs() } } } - // remove stabs associated with atoms that won't be in output - std::set allAtomsSet; - allAtomsSet.insert(fAllAtoms.begin(), fAllAtoms.end()); - fStabs.erase(std::remove_if(fStabs.begin(), fStabs.end(), NotInSet(allAtomsSet)), fStabs.end()); + + if ( someDwarf || someStabs ) { + // try to minimize re-allocations + fStabs.reserve(1024); + + // make mapping from atoms to ordinal + uint32_t ordinal = 1; + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + atomOrdinals[*it] = ordinal++; + } + } + + // process all dwarf .o files as a batch + if ( someDwarf ) { + // make mapping from readers with dwarf to ordinal + std::map readersWithDwarfOrdinals; + uint32_t readerOrdinal = 1; + for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); + it != fReadersThatHaveSuppliedAtoms.end(); + it++) { + ObjectFile::Reader* reader = *it; + if ( (reader != NULL) && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoDwarf) ) { + readersWithDwarfOrdinals[reader] = readerOrdinal++; + } + } + + // make a vector of atoms + std::vector allAtomsByReader(fAllAtoms.begin(), fAllAtoms.end()); + // remove those not from a reader that has dwarf + allAtomsByReader.erase(std::remove_if(allAtomsByReader.begin(), allAtomsByReader.end(), + NoDebugNoteAtom(readersWithDwarfOrdinals)), allAtomsByReader.end()); + // sort by reader then atom ordinal + std::sort(allAtomsByReader.begin(), allAtomsByReader.end(), ReadersWithDwarfSorter(readersWithDwarfOrdinals, atomOrdinals)); + // add debug notes for each atom + this->synthesizeDebugNotes(allAtomsByReader); + } + + // process all stabs .o files one by one + if ( someStabs ) { + // get stabs from each reader, in command line order + for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); + it != fReadersThatHaveSuppliedAtoms.end(); + it++) { + ObjectFile::Reader* reader = *it; + if ( reader != NULL ) { + switch ( reader->getDebugInfoKind() ) { + case ObjectFile::Reader::kDebugInfoDwarf: + case ObjectFile::Reader::kDebugInfoNone: + // do nothing + break; + case ObjectFile::Reader::kDebugInfoStabs: + case ObjectFile::Reader::kDebugInfoStabsUUID: + collectStabs(reader, atomOrdinals); + break; + default: + throw "Unhandled type of debug information"; + } + } + } + // remove stabs associated with atoms that won't be in output + std::set allAtomsSet; + allAtomsSet.insert(fAllAtoms.begin(), fAllAtoms.end()); + fStabs.erase(std::remove_if(fStabs.begin(), fStabs.end(), NotInSet(allAtomsSet)), fStabs.end()); + } } } @@ -1799,7 +1937,7 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) uint32_t fileOffset = OSSwapBigToHostInt32(archs[i].offset); len = OSSwapBigToHostInt32(archs[i].size); // if requested architecture is page aligned within fat file, then remap just that portion of file - if ( (fileOffset && 0x00000FFF) == 0 ) { + if ( (fileOffset & 0x00000FFF) == 0 ) { // unmap whole file munmap((caddr_t)p, info.fileLen); // re-map just part we need @@ -2300,7 +2438,7 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) case kWeakAndWeak: // have another weak atom, use whichever has largest alignment requirement // because codegen of some client may require alignment - useNew = ( newAtom.getAlignment() > existingAtom->getAlignment() ); + useNew = ( newAtom.getAlignment().leadingZeros() > existingAtom->getAlignment().leadingZeros() ); break; case kWeakAndTent: // replace existing weak atom with tentative one ??? @@ -2323,6 +2461,9 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) // use largest if ( newAtom.getSize() < existingAtom->getSize() ) { useNew = false; + } else { + if ( newAtom.getAlignment().leadingZeros() < existingAtom->getAlignment().leadingZeros() ) + fprintf(stderr, "ld64 warning: alignment lost in merging tentative definition %s\n", newAtom.getDisplayName()); } break; case kTentAndExtern: diff --git a/src/machochecker.cpp b/src/machochecker.cpp index 118aa49..1d7aec5 100644 --- a/src/machochecker.cpp +++ b/src/machochecker.cpp @@ -336,7 +336,7 @@ void MachOChecker::checkLoadCommands() throwf("section %s vm address not within segment", sect->sectname()); if ( (sect->addr()+sect->size()) > endAddr ) throwf("section %s vm address not within segment", sect->sectname()); - if ( (sect->flags() &SECTION_TYPE) != S_ZEROFILL ) { + if ( ((sect->flags() &SECTION_TYPE) != S_ZEROFILL) && (segCmd->filesize() != 0) ) { if ( sect->offset() < startOffset ) throwf("section %s file offset not within segment", sect->sectname()); if ( (sect->offset()+sect->size()) > endOffset ) @@ -402,10 +402,12 @@ void MachOChecker::checkLoadCommands() const macho_dysymtab_command

* dsymtab = (struct macho_dysymtab_command

*)cmd; fIndirectTable = (uint32_t*)((char*)fHeader + dsymtab->indirectsymoff()); fIndirectTableCount = dsymtab->nindirectsyms(); - if ( dsymtab->indirectsymoff() < linkEditSegment->fileoff() ) - throw "indirect symbol table not in __LINKEDIT"; - if ( (dsymtab->indirectsymoff()+fIndirectTableCount*8) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) - throw "indirect symbol table not in __LINKEDIT"; + if ( fIndirectTableCount != 0 ) { + if ( dsymtab->indirectsymoff() < linkEditSegment->fileoff() ) + throw "indirect symbol table not in __LINKEDIT"; + if ( (dsymtab->indirectsymoff()+fIndirectTableCount*8) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "indirect symbol table not in __LINKEDIT"; + } fLocalRelocationsCount = dsymtab->nlocrel(); if ( fLocalRelocationsCount != 0 ) { fLocalRelocations = (const macho_relocation_info

*)((char*)fHeader + dsymtab->locreloff()); diff --git a/src/rebase.cpp b/src/rebase.cpp index ab62f9b..e47ce34 100644 --- a/src/rebase.cpp +++ b/src/rebase.cpp @@ -406,6 +406,8 @@ void Rebaser::adjustSymbolTable() if ( entry->n_sect() != NO_SECT ) entry->set_n_value(entry->n_value() + fSlide); } + + // FIXME ¥¥¥ adjust dylib_module if it exists } template diff --git a/unit-tests/test-cases/align-modulus/Makefile b/unit-tests/test-cases/align-modulus/Makefile new file mode 100644 index 0000000..bf79c29 --- /dev/null +++ b/unit-tests/test-cases/align-modulus/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify that the modules of +# symbol _b is maintained. The address for _b must be +# 3 mod 16. Therefore the last hexdigit of the address +# must be 3. + +run: all + +all: + ${CC} ${ASMFLAGS} -dynamiclib -single_module -dead_strip foo.c align.s -exported_symbols_list foo.exp -o foo.${ARCH}.dylib + nm foo.${ARCH}.dylib | grep "3 d _b" | ${PASS_IFF_STDIN} + + +clean: + rm -rf foo.${ARCH}.dylib + + + diff --git a/unit-tests/test-cases/align-modulus/align.s b/unit-tests/test-cases/align-modulus/align.s new file mode 100644 index 0000000..a288960 --- /dev/null +++ b/unit-tests/test-cases/align-modulus/align.s @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + .data + .align 0 +_a: .byte 3 + .byte 3 + .byte 3 + .globl _b +_b: .byte 4 ;# address is 3 + .align 4 +L1: .quad 0 +_c: .long 8 + + .subsections_via_symbols + diff --git a/unit-tests/test-cases/align-modulus/foo.c b/unit-tests/test-cases/align-modulus/foo.c new file mode 100644 index 0000000..0b88dff --- /dev/null +++ b/unit-tests/test-cases/align-modulus/foo.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern char b; +int my = 2; + +char foo() +{ + return my+b; +} + + diff --git a/unit-tests/test-cases/align-modulus/foo.exp b/unit-tests/test-cases/align-modulus/foo.exp new file mode 100644 index 0000000..70eefe9 --- /dev/null +++ b/unit-tests/test-cases/align-modulus/foo.exp @@ -0,0 +1 @@ +_foo diff --git a/unit-tests/test-cases/dead_strip-archive/Makefile b/unit-tests/test-cases/dead_strip-archive/Makefile new file mode 100644 index 0000000..c2e66c0 --- /dev/null +++ b/unit-tests/test-cases/dead_strip-archive/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test that -dead_strip removes unreference code/data from archives +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + libtool -static foo.o -o libfoo.a + ${CC} ${CCFLAGS} main.c -mdynamic-no-pic -Os libfoo.a -dead_strip -o main + ${FAIL_IF_BAD_MACHO} main + nm -j main | grep dead_wood | ${PASS_IFF_EMPTY} + + +clean: + rm -rf main libfoo.a foo.o + + diff --git a/unit-tests/test-cases/dead_strip-archive/foo.c b/unit-tests/test-cases/dead_strip-archive/foo.c new file mode 100644 index 0000000..be1b438 --- /dev/null +++ b/unit-tests/test-cases/dead_strip-archive/foo.c @@ -0,0 +1,7 @@ + + +void foo() {} + + +int my_common; + diff --git a/unit-tests/test-cases/dead_strip-archive/main.c b/unit-tests/test-cases/dead_strip-archive/main.c new file mode 100644 index 0000000..f6a7d5a --- /dev/null +++ b/unit-tests/test-cases/dead_strip-archive/main.c @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern void foo(); +extern int my_common; + +int main() +{ + // the reference to the common symbol has to be first + my_common += 1; + // refrence to foo is next + foo(); + return 0; +} + + diff --git a/unit-tests/test-cases/dwarf-debug-notes-r/Makefile b/unit-tests/test-cases/dwarf-debug-notes-r/Makefile index b7f4eeb..8159cd9 100644 --- a/unit-tests/test-cases/dwarf-debug-notes-r/Makefile +++ b/unit-tests/test-cases/dwarf-debug-notes-r/Makefile @@ -40,7 +40,7 @@ all: foobar.o main.o crt1.o ${PASS_IFF} diff dwarf-test-${ARCH}.stabs expected-stabs foobar.o : foo.o bar.o - ${LD} -r foo.o bar.o -o foobar.o + ${LD} -r -arch ${ARCH} foo.o bar.o -o foobar.o foo.o : foo.cxx ${CXX} ${CCXXFLAGS} -gdwarf-2 foo.cxx -c -o foo.o -mdynamic-no-pic diff --git a/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs b/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs index 4853628..f8abc12 100644 --- a/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs +++ b/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs @@ -1,4 +1,12 @@ 0000 SO CWD/ +0000 SO main.cxx +0001 OSO CWD/main.o +0000 BNSYM +0000 FUN _main +0000 FUN +0000 ENSYM +0000 SO +0000 SO CWD/ 0000 SO foo.cxx 0001 OSO CWD/foo.o 0000 BNSYM @@ -14,11 +22,3 @@ 0000 FUN 0000 ENSYM 0000 SO -0000 SO CWD/ -0000 SO main.cxx -0001 OSO CWD/main.o -0000 BNSYM -0000 FUN _main -0000 FUN -0000 ENSYM -0000 SO diff --git a/unit-tests/test-cases/indirect-dylib/Makefile b/unit-tests/test-cases/indirect-dylib/Makefile new file mode 100644 index 0000000..32cb276 --- /dev/null +++ b/unit-tests/test-cases/indirect-dylib/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# The point of this test is a sanity check that an indirect +# library is not accidentally searched for symbols. +# +# wrong error message when symbol is found in unused indirect library# +# + +run: all + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib + ${CC} ${CCFLAGS} foo.c libbar.dylib -dynamiclib -o libfoo.dylib + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -o main libfoo.dylib 2> fail.log + grep ordinal fail.log | ${PASS_IFF_EMPTY} + +clean: + rm libfoo.dylib libbar.dylib main fail.log + + diff --git a/unit-tests/test-cases/indirect-dylib/bar.c b/unit-tests/test-cases/indirect-dylib/bar.c new file mode 100644 index 0000000..f39ee21 --- /dev/null +++ b/unit-tests/test-cases/indirect-dylib/bar.c @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// function called by a loaded bundle +int bar() +{ + return 1; +} + diff --git a/unit-tests/test-cases/indirect-dylib/foo.c b/unit-tests/test-cases/indirect-dylib/foo.c new file mode 100644 index 0000000..ba78c28 --- /dev/null +++ b/unit-tests/test-cases/indirect-dylib/foo.c @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int bar(); + +int foo() +{ + return bar(); +} diff --git a/unit-tests/test-cases/indirect-dylib/main.c b/unit-tests/test-cases/indirect-dylib/main.c new file mode 100644 index 0000000..13f57d7 --- /dev/null +++ b/unit-tests/test-cases/indirect-dylib/main.c @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern void bar(); + +int main() +{ + bar(); + return 0; +} + diff --git a/unit-tests/test-cases/relocs-asm/relocs-asm.s b/unit-tests/test-cases/relocs-asm/relocs-asm.s index 0629125..afacc62 100644 --- a/unit-tests/test-cases/relocs-asm/relocs-asm.s +++ b/unit-tests/test-cases/relocs-asm/relocs-asm.s @@ -220,13 +220,17 @@ _test_loads: # 1-byte store movb $0x12, _a(%rip) + movb $0x12, _a+2(%rip) + movb $0x12, L0(%rip) # 4-byte store movl $0x12345678, _a(%rip) + movl $0x12345678, _a+4(%rip) + movl $0x12345678, L0(%rip) # test local labels -# lea L1(%rip), %rax ### assembler bug -# movl L0(%rip), %eax ### assembler bug + lea L1(%rip), %rax + movl L0(%rip), %eax ret @@ -281,9 +285,9 @@ _prev: L1: .quad _test_branches - _test_diffs .quad _test_branches - _test_diffs + 4 .long _test_branches - _test_diffs -# .long LCL0-. ### assembler bug: content value should be (address(LCL0) - 0x24) +# .long LCL0-. ### assembler bug: should SUB/UNSIGNED with content= LCL0-24, or single pc-rel SIGNED reloc with content = LCL0-.+4 .quad L1 -# .quad L0 ### assembler bug: should be internal reloc to L0 + .quad L0 .quad _test_branches - . .quad _test_branches - L1 .quad L1 - _prev diff --git a/unit-tests/test-cases/relocs-objc/test.m b/unit-tests/test-cases/relocs-objc/test.m index a691e4a..1ca2157 100644 --- a/unit-tests/test-cases/relocs-objc/test.m +++ b/unit-tests/test-cases/relocs-objc/test.m @@ -34,7 +34,6 @@ @implementation Foo - - (id) init { self = [super init]; @@ -45,6 +44,16 @@ { [self class]; } +@end + + +@interface Base @end + +@implementation Base +@end + + + -- 2.45.2