]> git.saurik.com Git - apple/ld64.git/commitdiff
ld64-274.1.tar.gz developer-tools-80 developer-tools-81 v274.1
authorApple <opensource@apple.com>
Fri, 9 Dec 2016 18:04:01 +0000 (18:04 +0000)
committerApple <opensource@apple.com>
Fri, 9 Dec 2016 18:04:01 +0000 (18:04 +0000)
95 files changed:
.gitignore [new file with mode: 0644]
doc/man/man1/ld.1
ld64.xcodeproj/project.pbxproj
src/abstraction/MachOFileAbstraction.hpp
src/create_configure
src/ld/Architectures.hpp
src/ld/HeaderAndLoadCommands.hpp
src/ld/InputFiles.cpp
src/ld/InputFiles.h
src/ld/LinkEditClassic.hpp
src/ld/Options.cpp
src/ld/Options.h
src/ld/OutputFile.cpp
src/ld/OutputFile.h
src/ld/Resolver.cpp
src/ld/Resolver.h
src/ld/ld.cpp
src/ld/ld.hpp
src/ld/parsers/archive_file.cpp
src/ld/parsers/generic_dylib_file.hpp
src/ld/parsers/lto_file.cpp
src/ld/parsers/lto_file.h
src/ld/parsers/macho_dylib_file.cpp
src/ld/parsers/macho_relocatable_file.cpp
src/ld/parsers/textstub_dylib_file.cpp
src/ld/passes/bitcode_bundle.cpp
src/ld/passes/compact_unwind.cpp
src/ld/passes/dylibs.cpp
src/ld/passes/huge.cpp
src/ld/passes/objc.cpp
src/ld/passes/stubs/stub_arm64.hpp
src/ld/passes/stubs/stubs.cpp
src/other/ObjectDump.cpp
src/other/dyldinfo.cpp
src/other/machochecker.cpp
src/other/objcimageinfo.cpp [new file with mode: 0644]
src/other/unwinddump.cpp
unit-tests/include/common.makefile
unit-tests/test-cases/alias-basic/Makefile
unit-tests/test-cases/alias-objects/Makefile
unit-tests/test-cases/archive-image_info/Makefile
unit-tests/test-cases/cfstring-coalesce/Makefile
unit-tests/test-cases/cfstring-utf16/Makefile
unit-tests/test-cases/cstring-alt-segment/Makefile
unit-tests/test-cases/cstring-custom-section/Makefile
unit-tests/test-cases/dead_strip/Makefile
unit-tests/test-cases/dirty-data-alt-entry/1.dirty [new file with mode: 0644]
unit-tests/test-cases/dirty-data-alt-entry/2.dirty [new file with mode: 0644]
unit-tests/test-cases/dirty-data-alt-entry/Makefile [new file with mode: 0644]
unit-tests/test-cases/dirty-data-alt-entry/test.s [new file with mode: 0644]
unit-tests/test-cases/dwarf-ignore/Makefile
unit-tests/test-cases/dwarf-strip-objc/Makefile
unit-tests/test-cases/eh-coalescing/Makefile
unit-tests/test-cases/lto-archive-objc/Makefile [new file with mode: 0644]
unit-tests/test-cases/lto-archive-objc/bar.m [new file with mode: 0644]
unit-tests/test-cases/lto-archive-objc/baz.m [new file with mode: 0644]
unit-tests/test-cases/lto-archive-objc/foo.c [new file with mode: 0644]
unit-tests/test-cases/lto-archive-objc/main.c [new file with mode: 0644]
unit-tests/test-cases/lto-objc-image-info/Makefile
unit-tests/test-cases/lto-rename_section/Makefile
unit-tests/test-cases/lto-rename_segment/Makefile
unit-tests/test-cases/merge_zero_fill_sections/Makefile
unit-tests/test-cases/no_zero_fill_sections/Makefile
unit-tests/test-cases/objc-abi/Makefile
unit-tests/test-cases/objc-category-class-property-mismatch/Makefile [new file with mode: 0644]
unit-tests/test-cases/objc-category-class-property-mismatch/cat.m [new file with mode: 0644]
unit-tests/test-cases/objc-category-class-property-mismatch/class.m [new file with mode: 0644]
unit-tests/test-cases/objc-category-optimize-load/Makefile
unit-tests/test-cases/objc-category-optimize/Makefile
unit-tests/test-cases/objc-category-optimize/cat1.m
unit-tests/test-cases/objc-category-warning/Makefile
unit-tests/test-cases/objc-class-alias/Makefile
unit-tests/test-cases/objc-gc-checks/Makefile
unit-tests/test-cases/objc-literal-pointers-strip/Makefile
unit-tests/test-cases/re-export-relative-paths/Makefile
unit-tests/test-cases/re-export-relative-paths/baz.c [new file with mode: 0644]
unit-tests/test-cases/sectcreate-dead_strip/Makefile
unit-tests/test-cases/slow-x86-stubs/Makefile
unit-tests/test-cases/stripped-indirect-symbol-table/Makefile
unit-tests/test-cases/symbol-move-and-section-rename/Makefile [new file with mode: 0644]
unit-tests/test-cases/symbol-move-and-section-rename/foo.list [new file with mode: 0644]
unit-tests/test-cases/symbol-move-and-section-rename/foo1.c [new file with mode: 0644]
unit-tests/test-cases/symbol-move-and-section-rename/foo2.c [new file with mode: 0644]
unit-tests/test-cases/symbol-move-and-section-rename/hot.list [new file with mode: 0644]
unit-tests/test-cases/symbol-move-and-section-rename/main.c [new file with mode: 0644]
unit-tests/test-cases/symbol-move-and-section-rename/spec.list [new file with mode: 0644]
unit-tests/test-cases/symbol-section-move/Makefile
unit-tests/test-cases/tlv-dylib/foo.c
unit-tests/test-cases/tlv-dylib/main.c
unit-tests/test-cases/utf16-nul/Makefile
unit-tests/test-cases/weak_import-disable/Makefile [new file with mode: 0644]
unit-tests/test-cases/weak_import-disable/foo.c [new file with mode: 0644]
unit-tests/test-cases/weak_import-disable/main.c [new file with mode: 0644]
unit-tests/test-cases/weak_import-disable/main2.c [new file with mode: 0644]
unit-tests/test-cases/zero-fill3/Makefile

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..9190838
--- /dev/null
@@ -0,0 +1,2 @@
+build
+DerivedData
index 53fbaa5e777ac142545649319ed6d55149e7f811..30692dcae466bd1587cf77880f20fbaf54a4bafa 100644 (file)
@@ -6,14 +6,14 @@
 .Nd "linker"
 .Sh SYNOPSIS
 .Nm
-files... 
-.Op options 
-.Op Fl o Ar outputfile 
+files...
+.Op options
+.Op Fl o Ar outputfile
 .Sh DESCRIPTION
 The
 .Nm ld
 command combines several object files and libraries, resolves references, and
-produces an ouput file. 
+produces an ouput file.
 .Nm ld
 can produce a final linked image (executable, dylib, or bundle), or with the -r
 option, produce another object file.  If the -o option is not used, the output
@@ -21,7 +21,7 @@ file produced is named "a.out".
 .Ss Universal
 The linker accepts universal (multiple-architecture) input files, but
 always creates a "thin" (single-architecture), standard Mach-O output file.
-The architecture for the output file is specified using the -arch option.  
+The architecture for the output file is specified using the -arch option.
 If this option is not used,
 .Nm ld
 attempts to determine the output architecture by examining the object
@@ -29,76 +29,76 @@ files in command line order.  The first "thin"
 architecture determines that of the output file.  If no input
 object file is a "thin" file, the native 32-bit architecture for the host is used.
 .Pp
-Usually, 
+Usually,
 .Nm ld
-is not used directly.  Instead the 
+is not used directly.  Instead the
 .Xr gcc(1)
 compiler driver invokes
 .Nm ld.
-The compiler driver can be passed multiple -arch options and it will create a 
-universal final linked image by invoking 
+The compiler driver can be passed multiple -arch options and it will create a
+universal final linked image by invoking
 .Nm ld
 multiple times and then running
 .Xr lipo(1)
 merge the outputs into a universal file.
 .Ss Layout
 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. 
+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.
-Sections created from files with the -sectcreate option will be laid out at after 
-sections from .o files. The use of the -order_file option will alter the layout 
+Sections created from files with the -sectcreate option will be laid out at after
+sections from .o files. The use of the -order_file option will alter the layout
 rules above, and move the symbols specified to start of their section.
 .Ss Libraries
 A static library (aka static archive) is a collection of .o files with a table of contents
-that lists the global symbols in the .o files. 
+that lists the global symbols in the .o files.
 .Nm ld
 will only pull .o files out of a static library if needed to resolve some symbol reference.
-Unlike traditional linkers, 
+Unlike traditional linkers,
 .Nm ld
 will continually search a static library while linking. There is no need to specify a static
-library multiple times on the command line.  
+library multiple times on the command line.
 .Pp
 A dynamic library (aka dylib or framework) is a final linked image.  Putting a dynamic
-library on the command line causes two things: 1) The generated final linked image 
+library on the command line causes two things: 1) The generated final linked image
 will have encoded that it depends on that dynamic library. 2) Exported symbols from the
-dynamic library are used to resolve references.  
+dynamic library are used to resolve references.
 .Pp
-Both dynamic and static libraries are searched as they appear on the command line.  
+Both dynamic and static libraries are searched as they appear on the command line.
 .Ss Search paths
 .Nm ld
 maintains a list of directories to search for a library or framework to use.  The default
 library search path is /usr/lib then /usr/local/lib.  The -L option will add a new library search
 path.  The default framework search path is /Library/Frameworks then /System/Library/Frameworks.
 (Note: previously, /Network/Library/Frameworks was at the end of the default path.  If you need
-that functionality, you need to explicitly add -F/Network/Library/Frameworks). 
+that functionality, you need to explicitly add -F/Network/Library/Frameworks).
 The -F option will add a new framework search path.  The -Z option will remove
 the standard search paths.  The -syslibroot option will prepend a prefix to all search
 paths.
 .Ss Two-level namespace
-By default all references resolved to a dynamic library record the library to which 
+By default all references resolved to a dynamic library record the library to which
 they were resolved. At runtime, dyld uses that information to directly resolve
 symbols.  The alternative is to use the -flat_namespace option.  With flat namespace,
 the library is not recorded.  At runtime, dyld will search each dynamic library in load
-order when resolving symbols. This is slower, but more like how other operating systems 
-resolve symbols. 
+order when resolving symbols. This is slower, but more like how other operating systems
+resolve symbols.
 .Ss Indirect dynamic libraries
 If the command line specifies to link against dylib A, and when dylib A was built it linked
-against dylib B, then B is considered an indirect dylib.  
-When linking for two-level namespace, ld does not look at indirect dylibs, except when 
-re-exported by a direct dylibs.  On the other hand when linking for flat namespace, 
+against dylib B, then B is considered an indirect dylib.
+When linking for two-level namespace, ld does not look at indirect dylibs, except when
+re-exported by a direct dylibs.  On the other hand when linking for flat namespace,
 ld does load all indirect dylibs and uses them to resolve references.
-Even though indirect dylibs are specified via a full path, 
+Even though indirect dylibs are specified via a full path,
 .Nm ld
 first uses the specified search paths to locate each indirect dylib.  If one cannot
 be found using the search paths, the full path is used.
 .Ss Dynamic libraries undefines
-When linking for two-level namespace, 
-.Nm ld 
+When linking for two-level namespace,
+.Nm ld
 does not verify that undefines in dylibs actually
-exist.  But when linking for flat namespace, 
+exist.  But when linking for flat namespace,
 .Nm ld
-does check that all undefines from all loaded dylibs have a matching definition.  
+does check that all undefines from all loaded dylibs have a matching definition.
 This is sometimes used to force selected functions to be loaded from a static library.
 .Sh OPTIONS
 .Ss Options that control the kind of output
@@ -110,13 +110,13 @@ Produce a mach-o shared library that has file type MH_DYLIB.
 .It Fl bundle
 Produce a mach-o bundle that has file type MH_BUNDLE.
 .It Fl r
-Merges object files to produce another mach-o object file with file type MH_OBJECT.  
+Merges object files to produce another mach-o object file with file type MH_OBJECT.
 .It Fl dylinker
 Produce a mach-o dylinker that has file type MH_DYLINKER.  Only used when building dyld.
 .It Fl dynamic
 The default.  Implied by -dylib, -bundle, or -execute
 .It Fl static
-Produces a mach-o file that does not use the dyld.  Only used building the kernel. 
+Produces a mach-o file that does not use the dyld.  Only used building the kernel.
 .It Fl preload
 Produces a mach-o file in which the mach_header, load commands, and symbol table are
 not in any segment.  This output type is used for firmware or embedded development
@@ -137,44 +137,44 @@ This is the same as the -lx but forces the library and all references to it to b
 That is, the library is allowed to be missing at runtime.
 .It Fl weak_library Ar 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. 
+library and all references to it to be marked as weak imports.
 .It Fl reexport-l Ns Ar x
-This is the same as the -lx but specifies that the all symbols in library x should be available to 
+This is the same as the -lx but specifies that the all symbols in library x should be available to
 clients linking to the library being created.  This was previously done with a separate -sub_library option.
 .It Fl reexport_library Ar path_to_library
-This is the same as listing a file name path to a library on the link line and it specifies that the 
+This is the same as listing a file name path to a library on the link line and it specifies that the
 all symbols in library path should be available to clients linking to the library being created.
 This was previously done with a separate -sub_library option.
 .It Fl lazy-l Ns Ar x
 This is the same as the -lx but it is only for shared libraries and the linker
-will construct glue code so that the shared library is not loaded until 
+will construct glue code so that the shared library is not loaded until
 the first function in it is called.
 .It Fl lazy_library Ar path_to_library
 This is the same as listing a file name path to a shared library on the link line
-except that the linker will construct glue code so that the shared library is not 
+except that the linker will construct glue code so that the shared library is not
 loaded until the first function in it is called.
 .It Fl upward-l Ns Ar x
-This is the same as the -lx but specifies that the dylib is an upward dependency. 
+This is the same as the -lx but specifies that the dylib is an upward dependency.
 .It Fl upward_library Ar path_to_library
-This is the same as listing a file name path to a library on the link line but also marks 
+This is the same as listing a file name path to a library on the link line but also marks
 the dylib as an upward dependency.
 .It Fl L Ns dir
-Add 
-.Ar dir 
-to the list of directories in which to search for libraries. 
+Add
+.Ar dir
+to the list of directories in which to search for libraries.
 Directories specified with -L are searched in the order they appear on the command line
 and before the default search path. In Xcode4 and later, there can be a space between
 the -L and directory.
 .It Fl Z
 Do not search the standard directories when searching for libraries and frameworks.
 .It Fl syslibroot Ar rootdir
-Prepend 
-.Ar rootdir 
+Prepend
+.Ar rootdir
 to all search paths when searching for libraries or frameworks.
 .It Fl search_paths_first
-This is now the default (in Xcode4 tools).  When processing -lx the linker now searches each directory 
-in its library search paths for `libx.dylib' then `libx.a' before the moving on to the next path 
-in the library search path.  
+This is now the default (in Xcode4 tools).  When processing -lx the linker now searches each directory
+in its library search paths for `libx.dylib' then `libx.a' before the moving on to the next path
+in the library search path.
 .It Fl search_dylibs_first
 Changes the searching behavior for libraries.  The default is that when processing -lx the linker
 searches each directory in its library search paths for `libx.dylib' then `libx.a'.
@@ -186,24 +186,24 @@ This option tells the linker to search for `name.framework/name' the framework s
 If the optional suffix is specified the framework is first searched for the name with the suffix and then without
 (e.g. look for `name.framework/name_suffix' first, if not there try `name.framework/name').
 .It Fl weak_framework Ar name[,suffix]
-This is the same as the -framework name[,suffix] but forces the framework and all 
-references to it to be marked as weak imports. 
+This is the same as the -framework name[,suffix] but forces the framework and all
+references to it to be marked as weak imports.
 .It Fl reexport_framework Ar name[,suffix]
-This is the same as the -framework name[,suffix] but also specifies that the 
+This is the same as the -framework name[,suffix] but also specifies that the
 all symbols in that framework should be available to clients linking to the library being created.
 This was previously done with a separate -sub_umbrella option.
 .It Fl lazy_framework Ar name[,suffix]
-This is the same as the -framework name[,suffix] except that the linker will 
-construct glue code so that the framework is not 
+This is the same as the -framework name[,suffix] except that the linker will
+construct glue code so that the framework is not
 loaded until the first function in it is called.  You cannot directly access
 data or Objective-C classes in a framework linked this way.
 .It Fl upward_framework Ar name[,suffix]
-This is the same as the -framework name[,suffix] but also specifies that the 
-framework is an upward dependency.  
+This is the same as the -framework name[,suffix] but also specifies that the
+framework is an upward dependency.
 .It Fl F Ns dir
-Add 
+Add
 .Ar dir
-to the list of directories in which to search for frameworks. 
+to the list of directories in which to search for frameworks.
 Directories specified with -F are searched in the order they appear on the command line
 and before the default search path. In Xcode4 and later, there can be a space between
 the -F and directory.
@@ -218,42 +218,42 @@ archives to be loaded.  This option allows you to target a specific archive.
 .Ss Options that control additional content
 .Bl -tag
 .It Fl sectcreate Ar segname sectname file
-The section 
-.Ar sectname 
-in the segment 
-.Ar segname 
-is created from the contents of file 
-.Ar file. 
-The combination of segname and sectname must be unique Ð there cannot already be a section (segname,sectname) 
+The section
+.Ar sectname
+in the segment
+.Ar segname
+is created from the contents of file
+.Ar file.
+The combination of segname and sectname must be unique Ð there cannot already be a section (segname,sectname)
 from any other input.
 .It Fl filelist Ar file[,dirname]
-Specifies that the linker should link the files listed in 
-.Ar file . 
-This is an alternative to listing the files on the command line. 
+Specifies that the linker should link the files listed in
+.Ar file .
+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, 
-.Ar dirname 
+If the optional directory name,
+.Ar dirname
 is specified, it is prepended to each name in the list file.
 .It Fl dtrace Ar file
-Enables dtrace static probes when producing a final linked image.  The file 
+Enables dtrace static probes when producing a final linked image.  The file
 .Ar file
 must be a DTrace script which declares the static probes.
 .El
-.Ss Options that control optimizations 
+.Ss Options that control optimizations
 .Bl -tag
 .It Fl dead_strip
-Remove functions and data that are unreachable by the entry point or exported symbols.  
+Remove functions and data that are unreachable by the entry point or exported symbols.
 .It Fl order_file Ar file
 Alters the order in which functions and data are laid out.  For each section in the output file,
-any symbol in that section that are specified in the order file 
+any symbol in that section that are specified in the order file
 .Ar file
-is moved to the start of its section and laid out in the same order as in the order file 
+is moved to the start of its section and laid out in the same order as in the order file
 .Ar file .
 Order files are text files with one symbol name per line.  Lines starting with a # are comments.
-A symbol name may be optionally preceded with its object file leaf name and a colon (e.g. foo.o:_foo). 
-This is useful for static functions/data that occur in multiple files. 
+A symbol name may be optionally preceded with its object file leaf name and a colon (e.g. foo.o:_foo).
+This is useful for static functions/data that occur in multiple files.
 A symbol name may also be optionally preceded with the architecture (e.g. ppc:_foo or ppc:foo.o:_foo).
-This enables you to have one order file that works for multiple architectures. 
+This enables you to have one order file that works for multiple architectures.
 Literal c-strings may be ordered by by quoting the string (e.g. "Hello, world\\n") in the order file.
 .It Fl no_order_inits
 When the -order_file option is not used, the linker lays out functions in object file order and
@@ -279,22 +279,22 @@ Specifies the perferred load address for a dylib or bundle. The argument
 is a hexadecimal number with an optional leading 0x.  By choosing non-overlapping address for all
 dylibs and bundles that a program loads, launch time can be improved because dyld will not need to
 "rebase" the image (that is, adjust pointers within the image to work at the loaded address).
-It is often easier to not use this option, but instead use the rebase(1) tool, and give it a list of dylibs.  
+It is often easier to not use this option, but instead use the rebase(1) tool, and give it a list of dylibs.
 It will then choose non-overlapping addresses for the list and rebase them all.
 This option is also called -seg1addr for compatibility.
 .It Fl no_implicit_dylibs
-When creating a two-level namespace final linked image, normally the linker will hoist up public dylibs 
+When creating a two-level namespace final linked image, normally the linker will hoist up public dylibs
 that are implicitly linked to make the two-level namespace
-encoding more efficient for dyld.  For example, Cocoa re-exports AppKit and AppKit re-exports Foundation. 
+encoding more efficient for dyld.  For example, Cocoa re-exports AppKit and AppKit re-exports Foundation.
 If you link with -framework Cocoa and use a symbol from Foundation, the linker will implicitly add a load
-command to load Foundation and encode the symbol as coming from Foundation.  If you use this option, 
+command to load Foundation and encode the symbol as coming from Foundation.  If you use this option,
 the linker will not add a load command for Foundation and encode the symbol as coming from Cocoa.  Then
 at runtime dyld will have to search Cocoa and AppKit before finding the symbol in Foundation.
 .It Fl exported_symbols_order Ar file
 When targeting Mac OS X 10.6 or later, the format of the exported symbol information can be optimized to
 make lookups of popular symbols faster.  This option is used to pass a file containing a list of
 the symbols most frequently used by clients of the dynamic library being built. Not all exported symbols
-need to be listed.  
+need to be listed.
 .It Fl no_zero_fill_sections
 By default the linker moves all zero fill sections to the end of the __DATA segment and configures
 them to use no space on disk.  This option suppresses that optimization, so zero-filled data occupies
@@ -306,32 +306,32 @@ Disables linker creation of branch islands which allows images to be created tha
 maximum branch distance. Useful with -preload when code is in multiple sections but all are within
 the branch range.
 .El
-.Ss Options when creating a dynamic library (dylib) 
+.Ss Options when creating a dynamic library (dylib)
 .Bl -tag
 .It Fl install_name Ar name
-Sets an internal "install path" (LC_ID_DYLIB) in a dynamic library. Any clients linked against the library 
-will record that path as the way dyld should locate this library.  If this option is not specified, then 
+Sets an internal "install path" (LC_ID_DYLIB) in a dynamic library. Any clients linked against the library
+will record that path as the way dyld should locate this library.  If this option is not specified, then
 the -o path will be used.  This option is also called -dylib_install_name for compatibility.
 .It Fl mark_dead_strippable_dylib
-Specifies that the dylib being built can be dead strip by any client.  That is, the dylib has 
+Specifies that the dylib being built can be dead strip by any client.  That is, the dylib has
 no initialization side effects.  So if a client links against the dylib, but never uses
 any symbol from it, the linker can optimize away the use of the dylib.
 .It Fl compatibility_version Ar number
-Specifies the compatibility version number of the library.  When a library is loaded by dyld, the 
+Specifies the compatibility version number of the library.  When a library is loaded by dyld, 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 
-.Ar number 
-is X[.Y[.Z]] where X must be a positive non-zero number less than or equal to 65535, 
-and .Y and .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. 
+The format of
+.Ar number
+is X[.Y[.Z]] where X must be a positive non-zero number less than or equal to 65535,
+and .Y and .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 also called -dylib_compatibility_version for compatibility.
 .It Fl current_version Ar number
-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 
-.Ar number 
-is X[.Y[.Z]] where X must be a positive non-zero number less than or equal to 65535, 
-and .Y and .Z are optional and if present must be non-negative numbers less than or equal to 255. 
+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
+.Ar number
+is X[.Y[.Z]] where X must be a positive non-zero number less than or equal to 65535,
+and .Y and .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 also called -dylib_current_version for compatibility.
 .El
@@ -339,8 +339,8 @@ This option is also called -dylib_current_version for compatibility.
 .Bl -tag
 .It Fl pie
 This makes a special kind of main executable that is position independent (PIE).  On Mac OS X 10.5 and later, the OS
-the OS will load a PIE at a random address each time it is executed.  You cannot create a PIE from .o files compiled 
-with -mdynamic-no-pic.  That means the codegen is less optimal, but the address randomization adds some 
+the OS will load a PIE at a random address each time it is executed.  You cannot create a PIE from .o files compiled
+with -mdynamic-no-pic.  That means the codegen is less optimal, but the address randomization adds some
 security. When targeting Mac OS X 10.7 or later PIE is the default for main executables.
 .It Fl no_pie
 Do not make a position independent executable (PIE).  This is the default, when targeting 10.6 and earlier.
@@ -348,11 +348,11 @@ Do not make a position independent executable (PIE).  This is the default, when
 By default the linker creates an unreadable segment starting at address zero named __PAGEZERO.  Its existence
 will cause a bus error if a NULL pointer is dereferenced.  The argument
 .Ar size
-is a hexadecimal number with an optional leading 0x.  If  
+is a hexadecimal number with an optional leading 0x.  If
 .Ar size
-is zero, the linker will not generate a page zero segment.  By default on 32-bit architectures the page zero size 
+is zero, the linker will not generate a page zero segment.  By default on 32-bit architectures the page zero size
 is 4KB.  On 64-bit architectures, the default size is 4GB.  The ppc64 architecture has some special cases. Since Mac
-OS X 10.4 did not support 4GB page zero programs, the default page zero size for ppc64 will be 4KB unless 
+OS X 10.4 did not support 4GB page zero programs, the default page zero size for ppc64 will be 4KB unless
 -macosx_version_min is 10.5 or later.  Also, the -mdynamic-no-pic codegen model for ppc64 will only work if the
 code is placed in the lower 2GB of the address space, so the if the linker detects any such code, the page zero
 size is set to 4KB and then a new unreadable trailing segment is created after the code, filling up the lower 4GB.
@@ -360,31 +360,31 @@ size is set to 4KB and then a new unreadable trailing segment is created after t
 Specifies the maximum stack size for the main thread in a program.  Without this option a program has a 8MB stack.
 The argument
 .Ar size
-is a hexadecimal number with an optional leading 0x. The 
+is a hexadecimal number with an optional leading 0x. The
 .Ar size
 should be a multiple of the architecture's page size (4KB or 16KB).
-.It Fl allow_stack_execute 
+.It Fl allow_stack_execute
 Marks executable so that all stacks in the task will be given stack execution privilege. This includes pthread stacks.
 .It Fl export_dynamic
 Preserves all global symbols in main executables during LTO.  Without this option, Link Time Optimization
 is allowed to inline and remove global functions. This option is used when a main executable may load
-a plug-in which requires certain symbols from the main executable. 
+a plug-in which requires certain symbols from the main executable.
 .El
 .Ss Options when creating a bundle
 .Bl -tag
 .It Fl bundle_loader Ar executable
-This specifies the 
-.Ar executable 
-that will be loading the bundle output file being linked. 
-Undefined symbols from the bundle are checked against the specified 
-.Ar executable 
-like it was one of the 
+This specifies the
+.Ar executable
+that will be loading the bundle output file being linked.
+Undefined symbols from the bundle are checked against the specified
+.Ar executable
+like it was one of the
 dynamic libraries the bundle was linked with.
 .El
 .Ss Options when creating an object file
 .Bl -tag
 .It Fl keep_private_externs
-Don't turn private external (aka visibility=hidden) symbols into static symbols, 
+Don't turn private external (aka visibility=hidden) symbols into static symbols,
 but rather leave them as private external in the resulting object file.
 .It Fl d
 Force definition of common symbols.  That is, transform tentative definitions into real definitions.
@@ -392,85 +392,85 @@ Force definition of common symbols.  That is, transform tentative definitions in
 .Ss Options that control symbol resolution
 .Bl -tag
 .It Fl exported_symbols_list Ar filename
-The specified 
-.Ar filename 
-contains a list 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 __private_extern__ (aka visibility=hidden) 
+The specified
+.Ar filename
+contains a list 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 __private_extern__ (aka visibility=hidden)
 and will not be global in the output file. The symbol names listed in filename must be one per line.
-Leading and trailing white space are not part of the symbol name. 
+Leading and trailing white space are not part of the symbol name.
 Lines starting with # are ignored, as are lines with only white space.
 Some wildcards (similar to shell file matching) are supported.  The * matches zero or more characters.
 The ? matches one character.  [abc] matches one character which must be an 'a', 'b', or 'c'.  [a-z] matches
-any single lower case letter from 'a' to 'z'. 
+any single lower case letter from 'a' to 'z'.
 .It Fl exported_symbol Ar symbol
-The specified 
+The specified
 .Ar symbol
-is added to the list of global symbols names that will remain as global symbols in the output file.  This 
-option can be used multiple times.  For short lists, this can be more convenient than creating a file and using 
+is added to the list of global symbols names that will remain as global symbols in the output file.  This
+option can be used multiple times.  For short lists, this can be more convenient than creating a file and using
 -exported_symbols_list.
 .It Fl unexported_symbols_list Ar file
-The specified 
-.Ar filename 
-contains a list of global symbol names that will not remain as global symbols in the output file. 
+The specified
+.Ar filename
+contains a list 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 __private_extern__ (aka visibility=hidden) and will not be global
-in the output file. The symbol names listed in filename must be one per line. 
-Leading and trailing white space are not part of the symbol name. 
+in the output file. The symbol names listed in 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.
 Some wildcards (similar to shell file matching) are supported.  The * matches zero or more characters.
 The ? matches one character.  [abc] matches one character which must be an 'a', 'b', or 'c'.  [a-z] matches
-any single lower case letter from 'a' to 'z'. 
+any single lower case letter from 'a' to 'z'.
 .It Fl unexported_symbol Ar symbol
-The specified 
+The specified
 .Ar symbol
-is added to the list of global symbols names that will not remain as global symbols in the output file.  This 
-option can be used multiple times.  For short lists, this can be more convenient than creating a file and using 
+is added to the list of global symbols names that will not remain as global symbols in the output file.  This
+option can be used multiple times.  For short lists, this can be more convenient than creating a file and using
 -unexported_symbols_list.
 .It Fl reexported_symbols_list Ar file
-The specified 
-.Ar filename 
+The specified
+.Ar filename
 contains a list of symbol names that are implemented in a dependent dylib and should be re-exported
-through the dylib being created. 
+through the dylib being created.
 .It Fl alias Ar symbol_name Ar alternate_symbol_name
-Create an alias named 
+Create an alias named
 .Ar alternate_symbol_name
 for the symbol
 .Ar symbol_name .
 By default the alias symbol has global visibility.  This option was previous the -idef:indir option.
 .It Fl alias_list Ar filename
-The specified 
+The specified
 .Ar filename
 contains a list of aliases. The symbol name and its alias are on one line, separated by whitespace.
 Lines starting with # are ignored.
-.It Fl flat_namespace 
+.It Fl flat_namespace
 Alters how symbols are resolved at build time and runtime.  With -two_levelnamespace (the default), the linker
-only searches dylibs on the command line for symbols, and records in which dylib they were found.  With -flat_namespace, 
+only searches dylibs on the command line for symbols, and records in which dylib they were found.  With -flat_namespace,
 the linker searches all dylibs on the command line and all dylibs those original dylibs depend on.  The linker
 does not record which dylib an external symbol came from, so at runtime dyld again searches all images and uses
 the first definition it finds.  In addition, any undefines in loaded flat_namespace dylibs must be resolvable
-at build time.  
+at build time.
 .It Fl u Ar symbol_name
-Specified that symbol 
+Specified that symbol
 .Ar symbol_name
 must be defined for the link to succeed.  This is useful to force selected functions to be loaded
 from a static library.
 .It Fl U Ar symbol_name
-Specified that it is ok for 
+Specified that it is ok for
 .Ar symbol_name
 to have no definition.  With -two_levelnamespace, the resulting symbol will be marked dynamic_lookup which
 means dyld will search all loaded images.
 .It Fl undefined Ar treatment
 Specifies how undefined symbols are to be treated. Options are: error, warning, suppress, or dynamic_lookup.  The
-default is error. 
+default is error.
 .It Fl rpath Ar path
-Add 
+Add
 .Ar path
 to the runpath search path list for image being created.  At runtime, dyld uses the runpath when searching
 for dylibs whose load path begins with @rpath/.
 .It Fl commons Ar treatment
-Specifies how commons (aka tentative definitions) are resolved with respect to dylibs.  Options are: 
+Specifies how commons (aka tentative definitions) are resolved with respect to dylibs.  Options are:
 ignore_dylibs, use_dylibs, error.  The default is ignore_dylibs which means the linker will turn a tentative
 definition in an object file into a real definition and not even check dylibs for conflicts.  The dylibs
-option means the linker should check linked dylibs for definitions and use them to replace tentative definitions 
+option means the linker should check linked dylibs for definitions and use them to replace tentative definitions
 from object files.  The error option means the linker should issue an error whenever a tentative definition in an
 object file conflicts with an external symbol in a linked dylib.  See also -warn_commons.
 .El
@@ -480,7 +480,7 @@ object file conflicts with an external symbol in a linked dylib.  See also -warn
 Log why each object file in a static library is loaded. That is, what symbol was needed.  Also called -whyload
 for compatibility.
 .It Fl why_live Ar symbol_name
-Logs a chain of references to 
+Logs a chain of references to
 .Ar symbol_name .
 Only applicable with -dead_strip .
 It can help debug why something that you think should be dead strip removed is not removed.
@@ -508,15 +508,15 @@ that will be automatically removed when linked into a final linked image.  This
 allows dead code stripping, which uses symbols to break up code and data, to
 work properly and provides the security of having source symbol names removed.
 .It Fl non_global_symbols_strip_list Ar filename
-The specified 
-.Ar filename 
-contains a list of non-global symbol names that should be removed from the output file's symbol table.  All other 
+The specified
+.Ar filename
+contains a list of non-global symbol names that should be removed from the output file's symbol table.  All other
 non-global symbol names will remain in the output files symbol table. See -exported_symbols_list for syntax and use
 of wildcards.
 .It Fl non_global_symbols_no_strip_list Ar filename
-The specified 
-.Ar filename 
-contains a list of non-global symbol names that should be remain in the output file's symbol table.  All other 
+The specified
+.Ar filename
+contains a list of non-global symbol names that should be remain in the output file's symbol table.  All other
 symbol names will be removed from the output file's symbol table. See -exported_symbols_list for syntax and use
 of wildcards.
 .El
@@ -526,7 +526,7 @@ of wildcards.
 Generates an embedded bitcode bundle in the output binary. The bitcode bundle is embedded in __LLVM, __bundle section.
 This option requires all the object files, static libraries and user frameworks/dylibs contain bitcode.
 Note: not all the linker options are supported to use together with -bitcode_bundle.
-.It Fl bitcode_hide_symbol
+.It Fl bitcode_hide_symbols
 Specifies this option together with -bitcode_bundle to hide all non-exported symbols from output bitcode bundle.
 The hide symbol process might not be reversible. To obtain a reverse mapping file to recover all the symbols, use
 -bitcode_symbol_map option.
@@ -541,6 +541,9 @@ Otherwise, the reverse map will be written to a file at
 .Bl -tag
 .It Fl v
 Prints the version of the linker.
+.It Fl no_weak_imports
+Error if any symbols are weak imports (i.e. allowed to be unresolved (NULL) at runtime). Useful for config based
+projects that assume they are built and run on the same OS version.
 .It Fl no_deduplicate
 Don't run deduplication pass in linker
 .It Fl verbose_deduplicate
@@ -561,34 +564,33 @@ Moves data symbols to another segment.  The command line option specifies the
 target segment name and a path to a file containing a list of symbols to move.
 Comments can be added to the symbol file by starting a line with a #.
 If there are multiple instances of a symbol name (for instance a "static int foo=5;" in multiple files)
-the symbol name in the symbol list file can be prefixed with the object file name 
+the symbol name in the symbol list file can be prefixed with the object file name
 (e.g. "init.o:_foo") to move a specific instance.
-.It Fl move_to_ro_section Ar segment_name Ar section_name Ar filename
+.It Fl move_to_ro_segment Ar segment_name Ar filename
 Moves code symbols to another segment.  The command line option specifies the
 target segment name and a path to a file containing a list of symbols to move.
 Comments can be added to the symbol file by starting a line with a #.
 If there are multiple instances of a symbol name (for instance a "static int foo() {}" in multiple files)
-the symbol name in the symbol list file can be prefixed with the object file name 
+the symbol name in the symbol list file can be prefixed with the object file name
 (e.g. "init.o:_foo") to move a specific instance.
 .It Fl rename_section Ar orgSegment orgSection newSegment newSection
 Renames section orgSegment/orgSection to newSegment/newSection.
-.It Fl rename_segment Ar orgSegment newSegment 
+.It Fl rename_segment Ar orgSegment newSegment
 Renames all sections with orgSegment segment name to have newSegment segment name.
 .It Fl trace_symbol_layout
 For using in debugging -rename_section, -rename_segment, -move_to_ro_segment, and -move_to_rw_segment.
 This option prints out a line show where and why each symbol was moved.
-Note: These options do not chain.  For each symbol, the linker first checks
--move_to_ro_segment and -move_to_rw_segment.  If the symbol is not moved,
-it checks for an applicable -rename_section.  Only if the symbol still has
-not been moved, does the linker look for an applicable -rename_segment option.
+Note: These options do chain.  For each symbol, the linker first checks
+-move_to_ro_segment and -move_to_rw_segment. Next it applies any -rename_section options,
+and lastly and -rename_segment options.
 .It Fl section_order Ar segname Ar colon_separated_section_list
 Only for use with -preload.  Specifies the order that sections with the specified segment should be layout out.
-For example: "-section_order __ROM __text:__const:__cstring". 
+For example: "-section_order __ROM __text:__const:__cstring".
 .It Fl segment_order Ar colon_separated_segment_list
 Only for use with -preload.  Specifies the order segments should be layout out.
-For example: "-segment_order __ROM:__ROM2:__RAM". 
+For example: "-segment_order __ROM:__ROM2:__RAM".
 .It Fl allow_heap_execute
-Normally i386 main executables will be marked so that the Mac OS X 10.7 and later kernel 
+Normally i386 main executables will be marked so that the Mac OS X 10.7 and later kernel
 will only allow pages with the x-bit to execute instructions. This option overrides that
 behavior and allows instructions on any page to be executed.
 .It Fl application_extension
@@ -596,7 +598,7 @@ Specifies that the code is being linked for use in an application extension.  Th
 will then validiate that any dynamic libraries linked against are safe for use in
 application extensions.
 .It Fl no_application_extension
-Specifies that the code is being linked is not safe for use in an application extension. 
+Specifies that the code is being linked is not safe for use in an application extension.
 For instance, can be used when creating a framework that should not be used in
 an application extension.
 .It Fl fatal_warnings
@@ -608,22 +610,22 @@ linker but are needed by earlier linker tools.
 .It Fl warn_compact_unwind
 When producing a final linked image, the linker processes the __eh_frame section and
 produces an __unwind_info section. Most FDE entries in the __eh_frame can be represented
-by a 32-bit value in the __unwind_info section.  The option issues a warning for 
+by a 32-bit value in the __unwind_info section.  The option issues a warning for
 any function whose FDE cannot be expressed in the compact unwind format.
 .It Fl warn_weak_exports
 Issue a warning if the resulting final linked image contains weak external symbols. Such
 symbols require dyld to do extra work at launch time to coalesce those symbols.
 .It Fl objc_gc_compaction
-Marks the Objective-C image info in the final linked image with the bit that says that the  
+Marks the Objective-C image info in the final linked image with the bit that says that the
 code was built to work the compacting garbage collection.
 .It Fl objc_gc
 Verifies all code was compiled with -fobjc-gc or -fobjc-gc-only.
 .It Fl objc_gc_only
 Verifies all code was compiled with -fobjc-gc-only.
 .It Fl dead_strip_dylibs
-Remove dylibs that are unreachable by the entry point or exported symbols. That is,  
+Remove dylibs that are unreachable by the entry point or exported symbols. That is,
 suppresses the generation of load command commands for dylibs which supplied no
-symbols during the link. This option should not be used when linking against a dylib which 
+symbols during the link. This option should not be used when linking against a dylib which
 is required at runtime for some indirect reason such as the dylib has an important initializer.
 .It Fl allow_sub_type_mismatches
 Normally the linker considers different cpu-subtype for ARM (e.g. armv4t and armv6) to be different
@@ -636,32 +638,32 @@ Sets the MH_ROOT_SAFE bit in the mach header of the output file.
 .It Fl setuid_safe
 Sets the MH_SETUID_SAFE bit in the mach header of the output file.
 .It Fl interposable
-Indirects access to all to exported symbols when creating a dynamic library.  
+Indirects access to all to exported symbols when creating a dynamic library.
 .It Fl init Ar symbol_name
-The specified symbol_name will be run as the first initializer.   Only used when creating a dynamic library.  
+The specified symbol_name will be run as the first initializer.   Only used when creating a dynamic library.
 .It Fl sub_library Ar library_name
-The specified dylib will be re-exported. For example the library_name for /usr/lib/libobjc_profile.A.dylib would be libobjc. 
-Only used when creating a dynamic library.  
+The specified dylib will be re-exported. For example the library_name for /usr/lib/libobjc_profile.A.dylib would be libobjc.
+Only used when creating a dynamic library.
 .It Fl sub_umbrella Ar framework_name
-The specified framework will be re-exported.  Only used when creating a dynamic library. 
+The specified framework will be re-exported.  Only used when creating a dynamic library.
 .It Fl allowable_client Ar name
-Restricts what can link against the dynamic library being created.  By default any code 
+Restricts what can link against the dynamic library being created.  By default any code
 can link against any dylib. But if a dylib is supposed to be private to a small
 set of clients, you can formalize that by adding a -allowable_client for each client.
 If a client is libfoo.1.dylib its -allowable_client name would be "foo".  If a
 client is Foo.framework its -allowable_client name would be "Foo".  For the degenerate
-case where you want no one to ever link against a dylib, you can set the 
--allowable_client to "!".  
+case where you want no one to ever link against a dylib, you can set the
+-allowable_client to "!".
 .It Fl client_name Ar name
-Enables a bundle to link against a dylib that was built with -allowable_client.  
+Enables a bundle to link against a dylib that was built with -allowable_client.
 The name specified must match one of the -allowable_client names specified when the dylib was created.
 .It Fl umbrella Ar framework_name
 Specifies that the dylib being linked is re-exported through an umbrella framework of the specified name.
 .It Fl headerpad Ar size
-Specifies the minimum space for future expansion of the load commands.  Only useful if intend to run 
+Specifies the minimum space for future expansion of the load commands.  Only useful if intend to run
 install_name_tool to alter the load commands later. Size is a hexadecimal number.
 .It Fl headerpad_max_install_names
-Automatically adds space for future expansion of load commands such that all paths could expand to MAXPATHLEN. 
+Automatically adds space for future expansion of load commands such that all paths could expand to MAXPATHLEN.
 Only useful if intend to run install_name_tool to alter the load commands later.
 .It Fl bind_at_load
 Sets a bit in the mach header of the resulting binary which tells dyld to bind all symbols when the binary is loaded, rather than lazily.
@@ -669,43 +671,43 @@ Sets a bit in the mach header of the resulting binary which tells dyld to bind a
 Sets a bit in the mach header of the resulting binary which tells dyld to not only use flat namespace for the binary,
 but force flat namespace binding on all dylibs and bundles loaded in the process.  Can only be used when linking main executables.
 .It Fl sectalign Ar segname Ar sectname Ar value
-The section named sectname in the segment segname will have its alignment set to value, where value is a hexadecimal 
-number that must be an integral power of 2. 
+The section named sectname in the segment segname will have its alignment set to value, where value is a hexadecimal
+number that must be an integral power of 2.
 .It Fl stack_addr Ar address
 Specifies the initial address of the stack pointer value, where value is a hexadecimal number rounded to a page boundary.
 .It Fl segprot Ar segname Ar max_prot Ar init_prot
-Specifies the maximum and initial virtual memory protection of the named segment, name, to be max and init ,respectively. 
+Specifies the maximum and initial virtual memory protection of the named segment, name, to be max and init ,respectively.
 The values for max and init are any combination of the characters `r' (for read), `w' (for write), `x' (for execute) and `-' (no access).
 .It Fl seg_addr_table Ar filename
-Specifies a file containing base addresses for dynamic libraries.  Each line of the file is a hexadecimal base address 
+Specifies a file containing base addresses for dynamic libraries.  Each line of the file is a hexadecimal base address
 followed by whitespace then the install name of the corresponding dylib. The # character denotes a comment.
 .It Fl segs_read_write_addr Ar address
-Allows a dynamic library to be built where the read-only and read-write segments are not contiguous.  The address 
+Allows a dynamic library to be built where the read-only and read-write segments are not contiguous.  The address
 specified is a hexadecimal number that indicates the base address for the read-write segments.
 .It Fl segs_read_only_addr Ar address
-Allows a dynamic library to be built where the read-only and read-write segments are not contiguous.  The address 
+Allows a dynamic library to be built where the read-only and read-write segments are not contiguous.  The address
 specified is a hexadecimal number that indicates the base address for the read-only segments.
 .It Fl segaddr Ar name Ar address
-Specifies the starting address of the segment named name to be address. The address must be a hexadecimal number 
+Specifies the starting address of the segment named name to be address. The address must be a hexadecimal number
 that is a multiple of 4K page size.
 .It Fl seg_page_size Ar name Ar size
-Specifies the page size used by the specified segment.  By default the page size is 4096 for all segments. 
+Specifies the page size used by the specified segment.  By default the page size is 4096 for all segments.
 The linker will lay out segments such that size of a segment is always an even multiple of its page size.
 .It Fl dylib_file Ar install_name:file_name
-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. install_name specifies the path where the library normally resides. 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: 
+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. install_name specifies the path where the library normally resides. 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:
 -dylib_file /lib/libsys_s.A.dylib:/me/lib/libsys_s.A.dylib.
 .It Fl prebind
-The created output file will be in the prebound format.  This was used in Mac OS X 10.3 and earlier to improve launch performance.  
+The created output file will be in the prebound format.  This was used in Mac OS X 10.3 and earlier to improve launch performance.
 .It Fl weak_reference_mismatches Ar treatment
-Specifies what to do if a symbol is weak-imported in one object file but not weak-imported in another.  The valid 
+Specifies what to do if a symbol is weak-imported in one object file but not weak-imported in another.  The valid
 treatments are: error, weak, or non-weak.  The default is non-weak.
 .It Fl read_only_relocs Ar treatment
-Enables the use of relocations which will cause dyld to modify (copy-on-write) read-only pages.  The compiler will 
-normally never generate such code.  
+Enables the use of relocations which will cause dyld to modify (copy-on-write) read-only pages.  The compiler will
+normally never generate such code.
 .It Fl force_cpusubtype_ALL
 The is only applicable with -arch ppc.  It tells the linker to ignore the PowerPC cpu requirements (e.g. G3, G4 or G5) encoded
 in the object files and mark the resulting binary as runnable on any PowerPC cpu.
@@ -714,20 +716,20 @@ Only used when building dyld.
 .It Fl no_arch_warnings
 Suppresses warning messages about files that have the wrong architecture for the -arch flag
 .It Fl arch_errors_fatal
-Turns into errors, warnings about files that have the wrong architecture for the -arch flag. 
+Turns into errors, warnings about files that have the wrong architecture for the -arch flag.
 .It Fl e Ar symbol_name
 Specifies the entry point of a main executable.  By default the entry name is "start" which is found in crt1.o which contains
 the glue code need to set up and call main().
 .It Fl w
 Suppress all warning messages
 .It Fl final_output Ar name
-Specifies the install name of a dylib if -install_name is not used.  This option is used by gcc driver when it is invoked 
-with multiple -arch arguments.  
+Specifies the install name of a dylib if -install_name is not used.  This option is used by gcc driver when it is invoked
+with multiple -arch arguments.
 .It Fl arch_multiple
-Specifes that the linker should augment error and warning messages with the architecture name.  This option is used by gcc 
-driver when it is invoked with multiple -arch arguments.  
+Specifes that the linker should augment error and warning messages with the architecture name.  This option is used by gcc
+driver when it is invoked with multiple -arch arguments.
 .It Fl twolevel_namespace_hints
-Specifies that hints should be added to the resulting binary that can help speed up runtime binding by dyld as long as the 
+Specifies that hints should be added to the resulting binary that can help speed up runtime binding by dyld as long as the
 libraries being linked against have not changed.
 .It Fl dot Ar path
 Create a file at the specified path containing a graph of symbol dependencies.  The .dot file can be viewed in GraphViz.
@@ -743,17 +745,17 @@ in a header file.
 .It Fl read_only_stubs
 [i386 only] Makes the __IMPORT segment of a final linked images read-only.  This option makes a program slightly more
 secure in that the JMP instructions in the i386 fast stubs cannot be easily overwritten by malicious code.  The downside
-is the dyld must use mprotect() to temporarily make the segment writable while it is binding the stubs. 
+is the dyld must use mprotect() to temporarily make the segment writable while it is binding the stubs.
 .It Fl slow_stubs
-[i386 only]  Instead of using single JMP instruction stubs, the linker creates code in the __TEXT segment which 
-calls through a lazy pointer in the __DATA segment.  
+[i386 only]  Instead of using single JMP instruction stubs, the linker creates code in the __TEXT segment which
+calls through a lazy pointer in the __DATA segment.
 .It Fl interposable_list Ar filename
-The specified 
-.Ar filename 
+The specified
+.Ar filename
 contains a list of global symbol names that should always be accessed indirectly.  For instance, if libSystem.dylib
 is linked such that _malloc is interposable, then calls to malloc() from within libSystem will go through a dyld
-stub and could potentially indirected to an alternate malloc.  If libSystem.dylib were built without making _malloc 
-interposable then if _malloc was interposed at runtime, calls to malloc from with libSystem would be missed 
+stub and could potentially indirected to an alternate malloc.  If libSystem.dylib were built without making _malloc
+interposable then if _malloc was interposed at runtime, calls to malloc from with libSystem would be missed
 (not interposed) because they would be direct calls.
 .It Fl no_function_starts
 By default the linker creates a compress table of function start addresses in the LINKEDIT of
@@ -768,16 +770,27 @@ being linked for the optimization to occur.  Using this option disables that beh
 .It Fl object_path_lto Ar filename
 When performing Link Time Optimization (LTO) and a temporary mach-o object file is needed, if this
 option is used, the temporary file will be stored at the specified path and remain after the link
-is complete.  Without the option, the linker picks a path and deletes the object file before the linker 
+is complete.  Without the option, the linker picks a path and deletes the object file before the linker
 tool completes, thus tools such as the debugger or dsymutil will not be able to access the DWARF debug
 info in the temporary object file.
 .It Fl lto_library Ar path
 When performing Link Time Optimization (LTO), the linker normally loads libLTO.dylib relative to the linker
 binary (../lib/libLTO.dylib). This option allows the user to specify the path to a specific libLTO.dylib
 to load instead.
+.It Fl cache_path_lto Ar path
+When performing Incremental Link Time Optimization (LTO), use this directory as a cache for incremental rebuild.
+.It Fl prune_interval_lto Ar seconds
+When performing Incremental Link Time Optimization (LTO), the cache will pruned after the specified interval.
+.It Fl prune_after_lto Ar seconds
+When pruning the cache for Incremental Link Time Optimization (LTO), the cache entries are removed after the
+specificied interval.
+.It Fl max_relative_cache_size_lto Ar percent
+When performing Incremental Link Time Optimization (LTO), the cache will be pruned to not go over this percentage
+of the free space. I.e. a value of 100 would indicate that the cache may fill the disk, and a value of 50 would
+indicate that the cache size will be kept under the free disk space.
 .It Fl page_align_data_atoms
-During development, this option can be used to space out all global variables so each is on a separate page. 
-This is useful when analyzing dirty and resident pages.  The information can then be used to create an 
+During development, this option can be used to space out all global variables so each is on a separate page.
+This is useful when analyzing dirty and resident pages.  The information can then be used to create an
 order file  to cluster commonly used/dirty globals onto the same page(s).
 .It Fl not_for_dyld_shared_cache
 Normally, the linker will add extra info to dylibs with -install_name starting with /usr/lib or
@@ -787,7 +800,7 @@ tells the linker to not add that extra info.
 .Ss Obsolete Options
 .Bl -tag
 .It Fl segalign Ar value
-All segments must be page aligned. 
+All segments must be page aligned.
 .It Fl seglinkedit
 Object files (MH_OBJECT) with a LINKEDIT segment are no longer supported. This option is obsolete.
 .It Fl noseglinkedit
@@ -808,13 +821,13 @@ When using -prebind, the linker allows overlapping by default, so this option is
 LD_PREBIND is no longer supported as a way to force on prebinding, so there no longer needs to
 be a command line way to override LD_PREBIND.  This option is obsolete.
 .It Fl sect_diff_relocs Ar treatment
-This option was an attempt to warn about linking .o files compiled without -mdynamic-no-pic into 
+This option was an attempt to warn about linking .o files compiled without -mdynamic-no-pic into
 a main executable, but the false positive rate generated too much noise to make the option useful.
 This option is obsolete.
 .It Fl run_init_lazily
 This option was removed in Mac OS X 10.2.
 .It Fl single_module
-This is now the default so does not need to be specified. 
+This is now the default so does not need to be specified.
 .It Fl multi_module
 Multi-modules in dynamic libraries have been ignored at runtime since Mac OS X 10.4.0.  This option is obsolete.
 .It Fl no_dead_strip_inits_and_terms
@@ -824,7 +837,7 @@ Obsolete incremental load format.  This option is obsolete.
 .It Fl b
 Used with -A option to strip base file's symbols.  This option is obsolete.
 ..It Fl M
-Obsolete option to produce a load map.  Use -map option instead. 
+Obsolete option to produce a load map.  Use -map option instead.
 .It Fl Sn
 Don't strip any symbols.  This is the default.  This option is obsolete.
 .It Fl Si
@@ -835,15 +848,15 @@ This style of debugging is obsolete in Mac OS X 10.5.  This option is obsolete.
 .It Fl X
 Strip local symbols that begin with 'L'.  This is the default.  This option is obsolete.
 .It Fl s
-Completely strip the output, including removing the symbol table.  This file format variant is no longer supported.  
+Completely strip the output, including removing the symbol table.  This file format variant is no longer supported.
 This option is obsolete.
 .It Fl m
 Don't treat multiple definitions as an error.  This is no longer supported. This option is obsolete.
 .It Fl y Ns symbol
-Display each file in which 
+Display each file in which
 .Ar symbol
 is used.  This was previously used to debug where an undefined symbol was used, but the linker now
-automatically prints out all usages.  The -why_live option can also be used to display what kept 
+automatically prints out all usages.  The -why_live option can also be used to display what kept
 a symbol from being dead striped.  This option is obsolete.
 .It Fl Y Ar number
 Used to control how many occurrences of each symbol specified with -y would be shown.  This option is obsolete.
@@ -854,23 +867,23 @@ bit has been obsolete since Mac OS X 10.4.  This option is obsolete.
 Previously provided a way to warn or error if any of the symbol definitions in the output file matched any
 definitions in dynamic library being linked.  This option is obsolete.
 .It Fl multiply_defined Ar treatment
-Previously provided a way to warn or error if any of the symbols used from a dynamic library were also 
+Previously provided a way to warn or error if any of the symbols used from a dynamic library were also
 available in another linked dynamic library.  This option is obsolete.
 .It Fl private_bundle
-Previously prevented errors when -flat_namespace, -bundle, and -bundle_loader were used and the bundle 
+Previously prevented errors when -flat_namespace, -bundle, and -bundle_loader were used and the bundle
 contained a definition that conflicted with a symbol in the main executable.  The linker no longer
 errors on such conflicts.  This option is obsolete.
 .It Fl noall_load
 This is the default.  This option is obsolete.
 .It Fl seg_addr_table_filename Ar path
-Use 
+Use
 .Ar path
 instead of the install name of the library for matching an entry in the seg_addr_table.  This option is obsolete.
 .It Fl sectorder Ar segname sectname orderfile
 Replaced by more general -order_file option.
 .It Fl sectorder_detail
-Produced extra logging about which entries from a sectorder entries were used.  Replaced by -order_file_statistics. 
-This option is obsolete. 
+Produced extra logging about which entries from a sectorder entries were used.  Replaced by -order_file_statistics.
+This option is obsolete.
 .El
 .Sh SEE ALSO
 as(1), ar(1), cc(1), nm(1), otool(1) lipo(1),
index c47b806190262d8b4d9419c286ccbb7d613d4b0a..600aeac98043e75b8bd9c8c3fcaa0f5452c0ff6b 100644 (file)
@@ -20,6 +20,7 @@
                                F9EA73970974999B008B4F1D /* PBXTargetDependency */,
                                F9B693890EC4D28C00076912 /* PBXTargetDependency */,
                                F9F9AD68116D58AF0028EFAB /* PBXTargetDependency */,
+                               83046A911C90066900024A7E /* PBXTargetDependency */,
                        );
                        name = "unit-tests";
                        productName = "unit-tests";
@@ -43,6 +44,7 @@
 /* End PBXAggregateTarget section */
 
 /* Begin PBXBuildFile section */
+               83046A851C8FF2F700024A7E /* objcimageinfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83046A841C8FF2D000024A7E /* objcimageinfo.cpp */; };
                B028FCF21A9E7C3F00E3584B /* bitcode_bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B028FCF11A9E7C3F00E3584B /* bitcode_bundle.cpp */; };
                B3B672421406D42800A376BB /* Snapshot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B3B672411406D42800A376BB /* Snapshot.cpp */; };
                F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9023C3F06D5A254001BBF46 /* ld.cpp */; };
 /* End PBXBuildRule section */
 
 /* Begin PBXContainerItemProxy section */
+               83046A901C90066900024A7E /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = F9023C3006D5A227001BBF46 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 83046A771C8FF23E00024A7E;
+                       remoteInfo = objcimageinfo;
+               };
                F96904880A4333AC00B77D2A /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9023C3006D5A227001BBF46 /* Project object */;
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
+               83046A831C8FF23E00024A7E /* objcimageinfo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = objcimageinfo; sourceTree = BUILT_PRODUCTS_DIR; };
+               83046A841C8FF2D000024A7E /* objcimageinfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = objcimageinfo.cpp; path = src/other/objcimageinfo.cpp; sourceTree = "<group>"; };
                B028FCF01A9E7B4A00E3584B /* bitcode_bundle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bitcode_bundle.h; sourceTree = "<group>"; };
                B028FCF11A9E7C3F00E3584B /* bitcode_bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bitcode_bundle.cpp; sourceTree = "<group>"; };
                B091FB641ABA3AFB00CC8193 /* Bitcode.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Bitcode.hpp; path = src/ld/Bitcode.hpp; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
+               83046A7E1C8FF23E00024A7E /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                F9023C3706D5A23E001BBF46 /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                                F9B670080DDA176100E6D0DA /* unwinddump */,
                                F9BA51610ECE58BE00D1D62E /* dyldinfo */,
                                F9A3DDCA0ED762B700C590B9 /* libprunetrie.a */,
+                               83046A831C8FF23E00024A7E /* objcimageinfo */,
                        );
                        name = Products;
                        sourceTree = "<group>";
                                F9EC78050A2F8674002A3E39 /* rebase.cpp */,
                                F9A3DE0F0ED76D1900C590B9 /* prune_trie.h */,
                                F9A3DDD20ED762E400C590B9 /* PruneTrie.cpp */,
+                               83046A841C8FF2D000024A7E /* objcimageinfo.cpp */,
                        );
                        name = other;
                        sourceTree = "<group>";
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
+               83046A771C8FF23E00024A7E /* objcimageinfo */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 83046A7F1C8FF23E00024A7E /* Build configuration list for PBXNativeTarget "objcimageinfo" */;
+                       buildPhases = (
+                               83046A8F1C8FF68D00024A7E /* make configure.h */,
+                               83046A791C8FF23E00024A7E /* Sources */,
+                               83046A7E1C8FF23E00024A7E /* Frameworks */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = objcimageinfo;
+                       productName = ObjectDump;
+                       productReference = 83046A831C8FF23E00024A7E /* objcimageinfo */;
+                       productType = "com.apple.product-type.tool";
+               };
                F9023C3806D5A23E001BBF46 /* ld */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = F933D91B09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ld" */;
                                F9023C3606D5A23E001BBF46 /* Sources */,
                                F9023C3706D5A23E001BBF46 /* Frameworks */,
                                F97F5025070D0B6300B9FCD7 /* copy man page */,
+                               F94E0A911CAC6B870092DC75 /* Add libtapi symlink */,
                        );
                        buildRules = (
                                F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */,
                                F9EC77ED0A2F85F6002A3E39 /* rebase */,
                                F9B670010DDA176100E6D0DA /* unwinddump */,
                                F971EED206D5ACF60041D381 /* ObjectDump */,
+                               83046A771C8FF23E00024A7E /* objcimageinfo */,
                                F9EA72CA097454A6008B4F1D /* machocheck */,
                                F9BA51600ECE58BE00D1D62E /* dyldinfo */,
                                F9A3DDC90ED762B700C590B9 /* libprunetrie */,
 /* End PBXProject section */
 
 /* Begin PBXShellScriptBuildPhase section */
+               83046A8F1C8FF68D00024A7E /* make configure.h */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       inputPaths = (
+                       );
+                       name = "make configure.h";
+                       outputPaths = (
+                               "$(DERIVED_FILE_DIR)/configure.h",
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+                       shellPath = /bin/sh;
+                       shellScript = "${SRCROOT}/src/create_configure\n";
+                       showEnvVarsInLog = 0;
+               };
                B3C7A09714295B60005FC714 /* make compile_stub string */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 2147483647;
                        shellScript = "echo \"static const char *compile_stubs = \" > $DERIVED_FILE_DIR/compile_stubs.h\ncat compile_stubs | sed s/\\\"/\\\\\\\\\\\"/g | sed s/^/\\\"/ | sed s/\\$/\\\\\\\\n\\\"/ >> $DERIVED_FILE_DIR/compile_stubs.h\necho \";\" >> $DERIVED_FILE_DIR/compile_stubs.h";
                        showEnvVarsInLog = 0;
                };
+               F94E0A911CAC6B870092DC75 /* Add libtapi symlink */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       inputPaths = (
+                       );
+                       name = "Add libtapi symlink";
+                       outputPaths = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+                       shellPath = /bin/sh;
+                       shellScript = "if [ \"${CONFIGURATION}\" == \"Debug\" ]; then\n    cd \"${TARGET_BUILD_DIR}\"\n    cd ..\n    mkdir -p lib\n    cd lib\n    ln -s -f \"${DT_TOOLCHAIN_DIR}/usr/lib/libLTO.dylib\"\n    ln -s -f \"${DT_TOOLCHAIN_DIR}/usr/lib/libtapi.dylib\"\nfi\n\n";
+                       showEnvVarsInLog = 0;
+               };
                F96D5367094A2754008E9EE8 /* ShellScript */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 2147483647;
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
+               83046A791C8FF23E00024A7E /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               83046A851C8FF2F700024A7E /* objcimageinfo.cpp in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                F9023C3606D5A23E001BBF46 /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
+               83046A911C90066900024A7E /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 83046A771C8FF23E00024A7E /* objcimageinfo */;
+                       targetProxy = 83046A901C90066900024A7E /* PBXContainerItemProxy */;
+               };
                F96904890A4333AC00B77D2A /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = F9EC77ED0A2F85F6002A3E39 /* rebase */;
 /* End PBXTargetDependency section */
 
 /* Begin XCBuildConfiguration section */
+               83046A801C8FF23E00024A7E /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = dwarf;
+                               GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES;
+                               GCC_WARN_CHECK_SWITCH_STATEMENTS = YES;
+                               GCC_WARN_SHADOW = YES;
+                               GCC_WARN_UNUSED_FUNCTION = YES;
+                               GCC_WARN_UNUSED_VALUE = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               HEADER_SEARCH_PATHS = (
+                                       "$(SRCROOT)/src/ld",
+                                       "$(DEVELOPER_DIR)/usr/local/include",
+                                       "$(DT_TOOLCHAIN_DIR)/usr/local/include",
+                               );
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
+                               ONLY_ACTIVE_ARCH = NO;
+                               PRODUCT_NAME = objcimageinfo;
+                               SDKROOT = macosx.internal;
+                               WARNING_CFLAGS = (
+                                       "-Wmost",
+                                       "-Wno-four-char-constants",
+                                       "-Wno-unknown-pragmas",
+                               );
+                       };
+                       name = Debug;
+               };
+               83046A811C8FF23E00024A7E /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               COPY_PHASE_STRIP = YES;
+                               DEBUG_INFORMATION_FORMAT = dwarf;
+                               GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+                               GCC_OPTIMIZATION_LEVEL = s;
+                               GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;
+                               HEADER_SEARCH_PATHS = (
+                                       "$(SRCROOT)/src/ld",
+                                       "$(DEVELOPER_DIR)/usr/local/include",
+                                       "$(DT_TOOLCHAIN_DIR)/usr/local/include",
+                               );
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
+                               PRODUCT_NAME = objcimageinfo;
+                               SDKROOT = macosx.internal;
+                               WARNING_CFLAGS = (
+                                       "-Wmost",
+                                       "-Wno-four-char-constants",
+                                       "-Wno-unknown-pragmas",
+                               );
+                       };
+                       name = Release;
+               };
+               83046A821C8FF23E00024A7E /* Release-assert */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               COPY_PHASE_STRIP = YES;
+                               DEBUG_INFORMATION_FORMAT = dwarf;
+                               GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+                               GCC_OPTIMIZATION_LEVEL = s;
+                               GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;
+                               HEADER_SEARCH_PATHS = (
+                                       "$(SRCROOT)/src/ld",
+                                       "$(DEVELOPER_DIR)/usr/local/include",
+                                       "$(DT_TOOLCHAIN_DIR)/usr/local/include",
+                               );
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
+                               PRODUCT_NAME = objcimageinfo;
+                               SDKROOT = macosx.internal;
+                               WARNING_CFLAGS = (
+                                       "-Wmost",
+                                       "-Wno-four-char-constants",
+                                       "-Wno-unknown-pragmas",
+                               );
+                       };
+                       name = "Release-assert";
+               };
                F933D91C09291AC90083EAC8 /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                GCC_WARN_UNUSED_VARIABLE = YES;
                                HEADER_SEARCH_PATHS = (
                                        "$(DT_TOOLCHAIN_DIR)/usr/local/include",
-                                       "$(DEVELOPER_DIR)/usr/local/include",
-                                       "$(DEVELOPER_DIR)/usr/include",
+                                       "$(TOOLCHAIN_DIR)/usr/local/include",
                                );
                                INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
                                LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/";
                                        "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib",
                                        "@$(DERIVED_FILE_DIR)/linkExtras",
                                        "-Wl,-exported_symbol,__mh_execute_header",
+                                       "-L$(DT_TOOLCHAIN_DIR)/usr/lib",
+                                       "-ltapi",
                                );
                                PREBINDING = NO;
                                PRODUCT_NAME = ld;
                                SDKROOT = macosx.internal;
                                SECTORDER_FLAGS = "";
+                               TOOLCHAINS = osx;
                                VERSIONING_SYSTEM = "apple-generic";
                                WARNING_CFLAGS = "-Wall";
                        };
                                GCC_WARN_UNUSED_VARIABLE = YES;
                                HEADER_SEARCH_PATHS = (
                                        "$(DT_TOOLCHAIN_DIR)/usr/local/include",
-                                       "$(DEVELOPER_DIR)/usr/local/include",
-                                       "$(DEVELOPER_DIR)/usr/include",
+                                       "$(TOOLCHAIN_DIR)/usr/local/include",
                                );
                                INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
                                LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/";
                                        "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib",
                                        "@$(DERIVED_FILE_DIR)/linkExtras",
                                        "-Wl,-exported_symbol,__mh_execute_header",
+                                       "-L$(DT_TOOLCHAIN_DIR)/usr/lib",
+                                       "-ltapi",
                                );
                                PREBINDING = NO;
                                PRODUCT_NAME = ld;
                                SECTORDER_FLAGS = "";
                                STRIP_INSTALLED_PRODUCT = YES;
                                STRIP_STYLE = debugging;
+                               TOOLCHAINS = osx;
                                VALID_ARCHS = "x86_64 i386 ppc";
                                VERSIONING_SYSTEM = "apple-generic";
                                WARNING_CFLAGS = "-Wall";
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+                               CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
                                CLANG_CXX_LIBRARY = "libc++";
                                COPY_PHASE_STRIP = NO;
                                DEBUG_INFORMATION_FORMAT = dwarf;
                                GCC_WARN_UNUSED_VALUE = YES;
                                GCC_WARN_UNUSED_VARIABLE = YES;
                                HEADER_SEARCH_PATHS = (
-                                       "$(SRCROOT)/src/ld",
-                                       "$(DEVELOPER_DIR)/usr/local/include",
                                        "$(DT_TOOLCHAIN_DIR)/usr/local/include",
+                                       "$(TOOLCHAIN_DIR)/usr/local/include",
+                                       "$(SRCROOT)/src/ld",
                                );
                                INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
                                ONLY_ACTIVE_ARCH = NO;
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+                               CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
                                CLANG_CXX_LIBRARY = "libc++";
                                COPY_PHASE_STRIP = YES;
                                DEBUG_INFORMATION_FORMAT = dwarf;
                                GCC_OPTIMIZATION_LEVEL = s;
                                GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;
                                HEADER_SEARCH_PATHS = (
-                                       "$(SRCROOT)/src/ld",
-                                       "$(DEVELOPER_DIR)/usr/local/include",
                                        "$(DT_TOOLCHAIN_DIR)/usr/local/include",
+                                       "$(TOOLCHAIN_DIR)/usr/local/include",
+                                       "$(SRCROOT)/src/ld",
                                );
                                INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
                                OTHER_CPLUSPLUSFLAGS = (
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_TREAT_WARNINGS_AS_ERRORS = NO;
                                ONLY_ACTIVE_ARCH = YES;
+                               SDKROOT = macosx.internal;
                        };
                        name = Debug;
                };
                        buildSettings = {
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_TREAT_WARNINGS_AS_ERRORS = NO;
+                               SDKROOT = macosx.internal;
                        };
                        name = Release;
                };
                        buildSettings = {
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_TREAT_WARNINGS_AS_ERRORS = NO;
+                               SDKROOT = macosx.internal;
                        };
                        name = "Release-assert";
                };
                                GCC_WARN_UNUSED_VARIABLE = YES;
                                HEADER_SEARCH_PATHS = (
                                        "$(DT_TOOLCHAIN_DIR)/usr/local/include",
-                                       "$(DEVELOPER_DIR)/usr/local/include",
-                                       "$(DEVELOPER_DIR)/usr/include",
+                                       "$(TOOLCHAIN_DIR)/usr/local/include",
                                );
                                INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
                                LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/";
                                        "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib",
                                        "@$(DERIVED_FILE_DIR)/linkExtras",
                                        "-Wl,-exported_symbol,__mh_execute_header",
+                                       "-L$(DT_TOOLCHAIN_DIR)/usr/lib",
+                                       "-ltapi",
                                );
                                PREBINDING = NO;
                                PRODUCT_NAME = ld;
                                SECTORDER_FLAGS = "";
                                STRIP_INSTALLED_PRODUCT = YES;
                                STRIP_STYLE = debugging;
+                               TOOLCHAINS = osx;
                                VALID_ARCHS = "x86_64 i386 ppc";
                                VERSIONING_SYSTEM = "apple-generic";
                                WARNING_CFLAGS = "-Wall";
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+                               CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
                                CLANG_CXX_LIBRARY = "libc++";
                                COPY_PHASE_STRIP = YES;
                                DEBUG_INFORMATION_FORMAT = dwarf;
                                GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;
                                GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO;
                                HEADER_SEARCH_PATHS = (
-                                       "$(SRCROOT)/src/ld",
-                                       "$(DEVELOPER_DIR)/usr/local/include",
                                        "$(DT_TOOLCHAIN_DIR)/usr/local/include",
+                                       "$(TOOLCHAIN_DIR)/usr/local/include",
+                                       "$(SRCROOT)/src/ld",
                                );
                                INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
                                OTHER_CPLUSPLUSFLAGS = (
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
+               83046A7F1C8FF23E00024A7E /* Build configuration list for PBXNativeTarget "objcimageinfo" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               83046A801C8FF23E00024A7E /* Debug */,
+                               83046A811C8FF23E00024A7E /* Release */,
+                               83046A821C8FF23E00024A7E /* Release-assert */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = "Release-assert";
+               };
                F933D91B09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ld" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
index cfedc81d3b5c596e11f946c3099218fb4b7744c2..a417877b7e7c5745cbe421a8cd16c8c1ad5b37ba 100644 (file)
        #define CPU_SUBTYPE_ARM64_V8    1
 #endif
 
-
 #define ARM64_RELOC_UNSIGNED            0 // for pointers
 #define ARM64_RELOC_SUBTRACTOR          1 // must be followed by a ARM64_RELOC_UNSIGNED
 #define ARM64_RELOC_BRANCH26            2 // a B/BL instruction with 26-bit displacement
index 9ce654d2ffd9f6e189e683c5a78882256e69e817..3643079339cdac543b9d4f0ce18d42123f4e2a4c 100755 (executable)
@@ -16,8 +16,8 @@ fi
 
 for ANARCH in ${RC_SUPPORTED_ARCHS}
 do
-       KNOWN_ARCHS=",armv4t,armv5,armv6,armv7,armv7f,armv7k,armv7s,armv6m,armv7m,armv7em,armv8,arm64,arm64v8,i386,x86_64,x86_64h,"
-       FOUND=`echo "$KNOWN_ARCHS" | grep ",$ANARCH,"`
+    KNOWN_ARCHS=",armv4t,armv5,armv6,armv7,armv7f,armv7k,armv7s,armv6m,armv7m,armv7em,armv8,arm64,arm64v8,i386,x86_64,x86_64h,"
+    FOUND=`echo "$KNOWN_ARCHS" | grep ",$ANARCH,"`
        if [ $FOUND ]; then
                echo "#define SUPPORT_ARCH_$ANARCH  1" >> ${DERIVED_FILE_DIR}/configure.h
        else
index fdf795b2d8bc4c7af2e14b7e96f4489394d7d88b..eca8c561ed5b5fdc3523b264c9d24e0cf73cef7c 100644 (file)
@@ -61,6 +61,7 @@ struct arm64
        typedef Pointer64<LittleEndian>         P;
 };
 
+
 #endif // __ARCHITECTURES__
 
 
index b47acff9c7d5fcd62a81e4ea6cb5efc20e7a11c4..454eb1f668abeca75965c9bcb86cb8ef5aef3955 100644 (file)
@@ -52,6 +52,9 @@ public:
        virtual const uint8_t* getUUID() const = 0;
        virtual bool bitcodeBundleCommand(uint64_t& cmdOffset, uint64_t& cmdEnd,
                                                                          uint64_t& sectOffset, uint64_t& sectEnd) const = 0;
+       virtual void linkeditCmdInfo(uint64_t& offset, uint64_t& size) const = 0;
+       virtual void symbolTableCmdInfo(uint64_t& offset, uint64_t& size) const = 0;
+
 };
 
 template <typename A>
@@ -74,7 +77,10 @@ public:
        virtual const uint8_t* getUUID() const                          { return &_uuid[0]; }
        virtual bool bitcodeBundleCommand(uint64_t& cmdOffset, uint64_t& cmdEnd,
                                                                          uint64_t& sectOffset, uint64_t& sectEnd) const;
-       
+       virtual void linkeditCmdInfo(uint64_t& offset, uint64_t& size) const;
+       virtual void symbolTableCmdInfo(uint64_t& offset, uint64_t& size) const;
+
+
 private:
        typedef typename A::P                                           P;
        typedef typename A::P::E                                        E;
@@ -91,9 +97,9 @@ private:
        uint32_t                                        commandsCount() const;
        uint32_t                                        threadLoadCommandSize() const;
        uint8_t*                                        copySingleSegmentLoadCommand(uint8_t* p) const;
-       uint8_t*                                        copySegmentLoadCommands(uint8_t* p) const;
+       uint8_t*                                        copySegmentLoadCommands(uint8_t* p, uint8_t* base) const;
        uint8_t*                                        copyDyldInfoLoadCommand(uint8_t* p) const;
-       uint8_t*                                        copySymbolTableLoadCommand(uint8_t* p) const;
+       uint8_t*                                        copySymbolTableLoadCommand(uint8_t* p, uint8_t* base) const;
        uint8_t*                                        copyDynamicSymbolTableLoadCommand(uint8_t* p) const;
        uint8_t*                                        copyDyldLoadCommand(uint8_t* p) const;
        uint8_t*                                        copyDylibIDLoadCommand(uint8_t* p) const;
@@ -150,6 +156,8 @@ private:
        std::vector<const char*>        _subUmbrellaNames;
        uint8_t                                         _uuid[16];
        mutable macho_uuid_command<P>*  _uuidCmdInOutputBuffer;
+       mutable uint32_t                        _linkeditCmdOffset;
+       mutable uint32_t                        _symboltableCmdOffset;
        std::vector< std::vector<const char*> >  _linkerOptions;
        
        static ld::Section                      _s_section;
@@ -169,7 +177,7 @@ HeaderAndLoadCommandsAtom<A>::HeaderAndLoadCommandsAtom(const Options& opts, ld:
                                ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, 
                                ld::Atom::symbolTableNotIn, false, false, false, 
                                (opts.outputKind() == Options::kPreload) ? ld::Atom::Alignment(0) : ld::Atom::Alignment(12) ), 
-               _options(opts), _state(state), _writer(writer), _address(0), _uuidCmdInOutputBuffer(NULL)
+               _options(opts), _state(state), _writer(writer), _address(0), _uuidCmdInOutputBuffer(NULL), _linkeditCmdOffset(0), _symboltableCmdOffset(0)
 {
        bzero(_uuid, 16);
        _hasDyldInfoLoadCommand = opts.makeCompressedDyldInfo();
@@ -201,15 +209,13 @@ HeaderAndLoadCommandsAtom<A>::HeaderAndLoadCommandsAtom(const Options& opts, ld:
                                        break;
                                }
                        }
-                       for (CStringSet::const_iterator it = _state.linkerOptionFrameworks.begin(); it != _state.linkerOptionFrameworks.end(); ++it) {
-                               const char* frameWorkName = *it;
+                       for (const char* frameworkName : _state.unprocessedLinkerOptionFrameworks) {
                                std::vector<const char*>* lo = new std::vector<const char*>();
                                lo->push_back("-framework");
-                               lo->push_back(frameWorkName);
+                               lo->push_back(frameworkName);
                                _linkerOptions.push_back(*lo);
                        };
-                       for (CStringSet::const_iterator it = _state.linkerOptionLibraries.begin(); it != _state.linkerOptionLibraries.end(); ++it) {
-                               const char* libName = *it;
+                       for (const char* libName : _state.unprocessedLinkerOptionLibraries) {
                                std::vector<const char*>* lo = new std::vector<const char*>();
                                char * s = new char[strlen(libName)+3];
                                strcpy(s, "-l");
@@ -357,6 +363,21 @@ bool HeaderAndLoadCommandsAtom<A>::bitcodeBundleCommand(uint64_t &cmdOffset, uin
        return false;
 }
 
+template <typename A>
+void HeaderAndLoadCommandsAtom<A>::linkeditCmdInfo(uint64_t &offset, uint64_t &size) const
+{
+       offset = _linkeditCmdOffset;
+       size = sizeof(macho_segment_command<P>);
+}
+
+template <typename A>
+void HeaderAndLoadCommandsAtom<A>::symbolTableCmdInfo(uint64_t &offset, uint64_t &size) const
+{
+       offset = _symboltableCmdOffset;
+       size = sizeof(macho_symtab_command<P>);
+}
+
+
 template <typename A>
 uint64_t HeaderAndLoadCommandsAtom<A>::size() const
 {
@@ -635,7 +656,6 @@ template <> uint32_t HeaderAndLoadCommandsAtom<arm>::cpuType() const        { return CP
 template <> uint32_t HeaderAndLoadCommandsAtom<arm64>::cpuType() const { return CPU_TYPE_ARM64; }
 
 
-
 template <>
 uint32_t HeaderAndLoadCommandsAtom<x86>::cpuSubType() const
 {
@@ -894,7 +914,7 @@ bool HeaderAndLoadCommandsAtom<A>::sectionTakesNoDiskSpace(ld::Internal::FinalSe
 
 
 template <typename A>
-uint8_t* HeaderAndLoadCommandsAtom<A>::copySegmentLoadCommands(uint8_t* p) const
+uint8_t* HeaderAndLoadCommandsAtom<A>::copySegmentLoadCommands(uint8_t* p, uint8_t* base) const
 {
        // group sections into segments
        std::vector<SegInfo> segs;
@@ -954,7 +974,8 @@ uint8_t* HeaderAndLoadCommandsAtom<A>::copySegmentLoadCommands(uint8_t* p) const
                segCmd->set_initprot(si.initProt);
                segCmd->set_nsects(si.nonHiddenSectionCount);
                segCmd->set_flags(si.nonSectCreateSections ? 0 : SG_NORELOC); // FIXME, really should check all References
-
+               if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
+                       _linkeditCmdOffset = p - base;
                p += sizeof(macho_segment_command<P>);
                macho_section<P>* msect = (macho_section<P>*)p;
                for (std::vector<ld::Internal::FinalSection*>::iterator sit = si.sections.begin(); sit != si.sections.end(); ++sit) {
@@ -982,8 +1003,9 @@ uint8_t* HeaderAndLoadCommandsAtom<A>::copySegmentLoadCommands(uint8_t* p) const
 
 
 template <typename A>
-uint8_t* HeaderAndLoadCommandsAtom<A>::copySymbolTableLoadCommand(uint8_t* p) const
+uint8_t* HeaderAndLoadCommandsAtom<A>::copySymbolTableLoadCommand(uint8_t* p, uint8_t* base) const
 {
+       _symboltableCmdOffset = p - base;
        // build LC_SYMTAB command
        macho_symtab_command<P>*   symbolTableCmd = (macho_symtab_command<P>*)p;
        symbolTableCmd->set_cmd(LC_SYMTAB);
@@ -1498,7 +1520,7 @@ void HeaderAndLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
        if ( _options.outputKind() == Options::kObjectFile )
                p = this->copySingleSegmentLoadCommand(p);
        else
-               p = this->copySegmentLoadCommands(p);
+               p = this->copySegmentLoadCommands(p, buffer);
        
        if ( _hasDylibIDLoadCommand )
                p = this->copyDylibIDLoadCommand(p);
@@ -1507,7 +1529,7 @@ void HeaderAndLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
                p = this->copyDyldInfoLoadCommand(p);
                
        if ( _hasSymbolTableLoadCommand )
-               p = this->copySymbolTableLoadCommand(p);
+               p = this->copySymbolTableLoadCommand(p, buffer);
 
        if ( _hasDynamicSymbolTableLoadCommand )
                p = this->copyDynamicSymbolTableLoadCommand(p);
index ab31d4acab07ffe72a8e69cfde6a37cbf064fffc..e8e6bcc81cdf6a658f58429d7359df6ff3324f8f 100644 (file)
@@ -365,6 +365,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib
 
        ld::archive::File* archiveResult = ::archive::parse(p, len, info.path, info.modTime, info.ordinal, archOpts);
        if ( archiveResult != NULL ) {
+       
                OSAtomicAdd64(len, &_totalArchiveSize);
                OSAtomicIncrement32(&_totalArchivesLoaded);
                return archiveResult;
@@ -417,7 +418,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib
        }
 }
 
-void InputFiles::logDylib(ld::File* file, bool indirect)
+void InputFiles::logDylib(ld::File* file, bool indirect, bool speculative)
 {
        if ( _options.traceDylibs() ) {
                const char* fullPath = file->path();
@@ -429,11 +430,19 @@ void InputFiles::logDylib(ld::File* file, bool indirect)
                        // don't log upward dylibs when XBS is computing dependencies
                        logTraceInfo("[Logging for XBS] Used upward dynamic library: %s\n", fullPath);
                }
+               else if ( (dylib != NULL ) && dylib->speculativelyLoaded() ) {
+                       logTraceInfo("[Logging for XBS] Speculatively loaded dynamic library: %s\n", fullPath);
+               }
                else {
-                       if ( indirect ) 
-                               logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath);
-                       else 
+                       if ( indirect ) {
+                               if ( speculative )
+                                       logTraceInfo("[Logging for XBS] Speculatively loaded indirect dynamic library: %s\n", fullPath);
+                               else
+                                       logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath);
+                       }
+                       else {
                                logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath);
+                       }
                }
        }
        
@@ -459,7 +468,7 @@ void InputFiles::logDylib(ld::File* file, bool indirect)
 
 void InputFiles::logArchive(ld::File* file) const
 {
-       if ( _options.traceArchives() && (_archiveFilesLogged.count(file) == 0) ) {
+       if ( (_options.traceArchives() || _options.traceEmitJSON()) && (_archiveFilesLogged.count(file) == 0) ) {
                // <rdar://problem/4947347> LD_TRACE_ARCHIVES should only print out when a .o is actually used from an archive
                _archiveFilesLogged.insert(file);
                const char* fullPath = file->path();
@@ -467,6 +476,9 @@ void InputFiles::logArchive(ld::File* file) const
                if ( realpath(fullPath, realName) != NULL )
                        fullPath = realName;
                logTraceInfo("[Logging for XBS] Used static archive: %s\n", fullPath);
+               
+               std::string archivePath(fullPath);
+               _archiveFilePaths.push_back(archivePath);
        }
 }
 
@@ -505,7 +517,7 @@ void InputFiles::logTraceInfo(const char* format, ...) const
 }
 
 
-ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* fromPath)
+ld::dylib::File* InputFiles::findDylib(const char* installPath, const ld::dylib::File* fromDylib, bool speculative)
 {
        //fprintf(stderr, "findDylib(%s, %s)\n", installPath, fromPath);
        InstallNameToDylib::iterator pos = _installPathToDylibs.find(installPath);
@@ -526,7 +538,7 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from
                                        if ( dylibReader != NULL ) {
                                                addDylib(dylibReader, info);
                                                //_installPathToDylibs[strdup(installPath)] = dylibReader;
-                                               this->logDylib(dylibReader, true);
+                                               this->logDylib(dylibReader, true, speculative);
                                                return dylibReader;
                                        }
                                        else 
@@ -537,20 +549,9 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from
                                }
                        }
                }
-               char newPath[MAXPATHLEN];
-               // handle @loader_path
-               if ( strncmp(installPath, "@loader_path/", 13) == 0 ) {
-                       strcpy(newPath, fromPath);
-                       char* addPoint = strrchr(newPath,'/');
-                       if ( addPoint != NULL )
-                               strcpy(&addPoint[1], &installPath[13]);
-                       else
-                               strcpy(newPath, &installPath[13]);
-                       installPath = newPath;
-               }
-               // note: @executable_path case is handled inside findFileUsingPaths()
-               // search for dylib using -F and -L paths
-               Options::FileInfo info = _options.findFileUsingPaths(installPath);
+
+               // search for dylib using -F and -L paths and expanding @ paths
+               Options::FileInfo info = _options.findIndirectDylib(installPath, fromDylib);
                _indirectDylibOrdinal = _indirectDylibOrdinal.nextIndirectDylibOrdinal();
                info.ordinal = _indirectDylibOrdinal;
                info.options.fIndirectDylib = true;
@@ -561,7 +562,7 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from
                                //assert(_installPathToDylibs.find(installPath) !=  _installPathToDylibs.end());
                                //_installPathToDylibs[strdup(installPath)] = dylibReader;
                                addDylib(dylibReader, info);
-                               this->logDylib(dylibReader, true);
+                               this->logDylib(dylibReader, true, speculative);
                                return dylibReader;
                        }
                        else 
@@ -614,80 +615,96 @@ bool InputFiles::libraryAlreadyLoaded(const char* path)
                        return true;
        }
 
+       char realDylibPath[PATH_MAX];
+       if ( (realpath(path, realDylibPath) != NULL) && (strcmp(path, realDylibPath) != 0) ) {
+               return libraryAlreadyLoaded(realDylibPath);
+       }
+
        return false;
 }
 
 
 void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHandler& handler)
 {      
-    if ( _options.outputKind() == Options::kObjectFile ) 
-               return;
-
-       // process frameworks specified in .o linker options
-       for (CStringSet::const_iterator it = state.linkerOptionFrameworks.begin(); it != state.linkerOptionFrameworks.end(); ++it) {
-               const char* frameworkName = *it;
-               if ( state.linkerOptionFrameworksProcessed.count(frameworkName) )
-                       continue;
-               Options::FileInfo info = _options.findFramework(frameworkName);
-               if ( ! this->frameworkAlreadyLoaded(info.path, frameworkName) ) {
-                       info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal();
-                       try {
-                               ld::File* reader = this->makeFile(info, true);
-                               ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(reader);
-                               if ( dylibReader != NULL ) {
-                                       if ( ! dylibReader->installPathVersionSpecific() ) {
+       if ( _options.outputKind() == Options::kObjectFile )
+               return;
+  
+       while (! state.unprocessedLinkerOptionLibraries.empty() || ! state.unprocessedLinkerOptionFrameworks.empty()) {
+
+               // process frameworks specified in .o linker options
+               CStringSet newFrameworks = std::move(state.unprocessedLinkerOptionFrameworks);
+               state.unprocessedLinkerOptionFrameworks.clear();
+               for (const char* frameworkName : newFrameworks) {
+                       if ( state.linkerOptionFrameworks.count(frameworkName) )
+                               continue;
+                       Options::FileInfo info = _options.findFramework(frameworkName);
+                       if ( ! this->frameworkAlreadyLoaded(info.path, frameworkName) ) {
+                               _linkerOptionOrdinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal();
+                               info.ordinal = _linkerOptionOrdinal;
+                               try {
+                                       ld::File* reader = this->makeFile(info, true);
+                                       ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(reader);
+                                       if ( dylibReader != NULL ) {
+                                               if ( ! dylibReader->installPathVersionSpecific() ) {
+                                                       dylibReader->forEachAtom(handler);
+                                                       dylibReader->setImplicitlyLinked();
+                                                       dylibReader->setSpeculativelyLoaded();
+                                                       this->addDylib(dylibReader, info);
+                                               }
+                                       }
+                                       else {
+                                               throwf("framework linker option at %s is not a dylib", info.path);
+                                       }
+                               }
+                               catch (const char* msg) {
+                                       warning("Auto-Linking supplied '%s', %s", info.path, msg);
+                               }
+                       }
+                       state.linkerOptionFrameworks.insert(frameworkName);
+               }
+
+               // process libraries specified in .o linker options
+               // fixme optimize with std::move?
+               CStringSet newLibraries = std::move(state.unprocessedLinkerOptionLibraries);
+               state.unprocessedLinkerOptionLibraries.clear();
+               for (const char* libName : newLibraries) {
+                       if ( state.linkerOptionLibraries.count(libName) )
+                               continue;
+                       Options::FileInfo info = _options.findLibrary(libName);
+                       if ( ! this->libraryAlreadyLoaded(info.path) ) {
+                               _linkerOptionOrdinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal();
+                               info.ordinal = _linkerOptionOrdinal;
+                               try {
+                                       //<rdar://problem/17787306> -force_load_swift_libs
+                                       info.options.fForceLoad = _options.forceLoadSwiftLibs() && (strncmp(libName, "swift", 5) == 0);
+                                       ld::File* reader = this->makeFile(info, true);
+                                       ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(reader);
+                                       ld::archive::File* archiveReader = dynamic_cast<ld::archive::File*>(reader);
+                                       if ( dylibReader != NULL ) {
                                                dylibReader->forEachAtom(handler);
                                                dylibReader->setImplicitlyLinked();
+                                               dylibReader->setSpeculativelyLoaded();
                                                this->addDylib(dylibReader, info);
                                        }
-                               }
-                               else {
-                                       throwf("framework linker option at %s is not a dylib", info.path);
-                               }
-                       }
-                       catch (const char* msg) {
-                               warning("Auto-Linking supplied '%s', %s", info.path, msg);
-                       }
-               }
-               state.linkerOptionFrameworksProcessed.insert(frameworkName);
-       }
-       // process libraries specified in .o linker options
-       for (CStringSet::const_iterator it = state.linkerOptionLibraries.begin(); it != state.linkerOptionLibraries.end(); ++it) {
-               const char* libName = *it;
-               if ( state.linkerOptionLibrariesProcessed.count(libName) )
-                       continue;
-               Options::FileInfo info = _options.findLibrary(libName);
-               if ( ! this->libraryAlreadyLoaded(info.path) ) {
-                       info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal();
-                       try {
-                               //<rdar://problem/17787306> -force_load_swift_libs
-                               info.options.fForceLoad = _options.forceLoadSwiftLibs() && (strncmp(libName, "swift", 5) == 0);
-                               ld::File* reader = this->makeFile(info, true);
-                               ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(reader);
-                               ld::archive::File* archiveReader = dynamic_cast<ld::archive::File*>(reader);
-                               if ( dylibReader != NULL ) {
-                                       dylibReader->forEachAtom(handler);
-                                       dylibReader->setImplicitlyLinked();
-                                       this->addDylib(dylibReader, info);
-                               }
-                               else if ( archiveReader != NULL ) {
-                                       _searchLibraries.push_back(LibraryInfo(archiveReader));
-                                       if ( _options.dumpDependencyInfo() )
-                                               _options.dumpDependency(Options::depArchive, archiveReader->path());
-                                       //<rdar://problem/17787306> -force_load_swift_libs
-                                       if (info.options.fForceLoad) {
-                                               archiveReader->forEachAtom(handler);
+                                       else if ( archiveReader != NULL ) {
+                                               _searchLibraries.push_back(LibraryInfo(archiveReader));
+                                               if ( _options.dumpDependencyInfo() )
+                                                       _options.dumpDependency(Options::depArchive, archiveReader->path());
+                                               //<rdar://problem/17787306> -force_load_swift_libs
+                                               if (info.options.fForceLoad) {
+                                                       archiveReader->forEachAtom(handler);
+                                               }
                                        }
-                               }
-                               else {
-                                       throwf("linker option dylib at %s is not a dylib", info.path);
-                               }
-                       }
-                       catch (const char* msg) {
-                               warning("Auto-Linking supplied '%s', %s", info.path, msg);
-                       }
+                                       else {
+                                               throwf("linker option dylib at %s is not a dylib", info.path);
+                                       }
+                               }
+                               catch (const char* msg) {
+                                       warning("Auto-Linking supplied '%s', %s", info.path, msg);
+                               }
+                       }
+                       state.linkerOptionLibraries.insert(libName);
                }
-               state.linkerOptionLibrariesProcessed.insert(libName);
        }
 }
 
@@ -889,6 +906,7 @@ InputFiles::InputFiles(Options& opts, const char** archName)
        pthread_mutex_init(&_parseLock, NULL);
        pthread_cond_init(&_parseWorkReady, NULL);
        pthread_cond_init(&_newFileAvailable, NULL);
+       _neededFileSlot = -1;
 #endif
        const std::vector<Options::FileInfo>& files = _options.getInputFiles();
        if ( files.size() == 0 )
@@ -986,7 +1004,7 @@ void InputFiles::parseWorkerThread() {
                        if (_s_logPThreads) printf("parsing index %u\n", slot);
                        try {
                                file = makeFile(entry, false);
-                       } 
+                       }
                        catch (const char *msg) {
                                if ( (strstr(msg, "architecture") != NULL) && !_options.errorOnOtherArchFiles() ) {
                                        if ( _options.ignoreOtherArchInputFiles() ) {
@@ -1086,7 +1104,7 @@ ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo&
 
        // log direct readers
        if ( ! info.options.fIndirectDylib ) 
-               this->logDylib(reader, false);
+               this->logDylib(reader, false, false);
 
        // update stats
        _totalDylibsLoaded++;
@@ -1212,7 +1230,7 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler, ld::Internal
                        {
                                ld::archive::File* archive = (ld::archive::File*)file;
                                // <rdar://problem/9740166> force loaded archives should be in LD_TRACE
-                               if ( (info.options.fForceLoad || _options.fullyLoadArchives()) && _options.traceArchives() ) 
+                               if ( (info.options.fForceLoad || _options.fullyLoadArchives()) && (_options.traceArchives() || _options.traceEmitJSON()) )
                                        logArchive(archive);
                                _searchLibraries.push_back(LibraryInfo(archive));
                                if ( _options.dumpDependencyInfo() )
@@ -1309,16 +1327,17 @@ bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searc
                 ld::archive::File *archiveFile = lib.archive();
                 if ( dataSymbolOnly ) {
                     if ( archiveFile->justInTimeDataOnlyforEachAtom(name, handler) ) {
-                        if ( _options.traceArchives() 
+                        if ( _options.traceArchives() || _options.traceEmitJSON())
                             logArchive(archiveFile);
                         _options.snapshot().recordArchive(archiveFile->path());
+                                               // DALLAS _state.archives.push_back(archiveFile);
                         // found data definition in static library, done
                        return true;
                     }
                 }
                 else {
                     if ( archiveFile->justInTimeforEachAtom(name, handler) ) {
-                        if ( _options.traceArchives() 
+                        if ( _options.traceArchives() || _options.traceEmitJSON())
                             logArchive(archiveFile);
                         _options.snapshot().recordArchive(archiveFile->path());
                         // found definition in static library, done
@@ -1433,6 +1452,18 @@ void InputFiles::dylibs(ld::Internal& state)
                }
                // <rdar://problem/15002251> make implicit dylib order be deterministic by sorting by install_name
                std::sort(implicitDylibs.begin(), implicitDylibs.end(), DylibByInstallNameSorter());
+
+               if ( _options.traceDylibs() ) {
+                       for (ld::dylib::File* dylib :  implicitDylibs) {
+                               if ( dylib->speculativelyLoaded() && !dylib->explicitlyLinked() && dylib->providedExportAtom() ) {
+                                       const char* fullPath = dylib->path();
+                                       char realName[MAXPATHLEN];
+                                       if ( realpath(fullPath, realName) != NULL )
+                                               fullPath = realName;
+                                       logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath);
+                               }
+                       }
+               }
                state.dylibs.insert(state.dylibs.end(), implicitDylibs.begin(), implicitDylibs.end());
        }
 
@@ -1450,6 +1481,14 @@ void InputFiles::dylibs(ld::Internal& state)
                throw "dynamic main executables must link with libSystem.dylib";
 }
 
+void InputFiles::archives(ld::Internal& state)
+{
+       for (const std::string path :  _archiveFilePaths) {
+               
+               state.archivePaths.push_back(path);
+       }
+}
+
 
 } // namespace tool 
 } // namespace ld 
index e9927cd689346935098e5442ce0153add64bef21..21f878a513b3046e5e3258a6d804ff5187d56593 100644 (file)
@@ -60,7 +60,7 @@ public:
                                                                InputFiles(Options& opts, const char** archName);
 
        // implementation from ld::dylib::File::DylibHandler
-       virtual ld::dylib::File*        findDylib(const char* installPath, const char* fromPath);
+       virtual ld::dylib::File*        findDylib(const char* installPath, const ld::dylib::File* fromDylib, bool speculative);
        
        // iterates all atoms in initial files
        void                                            forEachInitialAtom(ld::File::AtomHandler&, ld::Internal& state);
@@ -72,6 +72,8 @@ public:
        // copy dylibs to link with in command line order
        void                                            dylibs(ld::Internal& state);
        
+       void                                            archives(ld::Internal& state);
+       
        bool                                            inferredArch() const { return _inferredArch; }
        
        void                                            addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHandler& handler);
@@ -91,7 +93,7 @@ private:
        ld::File*                                       makeFile(const Options::FileInfo& info, bool indirectDylib);
        ld::File*                                       addDylib(ld::dylib::File* f,        const Options::FileInfo& info);
        void                                            logTraceInfo (const char* format, ...) const;
-       void                                            logDylib(ld::File*, bool indirect);
+       void                                            logDylib(ld::File*, bool indirect, bool speculative);
        void                                            logArchive(ld::File*) const;
        void                                            markExplicitlyLinkedDylibs();
        void                                            checkDylibClientRestrictions(ld::dylib::File*);
@@ -113,6 +115,7 @@ private:
        const Options&                          _options;
        std::vector<ld::File*>          _inputFiles;
        mutable std::set<class ld::File*>       _archiveFilesLogged;
+       mutable std::vector<std::string>        _archiveFilePaths;
        InstallNameToDylib                      _installPathToDylibs;
        std::set<ld::dylib::File*>      _allDylibs;
        ld::dylib::File*                        _bundleLoader;
index 2eab13bfe85aa54fa8c1f3877c3aa12c0d3fbda5..ce7c8201ec69ea5756ca7c69a21fc7d7e2107899 100644 (file)
@@ -669,39 +669,18 @@ bool SymbolTableAtom<A>::hasStabs(uint32_t& ssos, uint32_t& ssoe, uint32_t& sos,
 template <typename A>
 void SymbolTableAtom<A>::encode()
 {
-       uint32_t symbolIndex = 0;
+       // Note: We lay out the symbol table so that the strings for the stabs (local) symbols are at the
+       // end of the string pool.  The stabs strings are not used when calculated the UUID for the image.
+       // If the stabs strings were not last, the string offsets for all other symbols may very which would alter the UUID.
 
-       // make nlist entries for all local symbols
-       std::vector<const ld::Atom*>& localAtoms = this->_writer._localAtoms;
-       std::vector<const ld::Atom*>& globalAtoms = this->_writer._exportedAtoms;
-       _locals.reserve(localAtoms.size()+this->_state.stabs.size());
-       this->_writer._localSymbolsStartIndex = 0;
-       // make nlist entries for all debug notes
-       _stabsIndexStart = symbolIndex;
-       _stabsStringsOffsetStart = this->_writer._stringPoolAtom->currentOffset();
-       for (std::vector<ld::relocatable::File::Stab>::const_iterator sit=this->_state.stabs.begin(); sit != this->_state.stabs.end(); ++sit) {
-               macho_nlist<P> entry;
-               entry.set_n_type(sit->type);
-               entry.set_n_sect(sectionIndexForStab(*sit));
-               entry.set_n_desc(sit->desc);
-               entry.set_n_value(valueForStab(*sit));
-               entry.set_n_strx(stringOffsetForStab(*sit, this->_writer._stringPoolAtom));
-               _locals.push_back(entry);
-               ++symbolIndex;
-       }
-       _stabsIndexEnd = symbolIndex;
-       _stabsStringsOffsetEnd = this->_writer._stringPoolAtom->currentOffset();
-       for (std::vector<const ld::Atom*>::const_iterator it=localAtoms.begin(); it != localAtoms.end(); ++it) {
-               const ld::Atom* atom = *it;
-               if ( this->addLocal(atom, this->_writer._stringPoolAtom) )
-                       this->_writer._atomToSymbolIndex[atom] = symbolIndex++;
-       }
-       this->_writer._localSymbolsCount = symbolIndex;
-       
+       // reserve space for local symbols
+       uint32_t localsCount = _state.stabs.size() + this->_writer._localAtoms.size();
 
        // make nlist entries for all global symbols
+       std::vector<const ld::Atom*>& globalAtoms = this->_writer._exportedAtoms;
        _globals.reserve(globalAtoms.size());
-       this->_writer._globalSymbolsStartIndex = symbolIndex;
+       uint32_t symbolIndex = localsCount;
+       this->_writer._globalSymbolsStartIndex = localsCount;
        for (std::vector<const ld::Atom*>::const_iterator it=globalAtoms.begin(); it != globalAtoms.end(); ++it) {
                const ld::Atom* atom = *it;
                this->addGlobal(atom, this->_writer._stringPoolAtom);
@@ -718,6 +697,31 @@ void SymbolTableAtom<A>::encode()
                this->_writer._atomToSymbolIndex[*it] = symbolIndex++;
        }
        this->_writer._importSymbolsCount = symbolIndex - this->_writer._importSymbolsStartIndex;
+
+       // go back to start and make nlist entries for all local symbols
+       std::vector<const ld::Atom*>& localAtoms = this->_writer._localAtoms;
+       _locals.reserve(localsCount);
+       symbolIndex = 0;
+       this->_writer._localSymbolsStartIndex = 0;
+       _stabsIndexStart = 0;
+       _stabsStringsOffsetStart = this->_writer._stringPoolAtom->currentOffset();
+       for (const ld::relocatable::File::Stab& stab : _state.stabs) {
+               macho_nlist<P> entry;
+               entry.set_n_type(stab.type);
+               entry.set_n_sect(sectionIndexForStab(stab));
+               entry.set_n_desc(stab.desc);
+               entry.set_n_value(valueForStab(stab));
+               entry.set_n_strx(stringOffsetForStab(stab, this->_writer._stringPoolAtom));
+               _locals.push_back(entry);
+               ++symbolIndex;
+       }
+       _stabsIndexEnd = symbolIndex;
+       _stabsStringsOffsetEnd = this->_writer._stringPoolAtom->currentOffset();
+       for (const ld::Atom* atom : localAtoms) {
+               if ( this->addLocal(atom, this->_writer._stringPoolAtom) )
+                       this->_writer._atomToSymbolIndex[atom] = symbolIndex++;
+       }
+       this->_writer._localSymbolsCount = symbolIndex;
 }
 
 template <typename A>
index 0f5985de5e3ea9832a0d3747c0a3d5144b83468e..faf9c169878445d50fdeabde1f4039a12a41bb75 100644 (file)
@@ -34,6 +34,7 @@
 #include <spawn.h>
 #include <cxxabi.h>
 #include <Availability.h>
+#include <tapi/tapi.h>
 
 #include <vector>
 #include <map>
@@ -125,6 +126,7 @@ bool Options::FileInfo::checkFileExists(const Options& options, const char *p)
        }
        if ( options.dumpDependencyInfo() )
                options.dumpDependency(Options::depNotFound, p);
+//     fprintf(stderr, "not found: %s\n", p);
     return false;
 }
 
@@ -165,7 +167,7 @@ Options::Options(int argc, const char* argv[])
          fSetuidSafe(false), fImplicitlyLinkPublicDylibs(true), fAddCompactUnwindEncoding(true),
          fWarnCompactUnwind(false), fRemoveDwarfUnwindIfCompactExists(false),
          fAutoOrderInitializers(true), fOptimizeZeroFill(true), fMergeZeroFill(false), fLogObjectFiles(false),
-         fLogAllFiles(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false),
+         fLogAllFiles(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), fTraceEmitJSON(false),
          fOutputSlidable(false), fWarnWeakExports(false), 
          fObjcGcCompaction(false), fObjCGc(false), fObjCGcOnly(false), 
          fDemangle(false), fTLVSupport(false), 
@@ -185,16 +187,15 @@ Options::Options(int argc, const char* argv[])
          fGenerateDtraceDOF(true), fAllowBranchIslands(true), fTraceSymbolLayout(false), 
          fMarkAppExtensionSafe(false), fCheckAppExtensionSafe(false), fForceLoadSwiftLibs(false),
          fSharedRegionEncodingV2(false), fUseDataConstSegment(false),
-         fUseDataConstSegmentForceOn(false), fUseDataConstSegmentForceOff(false),
+         fUseDataConstSegmentForceOn(false), fUseDataConstSegmentForceOff(false), fUseTextExecSegment(false),
          fBundleBitcode(false), fHideSymbols(false), fVerifyBitcode(false),
          fReverseMapUUIDRename(false), fDeDupe(true), fVerboseDeDupe(false),
          fReverseMapPath(NULL), fLTOCodegenOnly(false),
-         fIgnoreAutoLink(false), fAllowDeadDups(false), fBitcodeKind(kBitcodeProcess),
+         fIgnoreAutoLink(false), fAllowDeadDups(false), fAllowWeakImports(true), fBitcodeKind(kBitcodeProcess),
          fPlatform(kPlatformUnknown), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL),
-         fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset),
+         fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), fWatchOSVersionMin(ld::wOSVersionUnset),
          fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL),
-         fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1), fMaxDefaultCommonAlign(0), fFilePreference(kModTime),
-         fForceTextBasedStub(false)
+         fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1), fMaxDefaultCommonAlign(0)
 {
        this->checkForClassic(argc, argv);
        this->parsePreCommandLineEnvironmentSettings();
@@ -320,12 +321,15 @@ uint32_t Options::initialSegProtection(const char* segName) const
                        return it->init;
                }
        }
-       if ( strcmp(segName, "__PAGEZERO") == 0 ) {
-               return 0;
+       if ( strcmp(segName, "__TEXT") == 0 ) {
+               return ( fUseTextExecSegment ? VM_PROT_READ : (VM_PROT_READ | VM_PROT_EXECUTE) );
        }
-       else if ( strcmp(segName, "__TEXT") == 0 ) {
+       else if ( strcmp(segName, "__TEXT_EXEC") == 0 ) {
                return VM_PROT_READ | VM_PROT_EXECUTE;
        }
+       else if ( strcmp(segName, "__PAGEZERO") == 0 ) {
+               return 0;
+       }
        else if ( strcmp(segName, "__LINKEDIT") == 0 ) {
                return VM_PROT_READ;
        }
@@ -392,6 +396,9 @@ bool Options::hasCustomSectionAlignment(const char* segName, const char* sectNam
                if ( (strcmp(it->segmentName, segName) == 0) && (strcmp(it->sectionName, sectName) == 0) )
                        return true;
        }
+       if ( fEncryptable && (strcmp(sectName, "__oslogstring") == 0) && (strcmp(segName, "__TEXT") == 0) )
+               return true;
+
        return false;
 }
 
@@ -401,6 +408,9 @@ uint8_t Options::customSectionAlignment(const char* segName, const char* sectNam
                if ( (strcmp(it->segmentName, segName) == 0) && (strcmp(it->sectionName, sectName) == 0) )
                        return it->alignment;
        }
+       if ( fEncryptable && (strcmp(sectName, "__oslogstring") == 0) && (strcmp(segName, "__TEXT") == 0) )
+               return __builtin_ctz(fSegmentAlignment);
+
        return 0;
 }
 
@@ -605,7 +615,7 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype, Options::P
                        switch ( type ) {
                                case CPU_TYPE_I386:
                                case CPU_TYPE_X86_64:
-                                       if ( (fPlatform == kPlatformOSX) && (fOutputKind != Options::kObjectFile) ) {
+                                       if ( (fPlatform == kPlatformOSX) && (fOutputKind != Options::kObjectFile) && (fMacVersionMin == ld::macVersionUnset) ) {
                                #ifdef DEFAULT_MACOSX_MIN_VERSION
                                                warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION);
                                                setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION);
@@ -617,7 +627,7 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype, Options::P
                                        break;
                                case CPU_TYPE_ARM:
                                case CPU_TYPE_ARM64:
-                                       if ( (fPlatform == kPlatformiOS) && (fOutputKind != Options::kObjectFile) ) {
+                                       if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) {
                                #if defined(DEFAULT_IPHONEOS_MIN_VERSION)
                                                warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION);
                                                setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION);
@@ -817,52 +827,67 @@ bool Options::findFile(const std::string &path, const std::vector<std::string> &
                bool found = tbdInfo.checkFileExists(*this, newPath.c_str());
                if ( fTraceDylibSearching )
                        printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), newPath.c_str());
-               if ( found ) {
-                       if ( (fFilePreference == kTextBasedStub) || fForceTextBasedStub ) {
-                               result = tbdInfo;
-                               return true;
-                       } else {
-                               break;
-                       }
-               }
+               if ( found )
+                       break;
        }
 
+       // If we found a text-based stub file, check if it should be used.
+       if ( !tbdInfo.missing() ) {
+               if (tapi::LinkerInterfaceFile::shouldPreferTextBasedStubFile(tbdInfo.path)) {
+                       result = tbdInfo;
+                       return true;
+               }
+       }
        FileInfo dylibInfo;
        {
                bool found = dylibInfo.checkFileExists(*this, path.c_str());
                if ( fTraceDylibSearching )
                        printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), path.c_str());
-               if ( found &&  (fFilePreference == kMachO) ) {
-                       result = dylibInfo;
-                       return true;
-               }
        }
 
-       if ( !dylibInfo.missing() && tbdInfo.missing() ) {
-               result = dylibInfo;
+       // There is only a text-based stub file.
+       if ( !tbdInfo.missing() && dylibInfo.missing() ) {
+               result = tbdInfo;
                return true;
        }
-       else if ( dylibInfo.missing() && !tbdInfo.missing() ) {
-               result = tbdInfo;
+       // There is only a dynamic library file.
+       else if ( tbdInfo.missing() && !dylibInfo.missing() ) {
+               result = dylibInfo;
                return true;
        }
-       else if ( !dylibInfo.missing() && !tbdInfo.missing() ) {
-               if ( dylibInfo.modTime == tbdInfo.modTime ) {
+       // There are both - a text-based stub file and a dynamic library file.
+       else if ( !tbdInfo.missing() && !dylibInfo.missing() ) {
+               // If the files are still in synv we can use and should use the text-based stub file.
+               if (tapi::LinkerInterfaceFile::areEquivalent(tbdInfo.path, dylibInfo.path)) {
                        result = tbdInfo;
-                       return true;
                }
+               // Otherwise issue a warning and fall-back to the dynamic library file.
                else {
-                       // <rdar://problem/24459693> Disable false warning about out-of-sync .tbd files.
-                       //warning("text-based stub file %s and library file %s are out of sync. Falling back to library file for linking.", tbdInfo.path, dylibInfo.path);
+                       warning("text-based stub file %s and library file %s are out of sync. Falling back to library file for linking.", tbdInfo.path, dylibInfo.path);
                        result = dylibInfo;
-                       return true;
+
                }
+               return true;
        }
 
        return false;
 }
 
-Options::FileInfo Options::findFile(const std::string &path) const
+static bool startsWith(const std::string& str, const std::string& prefix)
+{
+       return (str.compare(0, prefix.length(), prefix) == 0);
+}
+
+static std::string getDirPath(const std::string& path)
+{
+       std::string::size_type lastSlashPos = path.find_last_of('/');
+       if ( lastSlashPos == std::string::npos )
+               return "./";
+       else
+               return path.substr(0, lastSlashPos+1);
+}
+
+Options::FileInfo Options::findFile(const std::string &path, const ld::dylib::File* fromDylib) const
 {
        FileInfo result;
 
@@ -874,35 +899,59 @@ Options::FileInfo Options::findFile(const std::string &path) const
                                return result;
                }
        }
+
+       // expand @ variables
+       if ( path[0] == '@' ) {
+               if ( startsWith(path, "@executable_path/") && (fExecutablePath != nullptr) ) {
+                       std::string exeBasedPath = getDirPath(fExecutablePath) + &path[17];
+                       if ( findFile(exeBasedPath, {".tbd"}, result) )
+                               return result;
+               }
+               else if ( startsWith(path, "@loader_path/") && (fromDylib != nullptr) ) {
+                       char absPath[PATH_MAX];
+                       if ( realpath(fromDylib->path(), absPath) != NULL ) {
+                               std::string loaderBasedPath = getDirPath(fromDylib->path()) + &path[13];
+                               if ( findFile(loaderBasedPath, {".tbd"}, result) )
+                                       return result;
+                       }
+               }
+               else if ( startsWith(path, "@rpath/") ) {
+                       // first search any LC_RPATH supplied by dyld that re-exports dylib to be found
+                       if ( fromDylib != nullptr ) {
+                               for (const char* rp : fromDylib->rpaths() ) {
+                                       std::string rpath = rp;
+                                       // handle dylib that has LC_RPATH = @loader_path/blah
+                                       if ( startsWith(rpath, "@loader_path/") ) {
+                                               char absPath[PATH_MAX];
+                                               if ( realpath(fromDylib->path(), absPath) != NULL )
+                                                       rpath = getDirPath(absPath) + &rpath[13];
+                                               else
+                                                       rpath = getDirPath(fromDylib->path()) + &rpath[13];
+                                       }
+                                       std::string rpathBasedPath = rpath + "/" + &path[6];
+                                       if ( findFile(rpathBasedPath, {".tbd"}, result) )
+                                               return result;
+                               }
+                       }
+               }
+       }
+
        // try raw path
        if ( findFile(path, {".tbd"}, result) )
                return result;
 
-       // try @executable_path substitution
-       if ( (path.find("@executable_path/") == 0) && (fExecutablePath != nullptr) ) {
-               char newPath[strlen(fExecutablePath) + path.size()];
-               strcpy(newPath, fExecutablePath);
-               char* addPoint = strrchr(newPath,'/');
-               if ( addPoint != nullptr )
-                       strcpy(&addPoint[1], &path[17]);
-               else
-                       strcpy(newPath, &path[17]);
-
-               if ( findFile(newPath, {".tbd"}, result) )
-                       return result;
-       }
-
        // not found
        throwf("file not found: %s", path.c_str());
 }
 
-Options::FileInfo Options::findFileUsingPaths(const std::string &path) const
+// search for indirect dylib first using -F and -L paths first
+Options::FileInfo Options::findIndirectDylib(const std::string& installName, const ld::dylib::File* fromDylib) const
 {
        FileInfo result;
 
-       auto lastSlashPos = path.find_last_of('/');
+       auto lastSlashPos = installName.find_last_of('/');
        auto pos = ( lastSlashPos != std::string::npos ) ? lastSlashPos + 1 : 0;
-       auto leafName = path.substr(pos);
+       auto leafName = installName.substr(pos);
 
        // Is this in a framework?
        // /path/Foo.framework/Foo                                                      ==> true (Foo)
@@ -911,7 +960,7 @@ Options::FileInfo Options::findFileUsingPaths(const std::string &path) const
        bool isFramework = false;
        if ( lastSlashPos != std::string::npos ) {
                auto frameworkDir = std::string("/").append(leafName).append(".framework/");
-               if ( path.rfind(frameworkDir) != std::string::npos )
+               if ( installName.rfind(frameworkDir) != std::string::npos )
                        isFramework = true;
        }
        
@@ -920,9 +969,9 @@ Options::FileInfo Options::findFileUsingPaths(const std::string &path) const
        // don't need to try variations, just paths. We do need to add the additional bits
        // onto the framework path though.
        if ( isFramework ) {
-               auto endPos = path.rfind(".framework");
-               auto beginPos = path.find_last_of('/', endPos);
-               auto leafPath = path.substr(beginPos);
+               auto endPos = installName.rfind(".framework");
+               auto beginPos = installName.find_last_of('/', endPos);
+               auto leafPath = installName.substr(beginPos);
                for (const auto* dir : fFrameworkSearchPaths) {
                        auto possiblePath = dir + leafPath;
                        if ( findFile(possiblePath, {".tbd"}, result) )
@@ -933,7 +982,7 @@ Options::FileInfo Options::findFileUsingPaths(const std::string &path) const
                // <rdar://problem/5427952> ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard
                bool embeddedDylib = ( (leafName.size() > 6)
                                        && (leafName.find(".dylib", leafName.size()-6) != std::string::npos)
-                                       && (path.find(".framework/") != std::string::npos) );
+                                       && (installName.find(".framework/") != std::string::npos) );
                if ( !embeddedDylib ) {
                        for (const auto* dir : fLibrarySearchPaths) {
                                //fprintf(stderr,"Finding Library: %s/%s\n", dir, leafName);
@@ -945,7 +994,7 @@ Options::FileInfo Options::findFileUsingPaths(const std::string &path) const
        }
 
        // If we didn't find it fall back to findFile.
-       return findFile(path);
+       return findFile(installName, fromDylib);
 }
 
 
@@ -1716,6 +1765,19 @@ void Options::parseOrderFile(const char* path, bool cstring)
        // order files override auto-ordering
        fAutoOrderInitializers = false;
 
+       // <rdar://problem/24856050> ld64 should prefer OrderFiles from the SDK over the ones in /
+       for (const char* sdkPath : fSDKPaths) {
+               char fullPath[PATH_MAX];
+               strlcpy(fullPath, sdkPath, PATH_MAX);
+               strlcat(fullPath, "/", PATH_MAX);
+               strlcat(fullPath, path, PATH_MAX);
+               struct stat statBuffer;
+               if ( stat(fullPath, &statBuffer) == 0 ) {
+                       path = strdup(fullPath);
+                       break;
+               }
+       }
+
        // read in whole file
        int fd = ::open(path, O_RDONLY, 0);
        if ( fd == -1 )
@@ -2357,6 +2419,40 @@ void Options::parse(int argc, const char* argv[])
                                if ( fOverridePathlibLTO == NULL )
                                        throw "missing argument to -lto_library";
                        }
+                       else if ( strcmp(arg, "-cache_path_lto") == 0 ) {
+                               fLtoCachePath = argv[++i];
+                               if ( fLtoCachePath == NULL )
+                                       throw "missing argument to -cache_path_lto";
+                       }
+                       else if ( strcmp(arg, "-prune_interval_lto") == 0 ) {
+                               const char* value = argv[++i];
+                               if ( value == NULL )
+                                       throw "missing argument to -prune_interval_lto";
+                               char* endptr;
+                               fLtoPruneInterval = strtoul(value, &endptr, 10);
+                               if ( *endptr != '\0')
+                                       throw "invalid argument for -prune_interval_lto";
+                       }
+                       else if ( strcmp(arg, "-prune_after_lto") == 0 ) {
+                               const char* value = argv[++i];
+                               if ( value == NULL )
+                                       throw "missing argument to -prune_after_lto";
+                               char* endptr;
+                               fLtoPruneAfter = strtoul(value, &endptr, 10);
+                               if ( *endptr != '\0')
+                                       throw "invalid argument for -prune_after_lto";
+                       }
+                       else if ( strcmp(arg, "-max_relative_cache_size_lto") == 0 ) {
+                               const char* value = argv[++i];
+                               if ( value == NULL )
+                                       throw "missing argument to -max_relative_cache_size_lto";
+                               char* endptr;
+                               fLtoMaxCacheSize = strtoul(value, &endptr, 10);
+                               if ( *endptr != '\0')
+                                       throw "invalid argument for -max_relative_cache_size_lto";
+                               if (fLtoMaxCacheSize > 100)
+                                       throw "Expect a value between 0 and 100 for -max_relative_cache_size_lto";
+                       }
                        else if ( (arg[1] == 'l') && (strncmp(arg,"-lazy_",6) !=0) ) {
                 snapshotArgCount = 0;
                                FileInfo info = findLibrary(&arg[2]);
@@ -3717,6 +3813,14 @@ void Options::parse(int argc, const char* argv[])
                                fUseDataConstSegmentForceOff = true;
                                cannotBeUsedWithBitcode(arg);
                        }
+                       else if ( strcmp(arg, "-text_exec") == 0 ) {
+                               fUseTextExecSegment = true;
+                               cannotBeUsedWithBitcode(arg);
+                       }
+                       else if ( strcmp(arg, "-add_split_seg_info") == 0) {
+                               fSharedRegionEligible = true;
+                               cannotBeUsedWithBitcode(arg);
+                       }
                        else if ( strcmp(arg, "-no_deduplicate") == 0 ) {
                                fDeDupe = false;
                        }
@@ -3745,14 +3849,8 @@ void Options::parse(int argc, const char* argv[])
                                }
                                fMaxDefaultCommonAlign = alignment;
                        }
-                       else if ( strcmp(arg, "-prefer-mod-time-check") == 0 ) {
-                               fFilePreference = kModTime;
-                       }
-                       else if ( strcmp(arg, "-prefer-text-based-stub-file") == 0 ) {
-                               fFilePreference = kTextBasedStub;
-                       }
-                       else if ( strcmp(arg, "-prefer-macho-file") == 0 ) {
-                               fFilePreference = kMachO;
+                       else if ( strcmp(argv[i], "-no_weak_imports") == 0 ) {
+                               fAllowWeakImports = false;
                        }
                        // put this last so that it does not interfer with other options starting with 'i'
                        else if ( strncmp(arg, "-i", 2) == 0 ) {
@@ -3856,6 +3954,7 @@ void Options::buildSearchPaths(int argc, const char* argv[])
                                const char* ltoVers = lto::version();
                                if ( ltoVers != NULL )
                                        fprintf(stderr, "LTO support using: %s\n", ltoVers);
+                               fprintf(stderr, "TAPI support using: %s\n", tapi::Version::getFullVersionAsString().c_str());
                                exit(0);
                        }
                }
@@ -4039,6 +4138,11 @@ void Options::parsePreCommandLineEnvironmentSettings()
            fTraceDylibs = true;
                fTraceIndirectDylibs = true;
        }
+       
+       if ((getenv("LD_TRACE_DEPENDENTS") != NULL)) {
+               
+               fTraceEmitJSON = true;
+       }
 
        if (getenv("RC_TRACE_DYLIB_SEARCHING") != NULL) {
            fTraceDylibSearching = true;
@@ -4047,7 +4151,7 @@ void Options::parsePreCommandLineEnvironmentSettings()
        if (getenv("LD_PRINT_OPTIONS") != NULL)
                fPrintOptions = true;
 
-       if (fTraceDylibs || fTraceArchives)
+       if (fTraceDylibs || fTraceArchives || fTraceEmitJSON)
                fTraceOutputFile = getenv("LD_TRACE_FILE");
 
        if (getenv("LD_PRINT_ORDER_FILE_STATISTICS") != NULL)
@@ -4091,10 +4195,6 @@ void Options::parsePreCommandLineEnvironmentSettings()
     if (pipeFdString != NULL) {
                fPipelineFifo = pipeFdString;
     }
-
-       // Workaround for rdar://problem/24301175
-       if ((getenv("RC_XBS") != NULL) && !(getenv("RC_BUILDIT") != NULL))
-               fForceTextBasedStub = true;
 }
 
 
@@ -4476,7 +4576,7 @@ void Options::reconfigureDefaults()
 
        // determine if info for shared region should be added
        if ( fOutputKind == Options::kDynamicLibrary ) {
-               if ( minOS(ld::mac10_5, ld::iOS_3_1) )
+               if ( minOS(ld::mac10_5, ld::iOS_3_1) && !fTargetIOSSimulator )
                        if ( !fPrebind && !fSharedRegionEligibleForceOff )
                                if ( (strncmp(this->installPath(), "/usr/lib/", 9) == 0)
                                        || (strncmp(this->installPath(), "/System/Library/", 16) == 0) )
@@ -4498,6 +4598,12 @@ void Options::reconfigureDefaults()
        if ( fUseDataConstSegmentForceOn ) {
                fUseDataConstSegment = true;
        }
+       // A -kext for iOS 10 ==>  -data_const, -text_exec, -add_split_seg_info
+       if ( (fOutputKind == Options::kKextBundle) && minOS(ld::mac10_Future, ld::iOS_10_0) && (fArchitecture == CPU_TYPE_ARM64) ) {
+               fUseDataConstSegment = true;
+               fUseTextExecSegment = true;
+               fSharedRegionEligible = true;
+       }
        if ( fUseDataConstSegment ) {
                addSectionRename("__DATA", "__got",                             "__DATA_CONST", "__got");
                addSectionRename("__DATA", "__la_symbol_ptr",   "__DATA_CONST", "__la_symbol_ptr");
@@ -4514,10 +4620,17 @@ void Options::reconfigureDefaults()
                addSectionRename("__DATA", "__objc_imageinfo",  "__DATA_CONST", "__objc_imageinfo");
                addSectionRename("__DATA", "__objc_const",          "__DATA_CONST", "__objc_const");
        }
+       if ( fUseTextExecSegment ) {
+               addSectionRename("__TEXT", "__text",                            "__TEXT_EXEC", "__text");
+               addSectionRename("__TEXT", "__stubs",                           "__TEXT_EXEC", "__stubs");
+       }
        
        // Use V2 shared cache info when targetting newer OSs
-       if ( fSharedRegionEligible && minOS(ld::mac10_Future, ld::iOS_9_0)) {
+       if ( fSharedRegionEligible && minOS(ld::mac10_12, ld::iOS_9_0)) {
                fSharedRegionEncodingV2 = true;
+               // <rdar://problem/24772435> only use v2 for Swift dylibs on Mac OS X
+               if ( (fPlatform == kPlatformOSX) && (strncmp(this->installPath(), "/System/Library/PrivateFrameworks/Swift/", 40) != 0) )
+                       fSharedRegionEncodingV2 = false;
                fIgnoreOptimizationHints = true;
        }
 
@@ -4609,7 +4722,8 @@ void Options::reconfigureDefaults()
                                fEncryptable = false;
                        break;
        }
-       if ( (fArchitecture != CPU_TYPE_ARM) && (fArchitecture != CPU_TYPE_ARM64) )
+       if ( (fArchitecture != CPU_TYPE_ARM) && (fArchitecture != CPU_TYPE_ARM64)
+         )
                fEncryptable = false;
        if ( fEncryptableForceOn )
                fEncryptable = true;
@@ -4673,7 +4787,6 @@ void Options::reconfigureDefaults()
                case CPU_TYPE_I386:
                case CPU_TYPE_ARM64:
                        fEnforceDylibSubtypesMatch = false;
-                       fAllowCpuSubtypeMismatches = true;
                        break;
        }
                
@@ -4712,7 +4825,7 @@ void Options::reconfigureDefaults()
                fCanUseUpwardDylib = true;
                
        // MacOSX 10.7 defaults to PIE
-       if ( ((fArchitecture == CPU_TYPE_X86_64) || (fArchitecture == CPU_TYPE_I386))
+       if ( (fArchitecture == CPU_TYPE_I386)
                && (fOutputKind == kDynamicExecutable)
                && (fMacVersionMin >= ld::mac10_7) ) {
                        fPositionIndependentExecutable = true;
@@ -4726,6 +4839,10 @@ void Options::reconfigureDefaults()
                        fPositionIndependentExecutable = true;
        }
 
+       // <rdar://problem/24535196> x86_64 defaults PIE (regardless of minOS version)
+       if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind == kDynamicExecutable) && (fMacVersionMin >= ld::mac10_6) )
+               fPositionIndependentExecutable = true;
+
        // Simulator defaults to PIE
        if ( fTargetIOSSimulator && (fOutputKind == kDynamicExecutable) )
                fPositionIndependentExecutable = true;
@@ -4735,8 +4852,12 @@ void Options::reconfigureDefaults()
                fPositionIndependentExecutable = false;
 
        // arm64 is always PIE
-       if ( (fArchitecture == CPU_TYPE_ARM64) && (fOutputKind == kDynamicExecutable) ) {
+       if ( ((fArchitecture == CPU_TYPE_ARM64)
+          )
+        && (fOutputKind == kDynamicExecutable) ) {
                fPositionIndependentExecutable = true;
+               if ( fDisablePositionIndependentExecutable )
+                       warning("-no_pie ignored for arm64");
        }
 
        // set fOutputSlidable
@@ -4763,7 +4884,9 @@ void Options::reconfigureDefaults()
        if ( fMacVersionMin >= ld::mac10_7 ) {
                fTLVSupport = true;
        }
-       else if ( (fArchitecture == CPU_TYPE_ARM64) && min_iOS(ld::iOS_8_0) ) {
+       else if ( ((fArchitecture == CPU_TYPE_ARM64)
+               )
+             && min_iOS(ld::iOS_8_0) ) {
                fTLVSupport = true;
        }
        else if ( (fArchitecture == CPU_TYPE_ARM) && min_iOS(ld::iOS_9_0) ) {
@@ -4954,7 +5077,8 @@ void Options::reconfigureDefaults()
                        case Options::kDynamicLibrary:
                        case Options::kDynamicBundle:
                        case Options::kDyld:
-                               if ( (fArchitecture == CPU_TYPE_ARM64) 
+                               if ( ((fArchitecture == CPU_TYPE_ARM64)
+                     )
                                || ((fArchitecture == CPU_TYPE_ARM) && min_iOS(ld::iOS_7_0)) ) {
                                        fSegmentAlignment = 4096*4;
                                }
@@ -4962,7 +5086,9 @@ void Options::reconfigureDefaults()
                        case Options::kStaticExecutable:
                        case Options::kKextBundle:
                                // <rdar://problem/14676611> 16KB segments for arm64 kexts
-                               if ( (fArchitecture == CPU_TYPE_ARM64) && min_iOS(ld::iOS_9_0) ) {
+                               if ( ((fArchitecture == CPU_TYPE_ARM64)
+                     )
+                                   && min_iOS(ld::iOS_9_0) ) {
                                        fSegmentAlignment = 4096*4;
                                }
                                break;
@@ -5398,7 +5524,7 @@ void Options::checkIllegalOptionCombinations()
                // zero page size not specified on command line, set default
                switch (fArchitecture) {
                        case CPU_TYPE_I386:
-            case CPU_TYPE_ARM:
+                       case CPU_TYPE_ARM:
                                // first 4KB for 32-bit architectures
                                fZeroPageSize = 0x1000;
                                break;
@@ -5521,8 +5647,8 @@ void Options::checkIllegalOptionCombinations()
        if ( (fOutputKind != Options::kDynamicExecutable) && (fDyldEnvironExtras.size() != 0) )
                throw "-dyld_env can only used used when created main executables";
 
-       // -segment_order can only be used with -preload
-       if ( !fSegmentOrder.empty() && (fOutputKind != Options::kPreload) )
+       // -segment_order can only be used with -preload or -static
+       if ( !fSegmentOrder.empty() && ((fOutputKind != Options::kPreload) && (fOutputKind != kStaticExecutable)) )
                throw "-segment_order can only used used with -preload output";
 
        // warn about bitcode option combinations
@@ -5550,6 +5676,12 @@ void Options::checkIllegalOptionCombinations()
                if ( !min_iOS(ld::iOS_8_0) && (fDylibInstallName[0] == '@') && !fEncryptableForceOff )
                        warning("embedded dylibs/frameworks only run on iOS 8 or later");
        }
+
+
+       // produce nicer error when no input
+       if ( fInputFiles.empty() ) {
+               throw "no object files specified";
+       }
 }      
 
 
index 5155de4ad876f5d13b177331c0cb1509677d8938..e257e30fde636ad3d2335fe84326a44b13f63159 100644 (file)
@@ -160,10 +160,10 @@ public:
                }
 
         // Create an empty FileInfo. The path can be set implicitly by checkFileExists().
-        FileInfo() : path(NULL), fileLen(0), modTime(0), options(), fromFileList(false) {};
+        FileInfo() : path(NULL), fileLen(0), modTime(-1), options(), fromFileList(false) {};
         
         // Create a FileInfo for a specific path, but does not stat the file.
-        FileInfo(const char *_path) : path(strdup(_path)), fileLen(0), modTime(0), options(), fromFileList(false) {};
+        FileInfo(const char *_path) : path(strdup(_path)), fileLen(0), modTime(-1), options(), fromFileList(false) {};
 
         ~FileInfo() { if (path) ::free((void*)path); }
         
@@ -175,7 +175,7 @@ public:
         
         // Returns true if a previous call to checkFileExists() succeeded.
         // Returns false if the file does not exist of checkFileExists() has never been called.
-        bool missing() const { return modTime==0; }
+        bool missing() const { return modTime == -1; }
        };
 
        struct ExtraSection {
@@ -292,6 +292,7 @@ public:
        bool                                            ignoreOtherArchInputFiles() const { return fIgnoreOtherArchFiles; }
        bool                                            traceDylibs() const     { return fTraceDylibs; }
        bool                                            traceArchives() const { return fTraceArchives; }
+       bool                                            traceEmitJSON() const { return fTraceEmitJSON; }
        bool                                            deadCodeStrip() const   { return fDeadStrip; }
        UndefinedTreatment                      undefinedTreatment() const { return fUndefinedTreatment; }
        ld::MacVersionMin                       macosxVersionMin() const { return fMacVersionMin; }
@@ -325,7 +326,7 @@ public:
        CommonsMode                                     commonsMode() const { return fCommonsMode; }
        bool                                            warnCommons() const { return fWarnCommons; }
        bool                                            keepRelocations();
-       FileInfo                                        findFile(const std::string &path) const;
+       FileInfo                                        findFile(const std::string &path, const ld::dylib::File* fromDylib=nullptr) const;
        bool                                            findFile(const std::string &path, const std::vector<std::string> &tbdExtensions, FileInfo& result) const;
        UUIDMode                                        UUIDMode() const { return fUUIDMode; }
        bool                                            warnStabs();
@@ -356,7 +357,7 @@ public:
        const std::vector<DylibOverride>&       dylibOverrides() const { return fDylibOverrides; }
        const char*                                     generatedMapPath() const { return fMapPath; }
        bool                                            positionIndependentExecutable() const { return fPositionIndependentExecutable; }
-       Options::FileInfo                       findFileUsingPaths(const std::string &path) const;
+       Options::FileInfo                       findIndirectDylib(const std::string& installName, const ld::dylib::File* fromDylib) const;
        bool                                            deadStripDylibs() const { return fDeadStripDylibs; }
        bool                                            allowedUndefined(const char* name) const { return ( fAllowedUndefined.find(name) != fAllowedUndefined.end() ); }
        bool                                            someAllowedUndefines() const { return (fAllowedUndefined.size() != 0); }
@@ -411,6 +412,10 @@ public:
        bool                                            addFunctionStarts() const { return fFunctionStartsLoadCommand; }
        bool                                            addDataInCodeInfo() const { return fDataInCodeInfoLoadCommand; }
        bool                                            canReExportSymbols() const { return fCanReExportSymbols; }
+       const char*                                     ltoCachePath() const { return fLtoCachePath; }
+       int                                                     ltoPruneInterval() const { return fLtoPruneInterval; }
+       int                                                     ltoPruneAfter() const { return fLtoPruneAfter; }
+       unsigned                                        ltoMaxCacheSize() const { return fLtoMaxCacheSize; }
        const char*                                     tempLtoObjectPath() const { return fTempLtoObjectPath; }
        const char*                                     overridePathlibLTO() const { return fOverridePathlibLTO; }
        const char*                                     mcpuLTO() const { return fLtoCpu; }
@@ -436,9 +441,11 @@ public:
        bool                                            ltoCodegenOnly() const { return fLTOCodegenOnly; }
        bool                                            ignoreAutoLink() const { return fIgnoreAutoLink; }
        bool                                            allowDeadDuplicates() const { return fAllowDeadDups; }
+       bool                                            allowWeakImports() const { return fAllowWeakImports; }
        BitcodeMode                                     bitcodeKind() const { return fBitcodeKind; }
        bool                                            sharedRegionEncodingV2() const { return fSharedRegionEncodingV2; }
        bool                                            useDataConstSegment() const { return fUseDataConstSegment; }
+       bool                                            useTextExecSegment() const { return fUseTextExecSegment; }
        bool                                            hasWeakBitTweaks() const;
        bool                                            forceWeak(const char* symbolName) const;
        bool                                            forceNotWeak(const char* symbolName) const;
@@ -475,6 +482,8 @@ public:
        std::string                                     getSDKVersionStr() const;
        std::string                                     getPlatformStr() const;
        uint8_t                                         maxDefaultCommonAlign() const { return fMaxDefaultCommonAlign; }
+       bool                                            hasDataSymbolMoves() const { return !fSymbolsMovesData.empty(); }
+       bool                                            hasCodeSymbolMoves() const { return !fSymbolsMovesCode.empty(); }
 
        static uint32_t                         parseVersionNumber32(const char*);
 
@@ -484,7 +493,6 @@ private:
        enum ExportMode { kExportDefault, kExportSome, kDontExportSome };
        enum LibrarySearchMode { kSearchDylibAndArchiveInEachDir, kSearchAllDirsForDylibsThenAllDirsForArchives };
        enum InterposeMode { kInterposeNone, kInterposeAllExternal, kInterposeSome };
-       enum FilePreference { kModTime, kTextBasedStub, kMachO };
 
        class SetWithWildcards {
        public:
@@ -611,6 +619,10 @@ private:
        const char*                                                     fSegAddrTablePath;
        const char*                                                     fMapPath;
        const char*                                                     fDyldInstallPath;
+       const char*                                                     fLtoCachePath;
+       int                                                                     fLtoPruneInterval;
+       int                                                                     fLtoPruneAfter;
+       unsigned                                                        fLtoMaxCacheSize;
        const char*                                                     fTempLtoObjectPath;
        const char*                                                     fOverridePathlibLTO;
        const char*                                                     fLtoCpu;
@@ -687,6 +699,7 @@ private:
        bool                                                            fTraceDylibs;
        bool                                                            fTraceIndirectDylibs;
        bool                                                            fTraceArchives;
+       bool                                                            fTraceEmitJSON;
        bool                                                            fOutputSlidable;
        bool                                                            fWarnWeakExports;
        bool                                                            fObjcGcCompaction;
@@ -732,6 +745,7 @@ private:
        bool                                                            fUseDataConstSegment;
        bool                                                            fUseDataConstSegmentForceOn;
        bool                                                            fUseDataConstSegmentForceOff;
+       bool                                                            fUseTextExecSegment;
        bool                                                            fBundleBitcode;
        bool                                                            fHideSymbols;
        bool                                                            fVerifyBitcode;
@@ -743,6 +757,7 @@ private:
        bool                                                            fLTOCodegenOnly;
        bool                                                            fIgnoreAutoLink;
        bool                                                            fAllowDeadDups;
+       bool                                                            fAllowWeakImports;
        BitcodeMode                                                     fBitcodeKind;
        Platform                                                        fPlatform;
        DebugInfoStripping                                      fDebugInfoStripping;
@@ -774,7 +789,6 @@ private:
        std::vector<SegmentRename>                      fSegmentRenames;
        std::vector<SymbolsMove>                        fSymbolsMovesData;
        std::vector<SymbolsMove>                        fSymbolsMovesCode;
-       std::vector<SymbolsMove>                        fSymbolsMovesZeroFill;
        bool                                                            fSaveTempFiles;
     mutable Snapshot                                   fLinkSnapshot;
     bool                                                               fSnapshotRequested;
@@ -782,8 +796,6 @@ private:
        const char*                                                     fDependencyInfoPath;
        mutable int                                                     fDependencyFileDescriptor;
        uint8_t                                                         fMaxDefaultCommonAlign;
-       FilePreference                                          fFilePreference;
-       bool                                                            fForceTextBasedStub;
 };
 
 
index 1d708da079aea22a21a90a6e143887a366ec771d..732eb547ee4ac72f0ccec2599b53ed6972435390 100644 (file)
@@ -52,6 +52,8 @@
 #include <algorithm>
 #include <unordered_set>
 #include <utility>
+#include <iostream>
+#include <fstream>
 
 #include <CommonCrypto/CommonDigest.h>
 #include <AvailabilityMacros.h>
@@ -162,6 +164,7 @@ void OutputFile::write(ld::Internal& state)
        //this->dumpAtomsBySection(state, false);
        this->writeOutputFile(state);
        this->writeMapFile(state);
+       this->writeJSONEntry(state);
 }
 
 bool OutputFile::findSegment(ld::Internal& state, uint64_t addr, uint64_t* start, uint64_t* end, uint32_t* index)
@@ -354,6 +357,12 @@ void OutputFile::setLoadCommandsPadding(ld::Internal& state)
                case Options::kPreload:
                        // mach-o MH_PRELOAD files need no padding between load commands and first section
                        paddingSize = 0;
+               case Options::kKextBundle:
+                       if ( _options.useTextExecSegment() ) {
+                               paddingSize = 32;
+                               break;
+                       }
+                       // else fall into default case
                default:
                        // work backwards from end of segment and lay out sections so that extra room goes to padding atom
                        uint64_t addr = 0;
@@ -495,6 +504,30 @@ uint64_t OutputFile::addressOf(const ld::Internal& state, const ld::Fixup* fixup
        throw "unexpected binding";
 }
 
+uint64_t OutputFile::addressAndTarget(const ld::Internal& state, const ld::Fixup* fixup, const ld::Atom** target)
+{
+       switch ( fixup->binding ) {
+               case ld::Fixup::bindingNone:
+                       throw "unexpected bindingNone";
+               case ld::Fixup::bindingByNameUnbound:
+                       throw "unexpected bindingByNameUnbound";
+               case ld::Fixup::bindingByContentBound:
+               case ld::Fixup::bindingDirectlyBound:
+                       *target = fixup->u.target;
+                       return (*target)->finalAddress();
+               case ld::Fixup::bindingsIndirectlyBound:
+                       *target = state.indirectBindingTable[fixup->u.bindingIndex];
+               #ifndef NDEBUG
+                       if ( ! (*target)->finalAddressMode() ) {
+                               throwf("reference to symbol (which has not been assigned an address) %s", (*target)->name());
+                       }
+               #endif
+                       return (*target)->finalAddress();
+       }
+       throw "unexpected binding";
+}
+
+
 uint64_t OutputFile::sectionOffsetOf(const ld::Internal& state, const ld::Fixup* fixup)
 {
        const ld::Atom* target = NULL;
@@ -2012,7 +2045,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::
                                {
                                        // GOT entry was optimized away, change LDR instruction to a ADD
                                        instruction = get32LE(fixUpLocation);
-                                       if ( (instruction & 0xFFC00000) != 0xF9400000 )
+                                       if ( (instruction & 0xBFC00000) != 0xB9400000 )
                                                throwf("GOT load reloc does not point to a LDR instruction in %s", atom->name());
                                        uint32_t offset = accumulator & 0x00000FFF;
                                        uint32_t imm12 = offset << 10;
@@ -2027,7 +2060,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::
                                {
                                        // TLV thunk in same linkage unit, so LEA it directly, changing LDR instruction to a ADD
                                        instruction = get32LE(fixUpLocation);
-                                       if ( (instruction & 0xFFC00000) != 0xF9400000 )
+                                       if ( (instruction & 0xBFC00000) != 0xB9400000 )
                                                throwf("TLV load reloc does not point to a LDR instruction in %s", atom->name());
                                        uint32_t offset = accumulator & 0x00000FFF;
                                        uint32_t imm12 = offset << 10;
@@ -2038,7 +2071,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::
                        case ld::Fixup::kindStoreARM64PointerToGOT:
                                set64LE(fixUpLocation, accumulator);
                                break;
-            case ld::Fixup::kindStoreARM64PCRelToGOT:
+        case ld::Fixup::kindStoreARM64PCRelToGOT:
                                if ( fit->contentAddendOnly )
                                        delta = accumulator;
                                else
@@ -2655,13 +2688,22 @@ void OutputFile::computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer)
                        uint64_t lastStabNlistFileOffset   = symbolTableFileOffset + stabsOffsetEnd;
                        uint64_t firstStabStringFileOffset = stringPoolFileOffset  + stabsStringsOffsetStart;
                        uint64_t lastStabStringFileOffset  = stringPoolFileOffset  + tabsStringsOffsetEnd;
-                       if ( log ) fprintf(stderr, "firstStabNlistFileOffset=0x%08llX\n", firstStabNlistFileOffset);
-                       if ( log ) fprintf(stderr, "lastStabNlistFileOffset=0x%08llX\n", lastStabNlistFileOffset);
-                       if ( log ) fprintf(stderr, "firstStabStringFileOffset=0x%08llX\n", firstStabStringFileOffset);
-                       if ( log ) fprintf(stderr, "lastStabStringFileOffset=0x%08llX\n", lastStabStringFileOffset);
+                       if ( log ) fprintf(stderr, "stabNlist offset=0x%08llX, size=0x%08llX\n", firstStabNlistFileOffset, lastStabNlistFileOffset-firstStabNlistFileOffset);
+                       if ( log ) fprintf(stderr, "stabString offset=0x%08llX, size=0x%08llX\n", firstStabStringFileOffset, lastStabStringFileOffset-firstStabStringFileOffset);
                        assert(firstStabNlistFileOffset <= firstStabStringFileOffset);
                        excludeRegions.emplace_back(std::pair<uint64_t, uint64_t>(firstStabNlistFileOffset, lastStabNlistFileOffset));
                        excludeRegions.emplace_back(std::pair<uint64_t, uint64_t>(firstStabStringFileOffset, lastStabStringFileOffset));
+                       // exclude LINKEDIT LC_SEGMENT (size field depends on stabs size)
+                       uint64_t linkeditSegCmdOffset;
+                       uint64_t linkeditSegCmdSize;
+                       _headersAndLoadCommandAtom->linkeditCmdInfo(linkeditSegCmdOffset, linkeditSegCmdSize);
+                       excludeRegions.emplace_back(std::pair<uint64_t, uint64_t>(linkeditSegCmdOffset, linkeditSegCmdOffset+linkeditSegCmdSize));
+                       if ( log ) fprintf(stderr, "linkedit SegCmdOffset=0x%08llX, size=0x%08llX\n", linkeditSegCmdOffset, linkeditSegCmdSize);
+                       uint64_t symbolTableCmdOffset;
+                       uint64_t symbolTableCmdSize;
+                       _headersAndLoadCommandAtom->symbolTableCmdInfo(symbolTableCmdOffset, symbolTableCmdSize);
+                       excludeRegions.emplace_back(std::pair<uint64_t, uint64_t>(symbolTableCmdOffset, symbolTableCmdOffset+symbolTableCmdSize));
+                       if ( log ) fprintf(stderr, "linkedit SegCmdOffset=0x%08llX, size=0x%08llX\n", symbolTableCmdOffset, symbolTableCmdSize);
                }
                if ( !excludeRegions.empty() ) {
                        CC_MD5_CTX md5state;
@@ -2671,6 +2713,7 @@ void OutputFile::computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer)
                        if ( lastSlash !=  NULL ) {
                                CC_MD5_Update(&md5state, lastSlash, strlen(lastSlash));
                        }
+                       std::sort(excludeRegions.begin(), excludeRegions.end());
                        uint64_t checksumStart = 0;
                        for ( auto& region : excludeRegions ) {
                                uint64_t regionStart = region.first;
@@ -2863,13 +2906,24 @@ void OutputFile::writeOutputFile(ld::Internal& state)
 }
 
 struct AtomByNameSorter
-{      
-        bool operator()(const ld::Atom* left, const ld::Atom* right)
-        {
-          return (strcmp(left->name(), right->name()) < 0);
-        }
+{
+       bool operator()(const ld::Atom* left, const ld::Atom* right) const
+       {
+               return (strcmp(left->name(), right->name()) < 0);
+       }
+
+       bool operator()(const ld::Atom* left, const char* right) const
+       {
+               return (strcmp(left->name(), right) < 0);
+       }
+
+       bool operator()(const char* left, const ld::Atom* right) const
+       {
+               return (strcmp(left, right->name()) < 0);
+       }
 };
 
+
 class NotInSet
 {
 public:
@@ -2904,7 +2958,9 @@ void OutputFile::buildSymbolTable(ld::Internal& state)
                                
                        // in -r mode, clarify symbolTableNotInFinalLinkedImages
                        if ( _options.outputKind() == Options::kObjectFile ) {
-                               if ( (_options.architecture() == CPU_TYPE_X86_64) || (_options.architecture() == CPU_TYPE_ARM64) ) {
+                               if ( (_options.architecture() == CPU_TYPE_X86_64)
+                                 || (_options.architecture() == CPU_TYPE_ARM64)
+                                  ) {
                                        // x86_64 .o files need labels on anonymous literal strings
                                        if ( (sect->type() == ld::Section::typeCString) && (atom->combine() == ld::Atom::combineByNameAndContent) ) {
                                                (const_cast<ld::Atom*>(atom))->setSymbolTableInclusion(ld::Atom::symbolTableIn);
@@ -3055,6 +3111,59 @@ void OutputFile::buildSymbolTable(ld::Internal& state)
        // sort by name
        std::sort(_exportedAtoms.begin(), _exportedAtoms.end(), AtomByNameSorter());
        std::sort(_importedAtoms.begin(), _importedAtoms.end(), AtomByNameSorter());
+
+       std::map<std::string, std::vector<std::string>> addedSymbols;
+       std::map<std::string, std::vector<std::string>> hiddenSymbols;
+       for (const auto *atom : _exportedAtoms) {
+               // The exported symbols have already been sorted. Early exit the loop
+               // once we see a symbol that is lexicographically past the special
+               // linker symbol.
+               if (atom->name()[0] > '$')
+                       break;
+
+               std::string name(atom->name());
+               if (name.rfind("$ld$add$", 7) == 0) {
+                       auto pos = name.find_first_of('$', 10);
+                       if (pos == std::string::npos) {
+                               warning("bad special linker symbol '%s'", atom->name());
+                               continue;
+                       }
+                       auto &&symbolName = name.substr(pos+1);
+                       auto it = addedSymbols.emplace(symbolName, std::initializer_list<std::string>{name});
+                       if (!it.second)
+                               it.first->second.emplace_back(name);
+               } else if (name.rfind("$ld$hide$", 8) == 0) {
+                       auto pos = name.find_first_of('$', 11);
+                       if (pos == std::string::npos) {
+                               warning("bad special linker symbol '%s'", atom->name());
+                               continue;
+                       }
+                       auto &&symbolName = name.substr(pos+1);
+                       auto it = hiddenSymbols.emplace(symbolName, std::initializer_list<std::string>{name});
+                       if (!it.second)
+                               it.first->second.emplace_back(name);
+               }
+       }
+
+       for (const auto &it : addedSymbols) {
+               if (!std::binary_search(_exportedAtoms.begin(), _exportedAtoms.end(), it.first.c_str(), AtomByNameSorter()))
+                       continue;
+               for (const auto &symbol :  it.second)
+                       warning("linker symbol '%s' adds already existing symbol '%s'", symbol.c_str(), it.first.c_str());
+       }
+
+       auto it = hiddenSymbols.begin();
+       while (it != hiddenSymbols.end()) {
+               if (std::binary_search(_exportedAtoms.begin(), _exportedAtoms.end(), it->first.c_str(), AtomByNameSorter()))
+                       it = hiddenSymbols.erase(it);
+               else
+                       ++it;
+       }
+
+       for (const auto &it : hiddenSymbols) {
+               for (const auto &symbol :  it.second)
+                       warning("linker symbol '%s' hides a non-existent symbol '%s'", symbol.c_str(), it.first.c_str());
+       }
 }
 
 void OutputFile::addPreloadLinkEdit(ld::Internal& state)
@@ -3181,7 +3290,10 @@ void OutputFile::addLinkEdit(ld::Internal& state)
                                localRelocationsSection = state.addAtom(*_localRelocsAtom);
                        }
                        if  ( _hasSplitSegInfo ) {
-                               _splitSegInfoAtom = new SplitSegInfoV1Atom<x86>(_options, state, *this);
+                               if ( _options.sharedRegionEncodingV2() )
+                                       _splitSegInfoAtom = new SplitSegInfoV2Atom<x86>(_options, state, *this);
+                               else
+                                       _splitSegInfoAtom = new SplitSegInfoV1Atom<x86>(_options, state, *this);
                                splitSegInfoSection = state.addAtom(*_splitSegInfoAtom);
                        }
                        if ( _hasFunctionStartsInfo ) {
@@ -3239,7 +3351,10 @@ void OutputFile::addLinkEdit(ld::Internal& state)
                                localRelocationsSection = state.addAtom(*_localRelocsAtom);
                        }
                        if  ( _hasSplitSegInfo ) {
-                               _splitSegInfoAtom = new SplitSegInfoV1Atom<x86_64>(_options, state, *this);
+                               if ( _options.sharedRegionEncodingV2() )
+                                       _splitSegInfoAtom = new SplitSegInfoV2Atom<x86_64>(_options, state, *this);
+                               else
+                                       _splitSegInfoAtom = new SplitSegInfoV1Atom<x86_64>(_options, state, *this);
                                splitSegInfoSection = state.addAtom(*_splitSegInfoAtom);
                        }
                        if ( _hasFunctionStartsInfo ) {
@@ -3770,7 +3885,7 @@ void OutputFile::generateLinkEditInfo(ld::Internal& state)
        for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) {
                ld::Internal::FinalSection* sect = *sit;
                // record end of last __TEXT section encrypted iPhoneOS apps.
-               if ( _options.makeEncryptable() && (strcmp(sect->segmentName(), "__TEXT") == 0) ) { 
+               if ( _options.makeEncryptable() && (strcmp(sect->segmentName(), "__TEXT") == 0) && (strcmp(sect->sectionName(), "__oslogstring") != 0) ) {
                        _encryptedTEXTendOffset = pageAlign(sect->fileOffset + sect->size);
                }
                bool objc1ClassRefSection = ( (sect->type() == ld::Section::typeCStringPointer) 
@@ -3908,14 +4023,17 @@ void OutputFile::noteTextReloc(const ld::Atom* atom, const ld::Atom* target)
        else if ( _options.positionIndependentExecutable() && (_options.outputKind() == Options::kDynamicExecutable) 
                && ((_options.iOSVersionMin() >= ld::iOS_4_3) || (_options.macosxVersionMin() >= ld::mac10_7)) ) {
                if ( ! this->pieDisabled ) {
+                       switch ( _options.architecture()) {
+#if SUPPORT_ARCH_arm64
+            case CPU_TYPE_ARM64:
+#endif
 #if SUPPORT_ARCH_arm64
-                       if ( _options.architecture() == CPU_TYPE_ARM64 ) {
+                       {
                                const char* demangledName = strdup(_options.demangleSymbol(atom->name()));
                                throwf("Absolute addressing not allowed in arm64 code but used in '%s' referencing '%s'", demangledName, _options.demangleSymbol(target->name()));
                        }
-                       else
 #endif
-                        {
+            default:
                                warning("PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, "
                                "but used in %s from %s. " 
                                "To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie", 
@@ -3962,9 +4080,9 @@ void OutputFile::addDyldInfo(ld::Internal& state,  ld::Internal::FinalSection* s
                                }
                                // Have direct reference to weak-global.  This should be an indrect reference
                                const char* demangledName = strdup(_options.demangleSymbol(atom->name()));
-                               warning("direct access in %s to global weak symbol %s means the weak symbol cannot be overridden at runtime. "
+                               warning("direct access in function '%s' from file '%s' to global weak symbol '%s' from file '%s' means the weak symbol cannot be overridden at runtime. "
                                                "This was likely caused by different translation units being compiled with different visibility settings.",
-                                                 demangledName, _options.demangleSymbol(target->name()));
+                                                 demangledName, atom->file()->path(), _options.demangleSymbol(target->name()), target->file()->path());
                        }
                        return;
                }
@@ -3991,9 +4109,9 @@ void OutputFile::addDyldInfo(ld::Internal& state,  ld::Internal::FinalSection* s
                        }
                        // Have direct reference to weak-global.  This should be an indrect reference
                        const char* demangledName = strdup(_options.demangleSymbol(atom->name()));
-                       warning("direct access in %s to global weak symbol %s means the weak symbol cannot be overridden at runtime. "
+                       warning("direct access in function '%s' from file '%s' to global weak symbol '%s' from file '%s' means the weak symbol cannot be overridden at runtime. "
                                        "This was likely caused by different translation units being compiled with different visibility settings.",
-                                        demangledName, _options.demangleSymbol(target->name()));
+                                         demangledName, atom->file()->path(), _options.demangleSymbol(target->name()), target->file()->path());
                }
                return;
        }
@@ -4134,7 +4252,8 @@ void OutputFile::addDyldInfo(ld::Internal& state,  ld::Internal::FinalSection* s
                if ( _options.sharedRegionEligible() ) {
                        // <rdar://problem/13287063> when range checking, ignore high byte of arm64 addends
                        uint64_t checkAddend = addend;
-                       if ( _options.architecture() == CPU_TYPE_ARM64 )
+                       if ( (_options.architecture() == CPU_TYPE_ARM64)
+                          )
                                checkAddend &= 0x0FFFFFFFFFFFFFFFULL;
                        if ( checkAddend != 0 ) {
                                // make sure the addend does not cause the pointer to point outside the target's segment
@@ -4342,7 +4461,9 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio
 
 bool OutputFile::useExternalSectionReloc(const ld::Atom* atom, const ld::Atom* target, ld::Fixup* fixupWithTarget)
 {
-       if ( (_options.architecture() == CPU_TYPE_X86_64) || (_options.architecture() == CPU_TYPE_ARM64) ) {
+       if ( (_options.architecture() == CPU_TYPE_X86_64)
+         || (_options.architecture() == CPU_TYPE_ARM64)
+       ) {
                // x86_64 and ARM64 use external relocations for everthing that has a symbol
                return ( target->symbolTableInclusion() != ld::Atom::symbolTableNotIn );
        }
@@ -4428,7 +4549,9 @@ void OutputFile::addSectionRelocs(ld::Internal& state, ld::Internal::FinalSectio
        bool minusTargetUsesExternalReloc = (minusTarget != NULL) && this->useExternalSectionReloc(atom, minusTarget, fixupWithMinusTarget);
        
        // in x86_64 and arm64 .o files an external reloc means the content contains just the addend
-       if ( (_options.architecture() == CPU_TYPE_X86_64) ||(_options.architecture() == CPU_TYPE_ARM64)  ) {
+       if ( (_options.architecture() == CPU_TYPE_X86_64)
+         || (_options.architecture() == CPU_TYPE_ARM64)
+          ) {
                if ( targetUsesExternalReloc ) {
                        fixupWithTarget->contentAddendOnly = true;
                        fixupWithStore->contentAddendOnly = true;
@@ -4492,7 +4615,7 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state)
                                if ( fit->firstInCluster() ) 
                                        target = NULL;
                                if ( this->setsTarget(fit->kind) ) {
-                                       accumulator = addressOf(state, fit, &target);                   
+                                       accumulator = addressOf(state, fit, &target);
                                        thumbTarget = targetIsThumb(state, fit);
                                        if ( thumbTarget ) 
                                                accumulator |= 1;
@@ -4562,7 +4685,7 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state)
                                                }
                                                break;
                                        case ld::Fixup::kindSetTargetImageOffset:
-                                               accumulator = addressOf(state, fit, &target);                   
+                                               accumulator = addressOf(state, fit, &target);
                                                assert(target != NULL);
                                                hadSubtract = true;
                                                break;
@@ -4603,6 +4726,7 @@ void OutputFile::makeSplitSegInfoV2(ld::Internal& state)
                        for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
                                if ( fit->firstInCluster() ) {
                                        target = NULL;
+                                       hadSubtract = false;
                                        fromTarget = NULL;
                                        kind = 0;
                                        addend = 0;
@@ -4610,7 +4734,7 @@ void OutputFile::makeSplitSegInfoV2(ld::Internal& state)
                                        fromOffset = atom->finalAddress() + fit->offsetInAtom - sect->address;
                                }
                                if ( this->setsTarget(fit->kind) ) {
-                                       accumulator = addressOf(state, fit, &target);                   
+                                       accumulator = addressAndTarget(state, fit, &target);
                                        thumbTarget = targetIsThumb(state, fit);
                                        if ( thumbTarget ) 
                                                accumulator |= 1;
@@ -4624,7 +4748,7 @@ void OutputFile::makeSplitSegInfoV2(ld::Internal& state)
                                }
                                switch ( fit->kind ) {
                                        case ld::Fixup::kindSubtractTargetAddress:
-                        accumulator -= addressOf(state, fit, &fromTarget);
+                        accumulator -= addressAndTarget(state, fit, &fromTarget);
                                                hadSubtract = true;
                                                break;
                     case ld::Fixup::kindAddAddend:
@@ -4729,7 +4853,7 @@ void OutputFile::makeSplitSegInfoV2(ld::Internal& state)
                                                break;
                                        case ld::Fixup::kindSetTargetImageOffset:
                                                kind = DYLD_CACHE_ADJ_V2_IMAGE_OFF_32;
-                                               accumulator = addressOf(state, fit, &target);                   
+                                               accumulator = addressAndTarget(state, fit, &target);
                                                assert(target != NULL);
                                                toSectionIndex = target->machoSection();
                                                toOffset = accumulator - state.atomToSection[target]->address;
@@ -4781,7 +4905,7 @@ void OutputFile::writeMapFile(ld::Internal& state)
                                        continue;
                                for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
                                        const ld::Atom* atom = *ait;
-                                       const ld::File* reader = atom->file();
+                                       const ld::File* reader = atom->originalFile();
                                        if ( reader == NULL )
                                                continue;
                                        ld::File::Ordinal readerOrdinal = reader->ordinal();
@@ -4792,6 +4916,17 @@ void OutputFile::writeMapFile(ld::Internal& state)
                                        }
                                }
                        }
+                       for (const ld::Atom* atom : state.deadAtoms) {
+                               const ld::File* reader = atom->originalFile();
+                               if ( reader == NULL )
+                                       continue;
+                               ld::File::Ordinal readerOrdinal = reader->ordinal();
+                               std::map<const ld::File*, ld::File::Ordinal>::iterator pos = readerToOrdinal.find(reader);
+                               if ( pos == readerToOrdinal.end() ) {
+                                       readerToOrdinal[reader] = readerOrdinal;
+                                       ordinalToReader[readerOrdinal] = reader;
+                               }
+                       }
                        fprintf(mapFile, "# Object files:\n");
                        fprintf(mapFile, "[%3u] %s\n", 0, "linker synthesized");
                        uint32_t fileIndex = 1;
@@ -4826,7 +4961,21 @@ void OutputFile::writeMapFile(ld::Internal& state)
                                                continue;
                                        if ( atom->contentType() == ld::Atom::typeCString ) {
                                                strcpy(buffer, "literal string: ");
-                                               strlcat(buffer, (char*)atom->rawContentPointer(), 4096);
+                                               const char* s = (char*)atom->rawContentPointer();
+                                               char* e = &buffer[4094];
+                                               for (char* b = &buffer[strlen(buffer)]; b < e;) {
+                                                       char c = *s++;
+                                                       if ( c == '\n' ) {
+                                                               *b++ = '\\';
+                                                               *b++ = 'n';
+                                                       }
+                                                       else {
+                                                               *b++ = c;
+                                                       }
+                                                       if ( c == '\0' )
+                                                               break;
+                                               }
+                                               buffer[4095] = '\0';
                                                name = buffer;
                                        }
                                        else if ( (atom->contentType() == ld::Atom::typeCFI) && (strcmp(name, "FDE") == 0) ) {
@@ -4858,7 +5007,41 @@ void OutputFile::writeMapFile(ld::Internal& state)
                                                name = buffer;
                                        }
                                        fprintf(mapFile, "0x%08llX\t0x%08llX\t[%3u] %s\n", atom->finalAddress(), atom->size(), 
-                                                       readerToFileOrdinal[atom->file()], name);
+                                                       readerToFileOrdinal[atom->originalFile()], name);
+                               }
+                       }
+                       // preload check is hack until 26613948 is fixed
+                       if ( _options.deadCodeStrip() && (_options.outputKind() != Options::kPreload) ) {
+                               fprintf(mapFile, "\n");
+                               fprintf(mapFile, "# Dead Stripped Symbols:\n");
+                               fprintf(mapFile, "#        \tSize    \tFile  Name\n");
+                               for (const ld::Atom* atom : state.deadAtoms) {
+                                       char buffer[4096];
+                                       const char* name = atom->name();
+                                       // don't add auto-stripped aliases to .map file
+                                       if ( (atom->size() == 0) && (atom->symbolTableInclusion() == ld::Atom::symbolTableNotInFinalLinkedImages) )
+                                               continue;
+                                       if ( atom->contentType() == ld::Atom::typeCString ) {
+                                               strcpy(buffer, "literal string: ");
+                                               const char* s = (char*)atom->rawContentPointer();
+                                               char* e = &buffer[4094];
+                                               for (char* b = &buffer[strlen(buffer)]; b < e;) {
+                                                       char c = *s++;
+                                                       if ( c == '\n' ) {
+                                                               *b++ = '\\';
+                                                               *b++ = 'n';
+                                                       }
+                                                       else {
+                                                               *b++ = c;
+                                                       }
+                                                       if ( c == '\0' )
+                                                               break;
+                                               }
+                                               buffer[4095] = '\0';
+                                               name = buffer;
+                                       }
+                                       fprintf(mapFile, "<<dead>> \t0x%08llX\t[%3u] %s\n",  atom->size(),
+                                                       readerToFileOrdinal[atom->originalFile()], name);
                                }
                        }
                        fclose(mapFile);
@@ -4869,6 +5052,101 @@ void OutputFile::writeMapFile(ld::Internal& state)
        }
 }
 
+void OutputFile::writeJSONEntry(ld::Internal& state)
+{
+       if ( _options.traceEmitJSON() && (_options.UUIDMode() != Options::kUUIDNone) && (_options.traceOutputFile() != NULL) ) {
+
+               // Convert the UUID to a string.
+               const uint8_t* uuid = _headersAndLoadCommandAtom->getUUID();
+               uuid_string_t uuidString;
+
+               uuid_unparse(uuid, uuidString);
+               
+               // Enumerate the dylibs.
+               std::vector<const ld::dylib::File*> dynamicList;
+               std::vector<const ld::dylib::File*> upwardList;
+               std::vector<const ld::dylib::File*> reexportList;
+
+               for (const ld::dylib::File* dylib :  _dylibsToLoad) {
+                       
+                       if (dylib->willBeUpwardDylib()) {
+                       
+                               upwardList.push_back(dylib);
+                       } else if (dylib->willBeReExported()) {
+                        
+                               reexportList.push_back(dylib);
+                       } else {
+                       
+                               dynamicList.push_back(dylib);
+                       }
+               }
+               
+               /*
+                * Build the JSON entry.
+                */
+               
+               std::string     jsonEntry = "{";
+
+               jsonEntry += "\"uuid\":\"" + std::string(uuidString) + "\",";
+
+               // installPath() returns -final_output for non-dylibs
+               const char* lastNameSlash = strrchr(_options.installPath(), '/');
+               const char* leafName = (lastNameSlash != NULL) ? lastNameSlash+1 : _options.outputFilePath();
+               jsonEntry += "\"name\":\"" + std::string(leafName) + "\",";
+
+               jsonEntry += "\"arch\":\"" + std::string(_options.architectureName()) + "\"";
+
+               if (dynamicList.size() > 0) {
+                       jsonEntry += ",\"dynamic\":[";
+                       for (const ld::dylib::File* dylib :  dynamicList) {
+                               jsonEntry += "\"" + std::string(dylib->path()) + "\"";
+                               if ((dylib != dynamicList.back())) {
+                                       jsonEntry += ",";
+                               }
+                       }
+                       jsonEntry += "]";
+               }
+               
+               if (upwardList.size() > 0) {
+                       jsonEntry += ",\"upward-dynamic\":[";
+                       for (const ld::dylib::File* dylib :  upwardList) {
+                               jsonEntry += "\"" + std::string(dylib->path()) + "\"";
+                               if ((dylib != upwardList.back())) {
+                                       jsonEntry += ",";
+                               }
+                       }
+                       jsonEntry += "]";
+               }
+               
+               if (reexportList.size() > 0) {
+                       jsonEntry += ",\"re-exports\":[";
+                       for (const ld::dylib::File* dylib :  reexportList) {
+                               jsonEntry += "\"" + std::string(dylib->path()) + "\"";
+                               if ((dylib != reexportList.back())) {
+                                       jsonEntry += ",";
+                               }
+                       }
+                       jsonEntry += "]";
+               }
+               
+               if (state.archivePaths.size() > 0) {
+                       jsonEntry += ",\"archives\":[";
+                       for (const std::string& archivePath : state.archivePaths) {
+                               jsonEntry += "\"" + std::string(archivePath) + "\"";
+                               if ((archivePath != state.archivePaths.back())) {
+                                       jsonEntry += ",";
+                               }
+                       }
+                       jsonEntry += "]";
+               }
+               jsonEntry += "}\n";
+               
+               // Write the JSON entry to the trace file.
+               std::ofstream out(_options.traceOutputFile(), ios::app);
+               out << jsonEntry;
+       }
+}
+       
 // used to sort atoms with debug notes
 class DebugNoteSorter
 {
index 5fd27d80592c0d72253598a2df33826e34edbb0c..81f2cfb842e2691ca669202907e456dca7f3de0c 100644 (file)
@@ -189,6 +189,7 @@ private:
        void                                            updateLINKEDITAddresses(ld::Internal& state);
        void                                            applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::Atom*  atom, uint8_t* buffer);
        uint64_t                                        addressOf(const ld::Internal& state, const ld::Fixup* fixup, const ld::Atom** target);
+       uint64_t                                        addressAndTarget(const ld::Internal& state, const ld::Fixup* fixup, const ld::Atom** target);
        bool                                            targetIsThumb(ld::Internal& state, const ld::Fixup* fixup);
        uint32_t                                        lazyBindingInfoOffsetForLazyPointerAddress(uint64_t lpAddress);
        void                                            copyNoOps(uint8_t* from, uint8_t* to, bool thumb);
@@ -205,6 +206,7 @@ private:
        void                                            makeSplitSegInfo(ld::Internal& state);
        void                                            makeSplitSegInfoV2(ld::Internal& state);
        void                                            writeMapFile(ld::Internal& state);
+       void                                            writeJSONEntry(ld::Internal& state);
        uint64_t                                        lookBackAddend(ld::Fixup::iterator fit);
        bool                                            takesNoDiskSpace(const ld::Section* sect);
        bool                                            hasZeroForFileOffset(const ld::Section* sect);
index ec04b46f889f49addbec98bf06a40835984c20e5..1c8f16e041bdcc99c210757518feca3b0bbd479a 100644 (file)
@@ -311,8 +311,10 @@ void Resolver::doLinkerOption(const std::vector<const char*>& linkerOption, cons
 {
        if ( linkerOption.size() == 1 ) {
                const char* lo1 = linkerOption.front();
-               if ( strncmp(lo1, "-l", 2) == 0 ) {
-                       _internal.linkerOptionLibraries.insert(&lo1[2]);
+               if ( strncmp(lo1, "-l", 2) == 0) {
+                       if (_internal.linkerOptionLibraries.count(&lo1[2]) == 0) {
+                               _internal.unprocessedLinkerOptionLibraries.insert(&lo1[2]);
+                       }
                }
                else {
                        warning("unknown linker option from object file ignored: '%s' in %s", lo1, fileName);
@@ -321,8 +323,10 @@ void Resolver::doLinkerOption(const std::vector<const char*>& linkerOption, cons
        else if ( linkerOption.size() == 2 ) {
                const char* lo2a = linkerOption[0];
                const char* lo2b = linkerOption[1];
-               if ( strcmp(lo2a, "-framework") == 0 ) {
-                       _internal.linkerOptionFrameworks.insert(lo2b);
+               if ( strcmp(lo2a, "-framework") == 0) {
+                       if (_internal.linkerOptionFrameworks.count(lo2b) == 0) {
+                               _internal.unprocessedLinkerOptionFrameworks.insert(lo2b);
+                       }
                }
                else {
                        warning("unknown linker option from object file ignored: '%s' '%s' from %s", lo2a, lo2b, fileName);
@@ -345,6 +349,9 @@ static void userReadableSwiftVersion(uint8_t value, char versionString[64])
                case 3:
                        strcpy(versionString, "2.0");
                        break;
+               case 4:
+                       strcpy(versionString, "3.0");
+                       break;
                default:
                        sprintf(versionString, "unknown ABI version 0x%02X", value);
        }
@@ -622,6 +629,28 @@ void Resolver::doFile(const ld::File& file)
                                }
                                break;
                }
+
+               // <rdar://problem/25680358> verify dylibs use same version of Swift language
+               if ( file.swiftVersion() != 0 ) {
+                       if ( _internal.swiftVersion == 0 ) {
+                               _internal.swiftVersion = file.swiftVersion();
+                       }
+                       else if ( file.swiftVersion() != _internal.swiftVersion ) {
+                               char fileVersion[64];
+                               char otherVersion[64];
+                               userReadableSwiftVersion(file.swiftVersion(), fileVersion);
+                               userReadableSwiftVersion(_internal.swiftVersion, otherVersion);
+                               if ( file.swiftVersion() > _internal.swiftVersion ) {
+                                       throwf("%s compiled with newer version of Swift language (%s) than previous files (%s)", 
+                                                  file.path(), fileVersion, otherVersion);
+                               }
+                               else {
+                                       throwf("%s compiled with older version of Swift language (%s) than previous files (%s)", 
+                                              file.path(), fileVersion, otherVersion);
+                               }
+                       }
+               }
+
                if ( _options.checkDylibsAreAppExtensionSafe() && !dylibFile->appExtensionSafe() ) {
                        warning("linking against a dylib which is not safe for use in application extensions: %s", file.path());
                }
@@ -730,9 +759,14 @@ void Resolver::doAtom(const ld::Atom& atom)
                        const std::vector<Options::AliasPair>& aliases = _options.cmdLineAliases();
                        for (std::vector<Options::AliasPair>::const_iterator it=aliases.begin(); it != aliases.end(); ++it) {
                                if ( strcmp(it->realName, atom.name()) == 0 ) {
-                                       const AliasAtom* alias = new AliasAtom(atom, it->alias);
-                                       _aliasesFromCmdLine.push_back(alias);
-                                       this->doAtom(*alias);
+                                       if ( strcmp(it->realName, it->alias) == 0 ) {
+                                               warning("ignoring alias of itself '%s'", it->realName);
+                                       }
+                                       else {
+                                               const AliasAtom* alias = new AliasAtom(atom, it->alias);
+                                               _aliasesFromCmdLine.push_back(alias);
+                                               this->doAtom(*alias);
+                                       }
                                }
                        }
                }
@@ -1188,11 +1222,13 @@ void Resolver::deadStripOptimize(bool force)
        }
        
        if ( _haveLLVMObjs && !force ) {
+                std::copy_if(_atoms.begin(), _atoms.end(), std::back_inserter(_internal.deadAtoms), NotLiveLTO() );
                // <rdar://problem/9777977> don't remove combinable atoms, they may come back in lto output
                _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLiveLTO()), _atoms.end());
                _symbolTable.removeDeadAtoms();
        }
        else {
+                std::copy_if(_atoms.begin(), _atoms.end(), std::back_inserter(_internal.deadAtoms), NotLive() );
                _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLive()), _atoms.end());
        }
 
@@ -1697,6 +1733,10 @@ void Resolver::linkTimeOptimize()
        lto::OptimizeOptions optOpt;
        optOpt.outputFilePath                           = _options.outputFilePath();
        optOpt.tmpObjectFilePath                        = _options.tempLtoObjectPath();
+       optOpt.ltoCachePath                                     = _options.ltoCachePath();
+       optOpt.ltoPruneInterval                         = _options.ltoPruneInterval();
+       optOpt.ltoPruneAfter                            = _options.ltoPruneAfter();
+       optOpt.ltoMaxCacheSize                          = _options.ltoMaxCacheSize();
        optOpt.preserveAllGlobals                       = _options.allGlobalsAreDeadStripRoots() || _options.hasExportRestrictList();
        optOpt.verbose                                          = _options.verbose();
        optOpt.saveTemps                                        = _options.saveTempFiles();
@@ -1718,6 +1758,7 @@ void Resolver::linkTimeOptimize()
        optOpt.arch                                                     = _options.architecture();
        optOpt.mcpu                                                     = _options.mcpuLTO();
        optOpt.platform                                         = _options.platform();
+       optOpt.minOSVersion                                     = _options.minOSversion();
        optOpt.llvmOptions                                      = &_options.llvmOptions();
        optOpt.initialUndefines                         = &_options.initialUndefines();
        
@@ -1827,6 +1868,12 @@ void Resolver::tweakWeakness()
        }
 }
 
+void Resolver::buildArchivesList()
+{
+       // Determine which archives were linked and update the internal state.
+       _inputFiles.archives(_internal);
+}
+
 void Resolver::dumpAtoms() 
 {
        fprintf(stderr, "Resolver all atoms:\n");
@@ -1853,6 +1900,7 @@ void Resolver::resolve()
        this->fillInInternalState();
        this->tweakWeakness();
     _symbolTable.checkDuplicateSymbols();
+       this->buildArchivesList();
 }
 
 
index 6631d11e7d46aaee5394e69f5b6e8f3d01669a76..06b7a8b0f145702c88e304a1bc473b8fe7ff83c3 100644 (file)
@@ -72,7 +72,7 @@ public:
                
                void                            resolve();
 
-       
+
 private:
        struct WhyLiveBackChain
        {
@@ -102,6 +102,7 @@ private:
        void                                    remainingUndefines(std::vector<const char*>&);
        bool                                    printReferencedBy(const char* name, SymbolTable::IndirectBindingSlot slot);
        void                                    tweakWeakness();
+       void                                    buildArchivesList();
        void                                    doLinkerOption(const std::vector<const char*>& linkerOption, const char* fileName);
        void                                    dumpAtoms();
 
index a856598108348ed33ea12dd04a53cfa2693658bf..a8d2276651f078ca402709b4a81c7f94696599e3 100644 (file)
@@ -118,6 +118,8 @@ public:
 
        virtual                                                                 ~InternalState() {}
 private:
+       bool                                                                    inMoveRWChain(const ld::Atom& atom, const char* filePath, const char*& dstSeg, bool& wildCardMatch);
+       bool                                                                    inMoveROChain(const ld::Atom& atom, const char* filePath, const char*& dstSeg, bool& wildCardMatch);
 
        class FinalSection : public ld::Internal::FinalSection 
        {
@@ -161,6 +163,7 @@ private:
        SectionInToOut                  _sectionInToFinalMap;
        const Options&                  _options;
        bool                                    _atomsOrderedInSections;
+       std::unordered_map<const ld::Atom*, const char*> _pendingSegMove;
 };
 
 ld::Section    InternalState::FinalSection::_s_DATA_data( "__DATA", "__data",  ld::Section::typeUnclassified);
@@ -196,7 +199,7 @@ InternalState::FinalSection::FinalSection(const ld::Section& sect, uint32_t sect
          _segmentOrder(segmentOrder(sect, opts)),
          _sectionOrder(sectionOrder(sect, sectionsSeen, opts))
 {
-       //fprintf(stderr, "FinalSection(%s, %s) _segmentOrder=%d, _sectionOrder=%d\n", 
+       //fprintf(stderr, "FinalSection(%16s, %16s) _segmentOrder=%3d, _sectionOrder=0x%08X\n",
        //              this->segmentName(), this->sectionName(), _segmentOrder, _sectionOrder);
 }
 
@@ -290,18 +293,33 @@ uint32_t InternalState::FinalSection::segmentOrder(const ld::Section& sect, cons
                if ( strcmp(sect.segmentName(), "__DATA") == 0 ) 
                        return order.size()+2;
        }
+       else if ( options.outputKind() == Options::kStaticExecutable ) {
+               const std::vector<const char*>& order = options.segmentOrder();
+               for (size_t i=0; i != order.size(); ++i) {
+                       if ( strcmp(sect.segmentName(), order[i]) == 0 )
+                               return i+1;
+               }
+               if ( strcmp(sect.segmentName(), "__PAGEZERO") == 0 )
+                       return 0;
+               if ( strcmp(sect.segmentName(), "__TEXT") == 0 )
+                       return order.size()+1;
+               if ( strcmp(sect.segmentName(), "__DATA") == 0 )
+                       return order.size()+2;
+       }
        else {
                if ( strcmp(sect.segmentName(), "__PAGEZERO") == 0 ) 
                        return 0;
                if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) 
                        return 1;
+               if ( strcmp(sect.segmentName(), "__TEXT_EXEC") == 0 )
+                       return 2;
                // in -r mode, want __DATA  last so zerofill sections are at end
                if ( strcmp(sect.segmentName(), "__DATA") == 0 ) 
-                       return (options.outputKind() == Options::kObjectFile) ? 5 : 2;
+                       return (options.outputKind() == Options::kObjectFile) ? 6 : 3;
                if ( strcmp(sect.segmentName(), "__OBJC") == 0 ) 
-                       return 3;
-               if ( strcmp(sect.segmentName(), "__IMPORT") == 0 ) 
                        return 4;
+               if ( strcmp(sect.segmentName(), "__IMPORT") == 0 )
+                       return 5;
        }
        // layout non-standard segments in order seen (+100 to shift beyond standard segments)
        for (uint32_t i=0; i < _s_segmentsSeen.size(); ++i) {
@@ -336,16 +354,21 @@ uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint
                                        return 10;
                                else
                                        return 11;
+                       case ld::Section::typeNonStdCString:
+                               if ( (strcmp(sect.sectionName(), "__oslogstring") == 0) && options.makeEncryptable() )
+                                       return INT_MAX-1;
+                               else
+                                       return sectionsSeen+20;
                        case ld::Section::typeStub:
                                return 12;
                        case ld::Section::typeStubHelper:
                                return 13;
                        case ld::Section::typeLSDA:
-                               return INT_MAX-3;
+                               return INT_MAX-4;
                        case ld::Section::typeUnwindInfo:
-                               return INT_MAX-2;
+                               return INT_MAX-3;
                        case ld::Section::typeCFI:
-                               return INT_MAX-1;
+                               return INT_MAX-2;
                        case ld::Section::typeStubClose:
                                return INT_MAX;
                        default:
@@ -367,15 +390,15 @@ uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint
                        case ld::Section::typeTerminatorPointers:
                                return 13;
                        case ld::Section::typeTLVInitialValues:
-                               return INT_MAX-4; // need TLV zero-fill to follow TLV init values
+                               return INT_MAX-259; // need TLV zero-fill to follow TLV init values
                        case ld::Section::typeTLVZeroFill:
-                               return INT_MAX-3;
+                               return INT_MAX-258;
                        case ld::Section::typeZeroFill:
                                // make sure __huge is always last zerofill section
                                if ( strcmp(sect.sectionName(), "__huge") == 0 )
                                        return INT_MAX-1;
                                else
-                                       return INT_MAX-2;
+                                       return INT_MAX-256+sectionsSeen; // <rdar://problem/25448494> zero fill need to be last and in "seen" order
                        default:
                                // <rdar://problem/14348664> __DATA,__const section should be near __mod_init_func not __data
                                if ( strcmp(sect.sectionName(), "__const") == 0 )
@@ -418,7 +441,7 @@ uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint
        }
        // make sure zerofill in any other section is at end of segment
        if ( sect.type() == ld::Section::typeZeroFill )
-               return INT_MAX-1;
+               return INT_MAX-256+sectionsSeen;
        return sectionsSeen+20;
 }
 
@@ -537,10 +560,90 @@ bool InternalState::hasReferenceToWeakExternal(const ld::Atom& atom)
        return false;
 }
 
+bool InternalState::inMoveRWChain(const ld::Atom& atom, const char* filePath, const char*& dstSeg, bool& wildCardMatch)
+{
+       if ( !_options.hasDataSymbolMoves() )
+               return false;
+
+       auto pos = _pendingSegMove.find(&atom);
+       if ( pos != _pendingSegMove.end() ) {
+               dstSeg = pos->second;
+               return true;
+       }
+
+       bool result = false;
+       if ( _options.moveRwSymbol(atom.name(), filePath, dstSeg, wildCardMatch) )
+               result = true;
+
+       for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) {
+               if ( fit->kind == ld::Fixup::kindNoneFollowOn ) {
+                       if ( fit->binding == ld::Fixup::bindingDirectlyBound ) {
+                               if ( inMoveRWChain(*(fit->u.target), filePath, dstSeg, wildCardMatch) )
+                                       result = true;
+                       }
+               }
+       }
+
+       if ( result ) {
+               for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) {
+                       if ( fit->kind == ld::Fixup::kindNoneFollowOn ) {
+                               if ( fit->binding == ld::Fixup::bindingDirectlyBound ) {
+                                       _pendingSegMove[fit->u.target] = dstSeg;
+                               }
+                       }
+               }
+       }
+
+       return result;
+}
+
+
+bool InternalState::inMoveROChain(const ld::Atom& atom, const char* filePath, const char*& dstSeg, bool& wildCardMatch)
+{
+       if ( !_options.hasCodeSymbolMoves() )
+               return false;
+
+       auto pos = _pendingSegMove.find(&atom);
+       if ( pos != _pendingSegMove.end() ) {
+               dstSeg = pos->second;
+               return true;
+       }
+
+       bool result = false;
+       if ( _options.moveRoSymbol(atom.name(), filePath, dstSeg, wildCardMatch) )
+               result = true;
+
+       for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) {
+               if ( fit->kind == ld::Fixup::kindNoneFollowOn ) {
+                       if ( fit->binding == ld::Fixup::bindingDirectlyBound ) {
+                               if ( inMoveROChain(*(fit->u.target), filePath, dstSeg, wildCardMatch) )
+                                       result = true;
+                       }
+               }
+       }
+
+       if ( result ) {
+               for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) {
+                       if ( fit->kind == ld::Fixup::kindNoneFollowOn ) {
+                               if ( fit->binding == ld::Fixup::bindingDirectlyBound ) {
+                                       _pendingSegMove[fit->u.target] = dstSeg;
+                               }
+                       }
+               }
+       }
+
+       return result;
+}
+
+
+
+
 ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom)
 {
+       //fprintf(stderr, "addAtom: %s\n", atom.name());
        ld::Internal::FinalSection* fs = NULL;
-       const char* sectName = atom.section().sectionName();
+       const char* curSectName = atom.section().sectionName();
+       const char* curSegName = atom.section().segmentName();
        ld::Section::Type sectType = atom.section().type();
        const ld::File* f = atom.file();
        const char* path = (f != NULL) ? f->path() : NULL;
@@ -548,16 +651,15 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom)
                // tentative defintions don't have a real section name yet
                sectType = ld::Section::typeZeroFill;
                if ( _options.mergeZeroFill() )
-                       sectName = FinalSection::_s_DATA_zerofill.sectionName();
+                       curSectName = FinalSection::_s_DATA_zerofill.sectionName();
                else
-                       sectName = FinalSection::_s_DATA_common.sectionName();
+                       curSectName = FinalSection::_s_DATA_common.sectionName();
        }
        // Support for -move_to_r._segment
        if ( atom.symbolTableInclusion() == ld::Atom::symbolTableIn ) {
                const char* dstSeg;
-               //fprintf(stderr, "%s\n", atom.name());
                bool wildCardMatch;
-               if ( _options.moveRwSymbol(atom.name(), path, dstSeg, wildCardMatch) ) {
+               if ( inMoveRWChain(atom, path, dstSeg, wildCardMatch) ) {
                        if ( (sectType != ld::Section::typeZeroFill) 
                          && (sectType != ld::Section::typeUnclassified) 
                          && (sectType != ld::Section::typeTentativeDefs)
@@ -566,60 +668,59 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom)
                                        warning("cannot move symbol '%s' from file %s to segment '%s' because symbol is not data (is %d)", atom.name(), path, dstSeg, sectType);
                        }
                        else {
+                               curSegName = dstSeg;
                                if ( _options.traceSymbolLayout() )
-                                       printf("symbol '%s', -move_to_rw_segment mapped it to %s/%s\n", atom.name(), dstSeg, sectName);
-                               fs = this->getFinalSection(dstSeg, sectName, sectType);
+                                       printf("symbol '%s', -move_to_rw_segment mapped it to %s/%s\n", atom.name(), curSegName, curSectName);
+                               fs = this->getFinalSection(curSegName, curSectName, sectType);
                        }
                }
-               if ( (fs == NULL) && _options.moveRoSymbol(atom.name(), path, dstSeg, wildCardMatch) ) {
+               if ( (fs == NULL) && inMoveROChain(atom, path, dstSeg, wildCardMatch) ) {
                        if ( (sectType != ld::Section::typeCode)
                          && (sectType != ld::Section::typeUnclassified) ) {
                                if ( !wildCardMatch )
                                        warning("cannot move symbol '%s' from file %s to segment '%s' because symbol is not code (is %d)", atom.name(), path, dstSeg, sectType);
                        }
                        else {
+                               curSegName = dstSeg;
                                if ( _options.traceSymbolLayout() )
-                                       printf("symbol '%s', -move_to_ro_segment mapped it to %s/%s\n", atom.name(), dstSeg, sectName);
-                               fs = this->getFinalSection(dstSeg, sectName, ld::Section::typeCode);
+                                       printf("symbol '%s', -move_to_ro_segment mapped it to %s/%s\n", atom.name(), curSegName, curSectName);
+                               fs = this->getFinalSection(curSegName, curSectName, ld::Section::typeCode);
                        }
                }
        }
        // support for -rename_section and -rename_segment
-       if ( fs == NULL ) {
-               const std::vector<Options::SectionRename>& sectRenames = _options.sectionRenames();
-               const std::vector<Options::SegmentRename>& segRenames = _options.segmentRenames();
-               for ( std::vector<Options::SectionRename>::const_iterator it=sectRenames.begin(); it != sectRenames.end(); ++it) {
-                       if ( (strcmp(sectName, it->fromSection) == 0) && (strcmp(atom.section().segmentName(), it->fromSegment) == 0) ) {
-                               if ( _options.useDataConstSegment() && (strcmp(sectName, "__const") == 0)
-                                               && (strcmp(atom.section().segmentName(), "__DATA") == 0) && hasReferenceToWeakExternal(atom) ) {
-                                       // if __DATA,__const atom has pointer to weak external symbol, don't move to __DATA_CONST
-                                       fs = this->getFinalSection("__DATA", "__const_weak", sectType);
-                                       if ( _options.traceSymbolLayout() )
-                                               printf("symbol '%s', contains pointers to weak symbols, so mapped it to __DATA/_const_weak\n", atom.name());
-                               }
-                               else if ( _options.useDataConstSegment() && (sectType == ld::Section::typeNonLazyPointer) && hasReferenceToWeakExternal(atom) ) {
-                                       // if __DATA,__nl_symbol_ptr atom has pointer to weak external symbol, don't move to __DATA_CONST
-                                       fs = this->getFinalSection("__DATA", "__got_weak", sectType);
-                                       if ( _options.traceSymbolLayout() )
-                                               printf("symbol '%s', contains pointers to weak symbols, so mapped it to __DATA/__got_weak\n", atom.name());
-                               }
-                               else {
-                                       fs = this->getFinalSection(it->toSegment, it->toSection, sectType);
-                                       if ( _options.traceSymbolLayout() )
-                                               printf("symbol '%s', -rename_section mapped it to %s/%s\n", atom.name(), fs->segmentName(), fs->sectionName());
-                               }
+       for (const Options::SectionRename& rename : _options.sectionRenames()) {
+               if ( (strcmp(curSectName, rename.fromSection) == 0) && (strcmp(curSegName, rename.fromSegment) == 0) ) {
+                       if ( _options.useDataConstSegment() && (strcmp(curSectName, "__const") == 0) && (strcmp(curSegName, "__DATA") == 0) && hasReferenceToWeakExternal(atom) ) {
+                               // if __DATA,__const atom has pointer to weak external symbol, don't move to __DATA_CONST
+                               curSectName = "__const_weak";
+                               fs = this->getFinalSection(curSegName, curSectName, sectType);
+                               if ( _options.traceSymbolLayout() )
+                                       printf("symbol '%s', contains pointers to weak symbols, so mapped it to __DATA/__const_weak\n", atom.name());
                        }
-               }
-               if ( fs == NULL ) {
-                       for ( std::vector<Options::SegmentRename>::const_iterator it=segRenames.begin(); it != segRenames.end(); ++it) {
-                               if ( strcmp(atom.section().segmentName(), it->fromSegment) == 0 ) {
-                                       if ( _options.traceSymbolLayout() )
-                                               printf("symbol '%s', -rename_segment mapped it to %s/%s\n", atom.name(), it->toSegment, sectName);
-                                       fs = this->getFinalSection(it->toSegment, sectName, sectType);
-                               }
+                       else if ( _options.useDataConstSegment() && (sectType == ld::Section::typeNonLazyPointer) && hasReferenceToWeakExternal(atom) ) {
+                               // if __DATA,__nl_symbol_ptr atom has pointer to weak external symbol, don't move to __DATA_CONST
+                               curSectName = "__got_weak";
+                               fs = this->getFinalSection("__DATA", curSectName, sectType);
+                               if ( _options.traceSymbolLayout() )
+                                       printf("symbol '%s', contains pointers to weak symbols, so mapped it to __DATA/__got_weak\n", atom.name());
+                       }
+                       else {
+                               curSegName = rename.toSegment;
+                               curSectName = rename.toSection;
+                               fs = this->getFinalSection(rename.toSegment, rename.toSection, sectType);
+                               if ( _options.traceSymbolLayout() )
+                                       printf("symbol '%s', -rename_section mapped it to %s/%s\n", atom.name(), fs->segmentName(), fs->sectionName());
                        }
                }
        }
+       for (const Options::SegmentRename& rename : _options.segmentRenames()) {
+               if ( strcmp(curSegName, rename.fromSegment) == 0 ) {
+                       if ( _options.traceSymbolLayout() )
+                               printf("symbol '%s', -rename_segment mapped it to %s/%s\n", atom.name(), rename.toSegment, curSectName);
+                       fs = this->getFinalSection(rename.toSegment, curSectName, sectType);
+               }
+       }
 
        // if no override, use default location
        if ( fs == NULL ) {
@@ -628,7 +729,7 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom)
                        printf("symbol '%s', use default mapping to %s/%s\n", atom.name(), fs->segmentName(), fs->sectionName());
        }
 
-       //fprintf(stderr, "InternalState::doAtom(%p), name=%s, sect=%s, finalsect=%p\n", &atom, atom.name(), atom.section().sectionName(), fs);
+       //fprintf(stderr, "InternalState::doAtom(%p), name=%s, sect=%s, finalseg=%s\n", &atom, atom.name(), atom.section().sectionName(), fs->segmentName());
 #ifndef NDEBUG
        validateFixups(atom);
 #endif
@@ -861,6 +962,21 @@ void InternalState::setSectionSizesAndAlignments()
                                this->hasThreadLocalVariableDefinitions = true;
                }
        }
+
+       // <rdar://problem/24221680> All __thread_data and __thread_bss sections must have same alignment
+       uint8_t maxThreadAlign = 0;
+       for (ld::Internal::FinalSection* sect : sections) {
+               if ( (sect->type() == ld::Section::typeTLVInitialValues) || (sect->type() == ld::Section::typeTLVZeroFill) ) {
+                       if ( sect->alignment > maxThreadAlign )
+                               maxThreadAlign = sect->alignment;
+               }
+       }
+       for (ld::Internal::FinalSection* sect : sections) {
+               if ( (sect->type() == ld::Section::typeTLVInitialValues) || (sect->type() == ld::Section::typeTLVZeroFill) ) {
+                       sect->alignment = maxThreadAlign;
+               }
+       }
+
 }
 
 uint64_t InternalState::assignFileOffsets() 
@@ -1270,7 +1386,9 @@ int main(int argc, const char* argv[])
                }
        }
        catch (const char* msg) {
-               if ( archInferred )
+               if ( strstr(msg, "malformed") != NULL )
+                       fprintf(stderr, "ld: %s\n", msg);
+               else if ( archInferred )
                        fprintf(stderr, "ld: %s for inferred architecture %s\n", msg, archName);
                else if ( showArch )
                        fprintf(stderr, "ld: %s for architecture %s\n", msg, archName);
index 89c091b84c9449821e0f464d4ed0e10fb1280652..0db1b7e58d57e3f11549dd3a96b66590a8c65627 100644 (file)
@@ -93,7 +93,7 @@ public:
                
                Ordinal (uint64_t ordinal) : _ordinal(ordinal) {}
                
-               enum { kArgListPartition=0, kIndirectDylibPartition=1, kLTOPartition = 2, kLinkerOptionPartition = 2, InvalidParition=0xffff };
+               enum { kArgListPartition=0, kIndirectDylibPartition=1, kLTOPartition = 2, kLinkerOptionPartition = 3, InvalidParition=0xffff };
                Ordinal(uint16_t partition, uint16_t majorIndex, uint16_t minorIndex, uint16_t counter) {
                        _ordinal = ((uint64_t)partition<<48) | ((uint64_t)majorIndex<<32) | ((uint64_t)minorIndex<<16) | ((uint64_t)counter<<0);
                }
@@ -102,14 +102,14 @@ public:
                const uint16_t  majorIndex() const              { return (_ordinal>>32)&0xffff; }
                const uint16_t  minorIndex() const              { return (_ordinal>>16)&0xffff; }
                const uint16_t  counter() const                 { return (_ordinal>>00)&0xffff; }
-               
+
                const Ordinal nextMajorIndex()          const { assert(majorIndex() < 0xffff); return Ordinal(_ordinal+((uint64_t)1<<32)); }
                const Ordinal nextMinorIndex()          const { assert(minorIndex() < 0xffff); return Ordinal(_ordinal+((uint64_t)1<<16)); }
                const Ordinal nextCounter()             const { assert(counter() < 0xffff); return Ordinal(_ordinal+((uint64_t)1<<0)); }
                
        public:
                Ordinal() : _ordinal(0) {};
-               
+
                static const Ordinal NullOrdinal()              { return Ordinal((uint64_t)0); }
                
                const bool validOrdinal() const { return _ordinal != 0; }
@@ -126,7 +126,7 @@ public:
                // Thus, an object pulled in from a .a that was listed in a file list could use all three fields.
                static const Ordinal makeArgOrdinal(uint16_t argIndex) { return Ordinal(kArgListPartition, argIndex, 0, 0); };
                const Ordinal nextFileListOrdinal() const { return nextMinorIndex(); }
-               const Ordinal archiveOrdinalWithMemberIndex(uint16_t index) const { return Ordinal(kArgListPartition, majorIndex(), minorIndex(), index); }
+               const Ordinal archiveOrdinalWithMemberIndex(uint16_t memberIndex) const { return Ordinal(partition(), majorIndex(), minorIndex(), memberIndex); }
                
                // For indirect libraries the partition is IndirectDylibPartition and the counter is used or order the libraries.
                static const ld::File::Ordinal indirectDylibBase() { return Ordinal(kIndirectDylibPartition, 0, 0, 0); }
@@ -137,8 +137,8 @@ public:
 
                // For linker options embedded in object files
                static const ld::File::Ordinal linkeOptionBase() { return Ordinal(kIndirectDylibPartition, 1, 0, 0); }
-               const Ordinal nextLinkerOptionOrdinal() { nextCounter(); return *this; };
-               
+               const Ordinal nextLinkerOptionOrdinal() { return nextCounter(); };
+
        };
        
        typedef enum { Reloc, Dylib, Archive, Other } Type;
@@ -152,6 +152,7 @@ public:
        virtual bool                                            forEachAtom(AtomHandler&) const = 0;
        virtual bool                                            justInTimeforEachAtom(const char* name, AtomHandler&) const = 0;
        virtual ObjcConstraint                          objCConstraint() const                  { return objcConstraintNone; }
+    virtual bool                                               objcHasCategoryClassPropertiesField() const { return false; }
        virtual uint8_t                                         swiftVersion() const                    { return 0; }
        virtual uint32_t                                        cpuSubType() const              { return 0; }
        virtual uint32_t                                        subFileCount() const    { return 1; }
@@ -173,11 +174,11 @@ private:
 //
 enum MacVersionMin { macVersionUnset=0, mac10_4=0x000A0400, mac10_5=0x000A0500, 
                                                mac10_6=0x000A0600, mac10_7=0x000A0700, mac10_8=0x000A0800,
-                                               mac10_9=0x000A0900, mac10_Future=0x10000000 };
+                                               mac10_9=0x000A0900, mac10_12=0x000A0C00, mac10_Future=0x10000000 };
 enum IOSVersionMin { iOSVersionUnset=0, iOS_2_0=0x00020000, iOS_3_1=0x00030100, 
                                                iOS_4_2=0x00040200, iOS_4_3=0x00040300, iOS_5_0=0x00050000,
                                                iOS_6_0=0x00060000, iOS_7_0=0x00070000, iOS_8_0=0x00080000,
-                                               iOS_9_0=0x00090000, iOS_Future=0x10000000};
+                                               iOS_9_0=0x00090000, iOS_10_0=0x000A0000, iOS_Future=0x10000000};
 enum WatchOSVersionMin  { wOSVersionUnset=0, wOS_1_0=0x00010000, wOS_2_0=0x00020000 };
 
 
@@ -242,13 +243,13 @@ namespace dylib {
                {
                public:
                        virtual                         ~DylibHandler() {}
-                       virtual File*           findDylib(const char* installPath, const char* fromPath) = 0;
+                       virtual File*           findDylib(const char* installPath, const ld::dylib::File* fromDylib, bool speculative) = 0;
                };
                        
                                                                                        File(const char* pth, time_t modTime, Ordinal ord)
                                                                                                : ld::File(pth, modTime, ord, Dylib), _dylibInstallPath(NULL), _frameworkName(NULL),
                                                                                                _dylibTimeStamp(0), _dylibCurrentVersion(0), _dylibCompatibilityVersion(0),
-                                                                                               _explicitlyLinked(false), _implicitlyLinked(false),
+                                                                                               _explicitlyLinked(false), _implicitlyLinked(false), _speculativelyLoaded(false),
                                                                                                _lazyLoadedDylib(false), _forcedWeakLinked(false), _reExported(false),
                                                                                                _upward(false), _dead(false) { }
                                const char*                                     installPath() const                     { return _dylibInstallPath; }
@@ -260,7 +261,9 @@ namespace dylib {
                                bool                                            explicitlyLinked() const        { return _explicitlyLinked; }
                                void                                            setImplicitlyLinked()           { _implicitlyLinked = true; }
                                bool                                            implicitlyLinked() const        { return _implicitlyLinked; }
-                               
+                               void                                            setSpeculativelyLoaded()        { _speculativelyLoaded = true; }
+                               bool                                            speculativelyLoaded() const     { return _speculativelyLoaded; }
+
                                // attributes of how dylib will be used when linked
                                void                                            setWillBeLazyLoadedDylb()               { _lazyLoadedDylib = true; }
                                bool                                            willBeLazyLoadedDylib() const   { return _lazyLoadedDylib; }
@@ -278,6 +281,7 @@ namespace dylib {
                virtual bool                                            providedExportAtom() const = 0;
                virtual const char*                                     parentUmbrella() const = 0;
                virtual const std::vector<const char*>* allowableClients() const = 0;
+               virtual const std::vector<const char*>& rpaths() const = 0;
                virtual bool                                            hasWeakExternals() const = 0;
                virtual bool                                            deadStrippable() const = 0;
                virtual bool                                            hasWeakDefinition(const char* name) const = 0;
@@ -294,6 +298,7 @@ namespace dylib {
                uint32_t                                                        _dylibCompatibilityVersion;
                bool                                                            _explicitlyLinked;
                bool                                                            _implicitlyLinked;
+               bool                                                            _speculativelyLoaded;
                bool                                                            _lazyLoadedDylib;
                bool                                                            _forcedWeakLinked;
                bool                                                            _reExported;
@@ -748,6 +753,9 @@ public:
        bool                                                                    finalAddressMode() const    { return (_mode == modeFinalAddress); }
 #endif
        virtual const File*                                             file() const = 0;
+       // Return the original file this atom belongs to, for instance for an LTO atom,
+       // file() would return the LTO MachO file instead of the original bitcode file.
+       virtual const ld::File*                             originalFile() const       { return file(); }
        virtual const char*                                             translationUnitSource() const { return NULL; }
        virtual const char*                                             name() const = 0;
        virtual uint64_t                                                objectAddress() const = 0;
@@ -888,14 +896,16 @@ public:
 
        std::vector<FinalSection*>                                      sections;
        std::vector<ld::dylib::File*>                           dylibs;
+       std::vector<std::string>                                        archivePaths;
        std::vector<ld::relocatable::File::Stab>        stabs;
        AtomToSection                                                           atomToSection;          
+       CStringSet                                                                      unprocessedLinkerOptionLibraries;
+       CStringSet                                                                      unprocessedLinkerOptionFrameworks;
        CStringSet                                                                      linkerOptionLibraries;
        CStringSet                                                                      linkerOptionFrameworks;
-       CStringSet                                                                      linkerOptionLibrariesProcessed;
-       CStringSet                                                                      linkerOptionFrameworksProcessed;
        std::vector<const ld::Atom*>                            indirectBindingTable;
        std::vector<const ld::relocatable::File*>       filesWithBitcode;
+       std::vector<const ld::Atom*>                            deadAtoms;
        std::unordered_set<const char*>                         allUndefProxies;
        const ld::dylib::File*                                          bundleLoader;
        const Atom*                                                                     entryPoint;
@@ -917,7 +927,7 @@ public:
        bool                                                                            someObjectHasOptimizationHints;
        bool                                                                            dropAllBitcode;
        bool                                                                            embedMarkerOnly;
-       std::string                                                                     ltoBitcodePath;
+       std::vector<std::string>                                        ltoBitcodePath;
 };
 
 
index 900453040e4a511e6a68468173cff94b5794fa9b..a1fec1a6f9f867421abc107445201a63b683182e 100644 (file)
@@ -45,7 +45,6 @@
 
 namespace archive {
 
-typedef const struct ranlib* ConstRanLibPtr;
 
 // forward reference
 template <typename A> class File;
@@ -112,26 +111,30 @@ private:
        struct MemberState { ld::relocatable::File* file; const Entry *entry; bool logged; bool loaded; uint32_t index;};
        bool                                                                                    loadMember(MemberState& state, ld::File::AtomHandler& handler, const char *format, ...) const;
 
-       typedef std::unordered_map<const char*, const struct ranlib*, ld::CStringHash, ld::CStringEquals> NameToEntryMap;
+       typedef std::unordered_map<const char*, uint64_t, ld::CStringHash, ld::CStringEquals> NameToOffsetMap;
 
        typedef typename A::P                                                   P;
        typedef typename A::P::E                                                E;
 
        typedef std::map<const class Entry*, MemberState> MemberToStateMap;
 
-       const struct ranlib*                                                    ranlibHashSearch(const char* name) const;
        MemberState&                                                                    makeObjectFileForMember(const Entry* member) const;
        bool                                                                                    memberHasObjCCategories(const Entry* member) const;
        void                                                                                    dumpTableOfContents();
        void                                                                                    buildHashTable();
-
+#ifdef SYMDEF_64
+       void                                                                                    buildHashTable64();
+#endif
        const uint8_t*                                                                  _archiveFileContent;
        uint64_t                                                                                _archiveFilelength;
        const struct ranlib*                                                    _tableOfContents;
+#ifdef SYMDEF_64
+       const struct ranlib_64*                                                 _tableOfContents64;
+#endif
        uint32_t                                                                                _tableOfContentCount;
        const char*                                                                             _tableOfContentStrings;
        mutable MemberToStateMap                                                _instantiatedEntries;
-       NameToEntryMap                                                                  _hashTable;
+       NameToOffsetMap                                                                 _hashTable;
        const bool                                                                              _forceLoadAll;
        const bool                                                                              _forceLoadObjC;
        const bool                                                                              _forceLoadThis;
@@ -225,7 +228,6 @@ template <> cpu_type_t File<x86_64>::architecture() { return CPU_TYPE_X86_64; }
 template <> cpu_type_t File<arm>::architecture()    { return CPU_TYPE_ARM; }
 template <> cpu_type_t File<arm64>::architecture()  { return CPU_TYPE_ARM64; }
 
-
 template <typename A>
 bool File<A>::validMachOFile(const uint8_t* fileContent, uint64_t fileLength, const mach_o::relocatable::ParserOptions& opts)
 {      
@@ -256,6 +258,10 @@ bool File<A>::validFile(const uint8_t* fileContent, uint64_t fileLength, const m
                // skip option table-of-content member
                if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) )
                        continue;
+#ifdef SYMDEF_64
+               if ( (p==start) && ((strcmp(memberName, SYMDEF_64_SORTED) == 0) || (strcmp(memberName, SYMDEF_64) == 0)) )
+                       continue;
+#endif
                // archive is valid if first .o file is valid
                return (validMachOFile(p->content(), p->contentSize(), opts) || validLTOFile(p->content(), p->contentSize(), opts));
        }       
@@ -269,7 +275,11 @@ File<A>::File(const uint8_t fileContent[], uint64_t fileLength, const char* pth,
                                        ld::File::Ordinal ord, const ParserOptions& opts)
  : ld::archive::File(strdup(pth), modTime, ord),
        _archiveFileContent(fileContent), _archiveFilelength(fileLength), 
-       _tableOfContents(NULL), _tableOfContentCount(0), _tableOfContentStrings(NULL), 
+       _tableOfContents(NULL),
+#ifdef SYMDEF_64
+       _tableOfContents64(NULL),
+#endif
+       _tableOfContentCount(0), _tableOfContentStrings(NULL),
        _forceLoadAll(opts.forceLoadAll), _forceLoadObjC(opts.forceLoadObjC), 
        _forceLoadThis(opts.forceLoadThisArchive), _objc2ABI(opts.objcABI2), _verboseLoad(opts.verboseLoad), 
        _logAllFiles(opts.logAllFiles), _objOpts(opts.objOpts)
@@ -292,6 +302,19 @@ File<A>::File(const uint8_t fileContent[], uint64_t fileLength, const char* pth,
                                throw "malformed archive, perhaps wrong architecture";
                        this->buildHashTable();
                }
+#ifdef SYMDEF_64
+               else if ( (strcmp(memberName, SYMDEF_64_SORTED) == 0) || (strcmp(memberName, SYMDEF_64) == 0) ) {
+                       const uint8_t* contents = firstMember->content();
+                       uint64_t ranlibArrayLen = E::get64(*((uint64_t*)contents));
+                       _tableOfContents64 = (const struct ranlib_64*)&contents[8];
+                       _tableOfContentCount = ranlibArrayLen / sizeof(struct ranlib_64);
+                       _tableOfContentStrings = (const char*)&contents[ranlibArrayLen+16];
+                       if ( ((uint8_t*)(&_tableOfContents[_tableOfContentCount]) > &fileContent[fileLength])
+                               || ((uint8_t*)_tableOfContentStrings > &fileContent[fileLength]) )
+                               throw "malformed archive, perhaps wrong architecture";
+                       this->buildHashTable64();
+               }
+#endif
                else
                        throw "archive has no table of contents";
        }
@@ -428,15 +451,19 @@ bool File<A>::forEachAtom(ld::File::AtomHandler& handler) const
                        p->getName(memberName, sizeof(memberName));
                        if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) )
                                continue;
+#ifdef SYMDEF_64
+                       if ( (p==start) && ((strcmp(memberName, SYMDEF_64_SORTED) == 0) || (strcmp(memberName, SYMDEF_64) == 0)) )
+                               continue;
+#endif
                        MemberState& state = this->makeObjectFileForMember(p);
                        didSome |= loadMember(state, handler, "%s forced load of %s(%s)\n", _forceLoadThis ? "-force_load" : "-all_load", this->path(), memberName);
                }
        }
        else if ( _forceLoadObjC ) {
                // call handler on all .o files in this archive containing objc classes
-               for(typename NameToEntryMap::const_iterator it = _hashTable.begin(); it != _hashTable.end(); ++it) {
-                       if ( (strncmp(it->first, ".objc_c", 7) == 0) || (strncmp(it->first, "_OBJC_CLASS_$_", 14) == 0) ) {
-                               const Entry* member = (Entry*)&_archiveFileContent[E::get32(it->second->ran_off)];
+               for (const auto& entry : _hashTable) {
+                       if ( (strncmp(entry.first, ".objc_c", 7) == 0) || (strncmp(entry.first, "_OBJC_CLASS_$_", 14) == 0) ) {
+                               const Entry* member = (Entry*)&_archiveFileContent[entry.second];
                                MemberState& state = this->makeObjectFileForMember(member);
                                char memberName[256];
                                member->getName(memberName, sizeof(memberName));
@@ -452,14 +479,31 @@ bool File<A>::forEachAtom(ld::File::AtomHandler& handler) const
                        // skip table-of-content member
                        if ( (member==start) && ((strcmp(mname, SYMDEF_SORTED) == 0) || (strcmp(mname, SYMDEF) == 0)) )
                                continue;
-                       MemberState& state = this->makeObjectFileForMember(member);
-                       // only look at files not already loaded
-                       if ( ! state.loaded ) {
-                               if ( this->memberHasObjCCategories(member) ) {
+#ifdef SYMDEF_64
+                       if ( (member==start) && ((strcmp(mname, SYMDEF_64_SORTED) == 0) || (strcmp(mname, SYMDEF_64) == 0)) )
+                               continue;
+#endif
+                       if ( validMachOFile(member->content(), member->contentSize(), _objOpts) ) {
+                               MemberState& state = this->makeObjectFileForMember(member);
+                               // only look at files not already loaded
+                               if ( ! state.loaded ) {
+                                       if ( this->memberHasObjCCategories(member) ) {
+                                               MemberState& state = this->makeObjectFileForMember(member);
+                                               char memberName[256];
+                                               member->getName(memberName, sizeof(memberName));
+                                               didSome |= loadMember(state, handler, "-ObjC forced load of %s(%s)\n", this->path(), memberName);
+                                       }
+                               }
+                       }
+                       else if ( validLTOFile(member->content(), member->contentSize(), _objOpts) ) {
+                               if ( lto::hasObjCCategory(member->content(), member->contentSize()) ) {
                                        MemberState& state = this->makeObjectFileForMember(member);
-                                       char memberName[256];
-                                       member->getName(memberName, sizeof(memberName));
-                                       didSome |= loadMember(state, handler, "-ObjC forced load of %s(%s)\n", this->path(), memberName);
+                                       // only look at files not already loaded
+                                       if ( ! state.loaded ) {
+                                               char memberName[256];
+                                               member->getName(memberName, sizeof(memberName));
+                                               didSome |= loadMember(state, handler, "-ObjC forced load of %s(%s)\n", this->path(), memberName);
+                                       }
                                }
                        }
                }
@@ -475,16 +519,16 @@ bool File<A>::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& han
                return false;
        
        // do a hash search of table of contents looking for requested symbol
-       const struct ranlib* result = ranlibHashSearch(name);
-       if ( result != NULL ) {
-               const Entry* member = (Entry*)&_archiveFileContent[E::get32(result->ran_off)];
-               MemberState& state = this->makeObjectFileForMember(member);
-               char memberName[256];
-               member->getName(memberName, sizeof(memberName));
-               return loadMember(state, handler, "%s forced load of %s(%s)\n", name, this->path(), memberName);
-       }
-       //fprintf(stderr, "%s NOT found in archive %s\n", name, this->path());
-       return false;
+       const auto& pos = _hashTable.find(name);
+       if ( pos == _hashTable.end() )
+               return false;
+
+       // do a hash search of table of contents looking for requested symbol
+       const Entry* member = (Entry*)&_archiveFileContent[pos->second];
+       MemberState& state = this->makeObjectFileForMember(member);
+       char memberName[256];
+       member->getName(memberName, sizeof(memberName));
+       return loadMember(state, handler, "%s forced load of %s(%s)\n", name, this->path(), memberName);
 }
 
 class CheckIsDataSymbolHandler : public ld::File::AtomHandler
@@ -514,38 +558,27 @@ bool File<A>::justInTimeDataOnlyforEachAtom(const char* name, ld::File::AtomHand
                return false;
        
        // do a hash search of table of contents looking for requested symbol
-       const struct ranlib* result = ranlibHashSearch(name);
-       if ( result != NULL ) {
-               const Entry* member = (Entry*)&_archiveFileContent[E::get32(result->ran_off)];
-               MemberState& state = this->makeObjectFileForMember(member);
-               // only call handler for each member once
-               if ( ! state.loaded ) {
-                       CheckIsDataSymbolHandler checker(name);
-                       state.file->forEachAtom(checker);
-                       if ( checker.symbolIsDataDefinition() ) {
-                               char memberName[256];
-                               member->getName(memberName, sizeof(memberName));
-                               return loadMember(state, handler, "%s forced load of %s(%s)\n", name, this->path(), memberName);
-                       }
+       const auto& pos = _hashTable.find(name);
+       if ( pos == _hashTable.end() )
+               return false;
+
+       const Entry* member = (Entry*)&_archiveFileContent[pos->second];
+       MemberState& state = this->makeObjectFileForMember(member);
+       // only call handler for each member once
+       if ( ! state.loaded ) {
+               CheckIsDataSymbolHandler checker(name);
+               state.file->forEachAtom(checker);
+               if ( checker.symbolIsDataDefinition() ) {
+                       char memberName[256];
+                       member->getName(memberName, sizeof(memberName));
+                       return loadMember(state, handler, "%s forced load of %s(%s)\n", name, this->path(), memberName);
                }
        }
+
        //fprintf(stderr, "%s NOT found in archive %s\n", name, this->path());
        return false;
 }
 
-
-typedef const struct ranlib* ConstRanLibPtr;
-
-template <typename A>
-ConstRanLibPtr  File<A>::ranlibHashSearch(const char* name) const
-{
-       typename NameToEntryMap::const_iterator pos = _hashTable.find(name);
-       if ( pos != _hashTable.end() )
-               return pos->second;
-       else
-               return NULL;
-}
-
 template <typename A>
 void File<A>::buildHashTable()
 {
@@ -554,16 +587,38 @@ void File<A>::buildHashTable()
        for (int i = _tableOfContentCount-1; i >= 0; --i) {
                const struct ranlib* entry = &_tableOfContents[i];
                const char* entryName = &_tableOfContentStrings[E::get32(entry->ran_un.ran_strx)];
-               if ( E::get32(entry->ran_off) > _archiveFilelength ) {
+               uint64_t offset = E::get32(entry->ran_off);
+               if ( offset > _archiveFilelength ) {
                        throwf("malformed archive TOC entry for %s, offset %d is beyond end of file %lld\n",
                                entryName, entry->ran_off, _archiveFilelength);
                }
                
                //const Entry* member = (Entry*)&_archiveFileContent[E::get32(entry->ran_off)];
                //fprintf(stderr, "adding hash %d, %s -> %p\n", i, entryName, entry);
-               _hashTable[entryName] = entry;
+               _hashTable[entryName] = offset;
+       }
+}
+
+#ifdef SYMDEF_64
+template <typename A>
+void File<A>::buildHashTable64()
+{
+       // walk through list backwards, adding/overwriting entries
+       // this assures that with duplicates those earliest in the list will be found
+       for (int i = _tableOfContentCount-1; i >= 0; --i) {
+               const struct ranlib_64* entry = &_tableOfContents64[i];
+               const char* entryName = &_tableOfContentStrings[E::get64(entry->ran_un.ran_strx)];
+               uint64_t offset = E::get64(entry->ran_off);
+               if ( offset > _archiveFilelength ) {
+                       throwf("malformed archive TOC entry for %s, offset %lld is beyond end of file %lld\n",
+                               entryName, entry->ran_off, _archiveFilelength);
+               }
+
+               //fprintf(stderr, "adding hash %d: %s -> 0x%0llX\n", i, entryName, offset);
+               _hashTable[entryName] = offset;
        }
 }
+#endif
 
 template <typename A>
 void File<A>::dumpTableOfContents()
index 301074f57d3e353b24b81d9fd8e6d8e7200c5666..529e4ab691cd025258779d9e4200ff7e5397d347 100644 (file)
@@ -124,7 +124,7 @@ class File : public ld::dylib::File
 {
 public:
        File(const char* path, time_t mTime, ld::File::Ordinal ordinal, Options::Platform platform,
-                uint32_t linkMinOSVersion, bool linkingFlatNamespace, bool hoistImplicitPublicDylibs,
+                uint32_t linkMinOSVersion, bool allowWeakImports, bool linkingFlatNamespace, bool hoistImplicitPublicDylibs,
                 bool allowSimToMacOSX, bool addVers);
 
        // overrides of ld::File
@@ -142,6 +142,7 @@ public:
        virtual bool                                                    providedExportAtom() const      override final { return _providedAtom; }
        virtual const char*                                             parentUmbrella() const override final { return _parentUmbrella; }
        virtual const std::vector<const char*>* allowableClients() const override final { return _allowableClients.empty() ? nullptr : &_allowableClients; }
+    virtual const std::vector<const char*>&    rpaths() const override final { return _rpaths; }
        virtual bool                                                    hasWeakExternals() const override final { return _hasWeakExports; }
        virtual bool                                                    deadStrippable() const override final { return _deadStrippable; }
        virtual bool                                                    hasWeakDefinition(const char* name) const override final;
@@ -190,7 +191,6 @@ private:
 
 protected:
        bool                                            isPublicLocation(const char* path) const;
-       void                                            addSymbol(const char* name, bool weak = false, bool tlv = false, pint_t address = 0);
 
 private:
        ld::Section                                                     _importProxySection;
@@ -204,6 +204,7 @@ protected:
        std::vector<Dependent>                          _dependentDylibs;
        ImportAtom<A>*                                          _importAtom;
        std::vector<const char*>                        _allowableClients;
+       std::vector<const char*>                        _rpaths;
        const char*                                                     _parentUmbrella;
        std::unique_ptr<ld::Bitcode>            _bitcode;
        const Options::Platform                         _platform;
@@ -222,7 +223,7 @@ protected:
        bool                                                            _deadStrippable;
        bool                                                            _hasPublicInstallName;
        bool                                                            _appExtensionSafe;
-
+    const bool                          _allowWeakImports;
        const bool                                                      _allowSimToMacOSXLinking;
        const bool                                                      _addVersionLoadCommand;
 
@@ -234,7 +235,7 @@ bool File<A>::_s_logHashtable = false;
 
 template <typename A>
 File<A>::File(const char* path, time_t mTime, ld::File::Ordinal ord,  Options::Platform platform,
-                         uint32_t linkMinOSVersion, bool linkingFlatNamespace,
+                         uint32_t linkMinOSVersion, bool allowWeakImports, bool linkingFlatNamespace,
                          bool hoistImplicitPublicDylibs,
                          bool allowSimToMacOSX, bool addVers)
        : ld::dylib::File(path, mTime, ord),
@@ -260,6 +261,7 @@ File<A>::File(const char* path, time_t mTime, ld::File::Ordinal ord,  Options::P
          _deadStrippable(false),
          _hasPublicInstallName(false),
          _appExtensionSafe(false),
+      _allowWeakImports(allowWeakImports),
          _allowSimToMacOSXLinking(allowSimToMacOSX),
          _addVersionLoadCommand(addVers)
 {
@@ -388,7 +390,7 @@ void File<A>::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, b
                fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath());
        if ( _linkingFlat ) {
                for (auto &dep : _dependentDylibs)
-                       dep.dylib = (File<A>*)handler->findDylib(dep.path, this->path());
+                       dep.dylib = (File<A>*)handler->findDylib(dep.path, this, false);
        }
        else if ( _noRexports ) {
                // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do
@@ -400,7 +402,7 @@ void File<A>::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, b
                                if ( log )
                                        fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), dep.path);
                                // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child
-                               dep.dylib = (File<A>*)handler->findDylib(dep.path, this->path());
+                               dep.dylib = (File<A>*)handler->findDylib(dep.path, this, this->speculativelyLoaded());
                                if ( dep.dylib->hasPublicInstallName() && !dep.dylib->wrongOS() ) {
                                        // promote this child to be automatically added as a direct dependent if this already is
                                        if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(dep.path, dep.dylib->installPath()) == 0) ) {
@@ -425,7 +427,7 @@ void File<A>::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, b
                        }
                        else if ( !_explictReExportFound ) {
                                // see if child contains LC_SUB_FRAMEWORK with my name
-                               dep.dylib = (File<A>*)handler->findDylib(dep.path, this->path());
+                               dep.dylib = (File<A>*)handler->findDylib(dep.path, this, this->speculativelyLoaded());
                                const char* parentUmbrellaName = dep.dylib->parentUmbrella();
                                if ( parentUmbrellaName != nullptr ) {
                                        const char* parentName = this->path();
@@ -477,64 +479,6 @@ bool File<A>::isPublicLocation(const char* path) const
        return false;
 }
 
-template <typename A>
-void File<A>::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address)
-{
-       // symbols that start with $ld$ are meta-data to the static linker
-       // <rdar://problem/5182537> need way for ld and dyld to see different exported symbols in a dylib
-       if ( strncmp(name, "$ld$", 4) == 0 ) {
-               //    $ld$ <action> $ <condition> $ <symbol-name>
-               const char* symAction = &name[4];
-               const char* symCond = strchr(symAction, '$');
-               if ( symCond != nullptr ) {
-                       char curOSVers[16];
-                       sprintf(curOSVers, "$os%d.%d$", (_linkMinOSVersion >> 16), ((_linkMinOSVersion >> 8) & 0xFF));
-                       if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) {
-                               const char* symName = strchr(&symCond[1], '$');
-                               if ( symName != nullptr ) {
-                                       ++symName;
-                                       if ( strncmp(symAction, "hide$", 5) == 0 ) {
-                                               if ( _s_logHashtable )
-                                                       fprintf(stderr, "  adding %s to ignore set for %s\n", symName, this->path());
-                                               _ignoreExports.insert(strdup(symName));
-                                               return;
-                                       }
-                                       else if ( strncmp(symAction, "add$", 4) == 0 ) {
-                                               this->addSymbol(symName, weakDef);
-                                               return;
-                                       }
-                                       else if ( strncmp(symAction, "install_name$", 13) == 0 ) {
-                                               _dylibInstallPath = strdup(symName);
-                                               _installPathOverride = true;
-                                               // <rdar://problem/14448206> CoreGraphics redirects to ApplicationServices, but with wrong compat version
-                                               if ( strcmp(_dylibInstallPath, "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices") == 0 )
-                                                       _dylibCompatibilityVersion = Options::parseVersionNumber32("1.0");
-                                               return;
-                                       }
-                                       else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) {
-                                               _dylibCompatibilityVersion = Options::parseVersionNumber32(symName);
-                                               return;
-                                       }
-                                       else {
-                                               warning("bad symbol action: %s in dylib %s", name, this->path());
-                                       }
-                               }
-                       }
-               }
-               else {
-                       warning("bad symbol condition: %s in dylib %s", name, this->path());
-               }
-       }
-
-       // add symbol as possible export if we are not supposed to ignore it
-       if ( _ignoreExports.count(name) == 0 ) {
-               AtomAndWeak bucket = { nullptr, weakDef, tlv, address };
-               if ( this->_s_logHashtable )
-                       fprintf(stderr, "  adding %s to hash table for %s\n", name, this->path());
-               _atoms[strdup(name)] = bucket;
-       }
-}
-
 // <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB
 template <typename A>
 bool File<A>::allSymbolsAreWeakImported() const
index f0f5f1047cc8a7eee907b37e0e5539a83481b2c5..a5ab8217bc268d2f491ffd5775c3c8b4cda7db93 100644 (file)
@@ -36,6 +36,8 @@
 #include <map>
 #include <unordered_set>
 #include <unordered_map>
+#include <iostream>
+#include <fstream>
 
 #include "MachOFileAbstraction.hpp"
 #include "Architectures.hpp"
@@ -63,14 +65,13 @@ class InternalAtom : public ld::Atom
 public:
                                                                                                InternalAtom(class File& f);
        // overrides of ld::Atom
-       virtual ld::File*                                                       file() const            { return &_file; }
-       virtual const char*                                                     name() const            { return "import-atom"; }
-       virtual uint64_t                                                        size() const            { return 0; }
-       virtual uint64_t                                                        objectAddress() const { return 0; }
-       virtual void                                                            copyRawContent(uint8_t buffer[]) const { }
-       virtual void                                                            setScope(Scope)         { }
-       virtual ld::Fixup::iterator                                     fixupsBegin() const     { return &_undefs[0]; }
-       virtual ld::Fixup::iterator                                     fixupsEnd()     const   { return &_undefs[_undefs.size()]; }
+       ld::File*                                                       file() const override           { return &_file; }
+       const char*                                                     name() const override           { return "import-atom"; }
+       uint64_t                                                        size() const override           { return 0; }
+       uint64_t                                                        objectAddress() const override { return 0; }
+       void                                                            copyRawContent(uint8_t buffer[]) const override { }
+       ld::Fixup::iterator                                     fixupsBegin() const override    { return &_undefs[0]; }
+       ld::Fixup::iterator                                     fixupsEnd()     const override  { return &_undefs[_undefs.size()]; }
 
        // for adding references to symbols outside bitcode file
        void                                                                            addReference(const char* nm)
@@ -91,24 +92,25 @@ class File : public ld::relocatable::File
 public:
                                                                                        File(const char* path, time_t mTime, ld::File::Ordinal ordinal, 
                                                                                                         const uint8_t* content, uint32_t contentLength, cpu_type_t arch);
-       virtual                                                                 ~File();
+                                                                                       ~File() override;
 
        // overrides of ld::File
-       virtual bool                                                                            forEachAtom(ld::File::AtomHandler&) const;
-       virtual bool                                                                            justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const 
+       bool                                                                            forEachAtom(ld::File::AtomHandler&) const override;
+       bool                                                                            justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const override
                                                                                                                                                                        { return false; }
-       virtual uint32_t                                                                        cpuSubType() const                      { return _cpuSubType; }
+       uint32_t                                                                        cpuSubType() const override                     { return _cpuSubType; }
        
        // overrides of ld::relocatable::File 
-       virtual DebugInfoKind                                                           debugInfo()     const                   { return _debugInfo; }
-       virtual const char*                                                                     debugInfoPath() const           { return _debugInfoPath; }
-       virtual time_t                                                                          debugInfoModificationTime() const 
+       DebugInfoKind                                                           debugInfo()     const override                  { return _debugInfo; }
+       const char*                                                                     debugInfoPath() const override          { return _debugInfoPath; }
+       time_t                                                                          debugInfoModificationTime() const override
                                                                                                                                                                        { return _debugInfoModTime; }
-       virtual const std::vector<ld::relocatable::File::Stab>* stabs() const                   { return NULL; }
-       virtual bool                                                                            canScatterAtoms() const         { return true; }
-       virtual LinkerOptionsList*                                                      linkerOptions() const           { return NULL; }
-
-
+       const std::vector<ld::relocatable::File::Stab>* stabs() const override                  { return NULL; }
+       bool                                                                            canScatterAtoms() const override                { return true; }
+       LinkerOptionsList*                                                      linkerOptions() const override          { return NULL; }
+       bool                                                                                            isThinLTO() const                       { return _isThinLTO; }
+       void                                                                                            setIsThinLTO(bool ThinLTO)      { _isThinLTO = ThinLTO; }
+       // fixme rdar://24734472 objCConstraint() and objcHasCategoryClassProperties()
        void                                                                                            release();
        lto_module_t                                                                            module()                                        { return _module; }
        class InternalAtom&                                                                     internalAtom()                          { return _internalAtom; }
@@ -122,11 +124,15 @@ public:
     static bool                                         sSupportsLocalContext;
     static bool                                         sHasTriedLocalContext;
     bool                                                mergeIntoGenerator(lto_code_gen_t generator, bool useSetModule);
+#if LTO_API_VERSION >= 18
+       void                                                addToThinGenerator(thinlto_code_gen_t generator);
+#endif
 private:
        friend class Atom;
        friend class InternalAtom;
        friend class Parser;
-       
+
+       bool                                                                    _isThinLTO;
        cpu_type_t                                                              _architecture;
        class InternalAtom                                              _internalAtom;
        class Atom*                                                             _atomArray;
@@ -157,31 +163,32 @@ public:
                                                                                                ld::Atom::Definition d, ld::Atom::Combine c, ld::Atom::Alignment a, bool ah);
 
        // overrides of ld::Atom
-       virtual ld::File*                                       file() const            { return &_file; }
-       virtual const char*                                     translationUnitSource() const
+       const ld::File*                         file() const override           { return (_compiledAtom ? _compiledAtom->file() : &_file ); }
+       const ld::File*                         originalFile() const override   { return &_file; }
+       const char*                                     translationUnitSource() const override
                                                                                                                        { return (_compiledAtom ? _compiledAtom->translationUnitSource() : NULL); }
-       virtual const char*                                     name() const            { return _name; }
-       virtual uint64_t                                        size() const            { return (_compiledAtom ? _compiledAtom->size() : 0); }
-       virtual uint64_t                                        objectAddress() const { return (_compiledAtom ? _compiledAtom->objectAddress() : 0); }
-       virtual void                                            copyRawContent(uint8_t buffer[]) const 
+       const char*                                     name() const override           { return _name; }
+       uint64_t                                        size() const override           { return (_compiledAtom ? _compiledAtom->size() : 0); }
+       uint64_t                                        objectAddress() const override { return (_compiledAtom ? _compiledAtom->objectAddress() : 0); }
+       void                                            copyRawContent(uint8_t buffer[]) const override
                                                                                                                        { if (_compiledAtom) _compiledAtom->copyRawContent(buffer); }
-       virtual const uint8_t*                          rawContentPointer() const 
+       const uint8_t*                          rawContentPointer() const override
                                                                                                                        { return (_compiledAtom ? _compiledAtom->rawContentPointer() : NULL);  }
-       virtual unsigned long                           contentHash(const class ld::IndirectBindingTable& ibt) const 
+       unsigned long                           contentHash(const class ld::IndirectBindingTable& ibt) const override
                                                                                                                        { return (_compiledAtom ? _compiledAtom->contentHash(ibt) : 0);  }
-       virtual bool                                            canCoalesceWith(const ld::Atom& rhs, const class ld::IndirectBindingTable& ibt) const 
+       bool                                            canCoalesceWith(const ld::Atom& rhs, const class ld::IndirectBindingTable& ibt) const override
                                                                                                                        { return (_compiledAtom ? _compiledAtom->canCoalesceWith(rhs,ibt) : false); }
-       virtual ld::Fixup::iterator                             fixupsBegin() const     
+       ld::Fixup::iterator                             fixupsBegin() const override
                                                                                                                        { return (_compiledAtom ? _compiledAtom->fixupsBegin() : (ld::Fixup*)&_file._fixupToInternal); }
-       virtual ld::Fixup::iterator                             fixupsEnd() const       
+       ld::Fixup::iterator                             fixupsEnd() const override
                                                                                                                        { return (_compiledAtom ? _compiledAtom->fixupsEnd() : &((ld::Fixup*)&_file._fixupToInternal)[1]); }
-       virtual ld::Atom::UnwindInfo::iterator  beginUnwind() const 
+       ld::Atom::UnwindInfo::iterator  beginUnwind() const override
                                                                                                                        { return (_compiledAtom ? _compiledAtom->beginUnwind() : NULL); }
-       virtual ld::Atom::UnwindInfo::iterator  endUnwind() const       
+       ld::Atom::UnwindInfo::iterator  endUnwind() const override
                                                                                                                        { return (_compiledAtom ? _compiledAtom->endUnwind() : NULL); }
-       virtual ld::Atom::LineInfo::iterator    beginLineInfo() const 
+       ld::Atom::LineInfo::iterator    beginLineInfo() const override
                                                                                                                        { return (_compiledAtom ? _compiledAtom->beginLineInfo() : NULL); }
-       virtual ld::Atom::LineInfo::iterator    endLineInfo() const 
+       ld::Atom::LineInfo::iterator    endLineInfo() const override
                                                                                                                        { return (_compiledAtom ? _compiledAtom->endLineInfo() : NULL); }
                                                                                                                        
        const ld::Atom*                                         compiledAtom()          { return _compiledAtom; }
@@ -216,11 +223,13 @@ public:
                                                                                                std::vector<const ld::Atom*>&           newAtoms, 
                                                                                                std::vector<const char*>&                       additionalUndefines);
 
-       static const char*                      ltoVersion()    { return ::lto_get_version(); }
+       static const char*                              ltoVersion()    { return ::lto_get_version(); }
 
 private:
+
        static const char*                              tripletPrefixForArch(cpu_type_t arch);
-       static ld::relocatable::File*   parseMachOFile(const uint8_t* p, size_t len, const OptimizeOptions& options);
+       static ld::relocatable::File*   parseMachOFile(const uint8_t* p, size_t len, const std::string &path, const OptimizeOptions& options,
+                                                                                                  ld::File::Ordinal ordinal);
 #if LTO_API_VERSION >= 7
        static void ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t, const char*, void*);
 #endif
@@ -231,23 +240,70 @@ private:
        class AtomSyncer : public ld::File::AtomHandler {
        public:
                                                        AtomSyncer(std::vector<const char*>& a, std::vector<const ld::Atom*>&na,
-                                                                               CStringToAtom la, CStringToAtom dla, const OptimizeOptions& options) :
-                                                                               _options(options), _additionalUndefines(a), _newAtoms(na), _llvmAtoms(la), _deadllvmAtoms(dla) { }
-               virtual void            doAtom(const class ld::Atom&);
-               virtual void            doFile(const class ld::File&) { }
+                                                                               const CStringToAtom &la, const CStringToAtom &dla, const OptimizeOptions& options) :
+                                                                               _options(options), _additionalUndefines(a), _newAtoms(na), _llvmAtoms(la), _deadllvmAtoms(dla), _lastProxiedAtom(NULL), _lastProxiedFile(NULL) {}
+               void            doAtom(const class ld::Atom&) override;
+               void            doFile(const class ld::File&) override { }
                
-
                const OptimizeOptions&                  _options;
                std::vector<const char*>&               _additionalUndefines;
                std::vector<const ld::Atom*>&   _newAtoms;
-               CStringToAtom                                   _llvmAtoms;
-               CStringToAtom                                   _deadllvmAtoms;
+               const CStringToAtom                                     &_llvmAtoms;
+               const CStringToAtom                                     &_deadllvmAtoms;
+               const ld::Atom*                                 _lastProxiedAtom;
+               const ld::File*                                 _lastProxiedFile;
        };
 
+       static void                                             setPreservedSymbols(const std::vector<const ld::Atom*>& allAtoms,
+                                                                                                               ld::Internal&                                           state,
+                                                                                                               const OptimizeOptions&                          options,
+                                                                                                               CStringToAtom &deadllvmAtoms,
+                                                                                                               CStringToAtom &llvmAtoms,
+                                                                                                               lto_code_gen_t generator);
+
+       static std::tuple<uint8_t *, size_t> codegen(const OptimizeOptions& options,
+                                                                                                ld::Internal&                  state,
+                                                                                                lto_code_gen_t                 generator,
+                                                                                            std::string&           object_path);
+
+
+       static void loadMachO(ld::relocatable::File*                            machoFile,
+                                                 const OptimizeOptions&                options,
+                                                 ld::File::AtomHandler&                handler,
+                                                 std::vector<const ld::Atom*>& newAtoms,
+                                                 std::vector<const char*>&             additionalUndefines,
+                                                 CStringToAtom                                 &llvmAtoms,
+                                                 CStringToAtom                                 &deadllvmAtoms);
+
+       static bool optimizeLTO(const std::vector<File*> files,
+                                                       const std::vector<const ld::Atom*>&     allAtoms,
+                                                       ld::Internal&                                           state,
+                                                       const OptimizeOptions&                          options,
+                                                       ld::File::AtomHandler&                          handler,
+                                                       std::vector<const ld::Atom*>&           newAtoms,
+                                                       std::vector<const char*>&                       additionalUndefines);
+
+       static bool optimizeThinLTO(const std::vector<File*>&              Files,
+                                                           const std::vector<const ld::Atom*>& allAtoms,
+                                                               ld::Internal&                                           state,
+                                                               const OptimizeOptions&                          options,
+                                                               ld::File::AtomHandler&                          handler,
+                                                               std::vector<const ld::Atom*>&           newAtoms,
+                                                               std::vector<const char*>&                       additionalUndefines);
+
+       static thinlto_code_gen_t init_thinlto_codegen(const std::vector<File*>&           files,
+                                                                                                  const std::vector<const ld::Atom*>& allAtoms,
+                                                                                                  ld::Internal&                                       state,
+                                                                                                  const OptimizeOptions&                          options,
+                                                                                                  CStringToAtom&                      deadllvmAtoms,
+                                                                                                  CStringToAtom&                      llvmAtoms);
+
        static std::vector<File*>               _s_files;
+       static bool                                             _s_llvmOptionsProcessed;
 };
 
 std::vector<File*> Parser::_s_files;
+bool Parser::_s_llvmOptionsProcessed = false;
 
 
 bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch)
@@ -298,7 +354,8 @@ File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char*
 }
 
 
-ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, const OptimizeOptions& options) 
+ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, const std::string &path, const OptimizeOptions& options,
+                                                                                         ld::File::Ordinal ordinal)
 {
        mach_o::relocatable::ParserOptions objOpts;
        objOpts.architecture            = options.arch;
@@ -313,23 +370,23 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons
        objOpts.simulator                       = options.simulator;
        objOpts.ignoreMismatchPlatform = options.ignoreMismatchPlatform;
        objOpts.platform                        = options.platform;
+       objOpts.minOSVersion            = options.minOSVersion;
        objOpts.subType                         = 0;
        objOpts.srcKind                         = ld::relocatable::File::kSourceLTO;
        objOpts.treateBitcodeAsData = false;
        objOpts.usingBitcode            = options.bitcodeBundle;
        objOpts.maxDefaultCommonAlignment = options.maxDefaultCommonAlignment;
 
-       // mach-o parsing is done in-memory, but need path for debug notes
-       const char* path = "/tmp/lto.o";
+       const char *object_path = path.c_str();
+       if (path.empty())
+               object_path = "/tmp/lto.o";
+
        time_t modTime = 0;
-       if ( options.tmpObjectFilePath != NULL ) {
-               path = options.tmpObjectFilePath;
-               struct stat statBuffer;
-               if ( stat(options.tmpObjectFilePath, &statBuffer) == 0 )
-                       modTime = statBuffer.st_mtime;
-       }
-       
-       ld::relocatable::File* result = mach_o::relocatable::parse(p, len, path, modTime, ld::File::Ordinal::LTOOrdinal(), objOpts);
+       struct stat statBuffer;
+       if ( stat(object_path, &statBuffer) == 0 )
+               modTime = statBuffer.st_mtime;
+
+       ld::relocatable::File* result = mach_o::relocatable::parse(p, len, strdup(object_path), modTime, ordinal, objOpts);
        if ( result != NULL )
                return result;
        throw "LLVM LTO, file is not of required architecture";
@@ -338,7 +395,7 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons
 
 
 File::File(const char* pth, time_t mTime, ld::File::Ordinal ordinal, const uint8_t* content, uint32_t contentLength, cpu_type_t arch) 
-       : ld::relocatable::File(pth,mTime,ordinal), _architecture(arch), _internalAtom(*this), 
+       : ld::relocatable::File(pth,mTime,ordinal), _isThinLTO(false), _architecture(arch), _internalAtom(*this),
        _atomArray(NULL), _atomArrayCount(0), _module(NULL), _path(pth),
        _content(content), _contentLength(contentLength), _debugInfoPath(pth),
        _section("__TEXT_", "__tmp_lto", ld::Section::typeTempLTO),
@@ -367,7 +424,11 @@ File::File(const char* pth, time_t mTime, ld::File::Ordinal ordinal, const uint8
                throwf("could not parse object file %s: '%s', using libLTO version '%s'", pth, ::lto_get_error_message(), ::lto_get_version());
 
        if ( log ) fprintf(stderr, "bitcode file: %s\n", pth);
-       
+
+#if LTO_API_VERSION >= 18
+       _isThinLTO = ::lto_module_is_thinlto(_module);
+#endif
+
        // create atom for each global symbol in module
        uint32_t count = ::lto_module_get_num_symbols(_module);
        _atomArray = (Atom*)malloc(sizeof(Atom)*count);
@@ -481,6 +542,13 @@ bool File::mergeIntoGenerator(lto_code_gen_t generator, bool useSetModule) {
     return false;
 }
 
+#if LTO_API_VERSION >= 18
+void File::addToThinGenerator(thinlto_code_gen_t generator) {
+       assert(!_module && "Expected module to be disposed");
+       ::thinlto_codegen_add_module(generator, _path, (const char *)_content, _contentLength);
+}
+#endif
+
 void File::release()
 {
        if ( _module != NULL )
@@ -556,79 +624,27 @@ void Parser::ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t severity, co
 }
 #endif
 
-bool Parser::optimize(  const std::vector<const ld::Atom*>&    allAtoms,
-                                               ld::Internal&                                           state,
-                                               const OptimizeOptions&                          options,
-                                               ld::File::AtomHandler&                          handler,
-                                               std::vector<const ld::Atom*>&           newAtoms, 
-                                               std::vector<const char*>&                       additionalUndefines)
-{
-       const bool logMustPreserve = false;
-       const bool logExtraOptions = false;
-       const bool logBitcodeFiles = false;
-       const bool logAtomsBeforeSync = false;
-
-       // exit quickly if nothing to do
-       if ( _s_files.size() == 0 ) 
-               return false;
-       
-       // print out LTO version string if -v was used
-       if ( options.verbose )
-               fprintf(stderr, "%s\n", ::lto_get_version());
-       
-       // create optimizer and add each Reader
-       lto_code_gen_t generator = NULL;
-#if LTO_API_VERSION >= 11
-       if ( File::sSupportsLocalContext )
-               generator = ::lto_codegen_create_in_local_context();
-       else
-#endif
-               generator = ::lto_codegen_create();
-#if LTO_API_VERSION >= 7
-       lto_codegen_set_diagnostic_handler(generator, ltoDiagnosticHandler, NULL);
-#endif
-
-       // <rdar://problem/12379604> The order that files are merged must match command line order
-       std::sort(_s_files.begin(), _s_files.end(), CommandLineOrderFileSorter());
-       ld::File::Ordinal lastOrdinal;
-
-       // When flto_codegen_only is on and we have a single .bc file, use lto_codegen_set_module instead of
-       // lto_codegen_add_module, to make sure the the destination module will be the same as the input .bc file.
-       bool useSetModule = false;
-#if LTO_API_VERSION >= 13
-       useSetModule = (_s_files.size() == 1) && options.ltoCodegenOnly && (::lto_api_version() >= 13);
-#endif
-       for (std::vector<File*>::iterator it=_s_files.begin(); it != _s_files.end(); ++it) {
-               File* f = *it;
-               assert(f->ordinal() > lastOrdinal);
-               if ( logBitcodeFiles && !useSetModule) fprintf(stderr, "lto_codegen_add_module(%s)\n", f->path());
-               if ( logBitcodeFiles && useSetModule) fprintf(stderr, "lto_codegen_set_module(%s)\n", f->path());
-               if ( f->mergeIntoGenerator(generator, useSetModule) )
-                       throwf("lto: could not merge in %s because '%s', using libLTO version '%s'", f->path(), ::lto_get_error_message(), ::lto_get_version());
-               lastOrdinal = f->ordinal();
-       }
-
-       // add any -mllvm command line options
-       for (std::vector<const char*>::const_iterator it=options.llvmOptions->begin(); it != options.llvmOptions->end(); ++it) {
-               if ( logExtraOptions ) fprintf(stderr, "passing option to llvm: %s\n", *it);
-               ::lto_codegen_debug_options(generator, *it);
-       }
 
-       // <rdar://problem/13687397> Need a way for LTO to get cpu variants (until that info is in bitcode)
-       if ( options.mcpu != NULL )
-               ::lto_codegen_set_cpu(generator, options.mcpu);
+/// Instruct libLTO about the list of symbols to preserve, compute deadllvmAtoms and llvmAtoms
+void Parser::setPreservedSymbols(      const std::vector<const ld::Atom*>&     allAtoms,
+                                                                       ld::Internal&                                           state,
+                                                                       const OptimizeOptions&                          options,
+                                                                       CStringToAtom &deadllvmAtoms,
+                                                                       CStringToAtom &llvmAtoms,
+                                                                       lto_code_gen_t generator) {
+       const bool logMustPreserve = false;
 
-       // The atom graph uses directed edges (references). Collect all references where 
-       // originating atom is not part of any LTO Reader. This allows optimizer to optimize an 
-       // external (i.e. not originated from same .o file) reference if all originating atoms are also 
+       // The atom graph uses directed edges (references). Collect all references where
+       // originating atom is not part of any LTO Reader. This allows optimizer to optimize an
+       // external (i.e. not originated from same .o file) reference if all originating atoms are also
        // defined in llvm bitcode file.
        CStringSet nonLLVMRefs;
-       CStringToAtom llvmAtoms;
-    bool hasNonllvmAtoms = false;
+       bool hasNonllvmAtoms = false;
        for (std::vector<const ld::Atom*>::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) {
                const ld::Atom* atom = *it;
-               // only look at references that come from an atom that is not an llvm atom
-               if ( atom->contentType() != ld::Atom::typeLTOtemporary ) {
+               // only look at references that come from an atom that is not an LTO atom
+               if (atom->contentType() != ld::Atom::typeLTOtemporary ||
+                       ((lto::File *)atom->file())->isThinLTO()) {
                        if ( (atom->section().type() != ld::Section::typeMachHeader) && (atom->definition() != ld::Atom::definitionProxy) ) {
                                hasNonllvmAtoms = true;
                        }
@@ -637,7 +653,7 @@ bool Parser::optimize(  const std::vector<const ld::Atom*>& allAtoms,
                                switch ( fit->binding ) {
                                        case ld::Fixup::bindingDirectlyBound:
                                                // that reference an llvm atom
-                                               if ( fit->u.target->contentType() == ld::Atom::typeLTOtemporary ) 
+                                               if ( fit->u.target->contentType() == ld::Atom::typeLTOtemporary )
                                                        nonLLVMRefs.insert(fit->u.target->name());
                                                break;
                                        case ld::Fixup::bindingsIndirectlyBound:
@@ -655,15 +671,14 @@ bool Parser::optimize(  const std::vector<const ld::Atom*>&       allAtoms,
        }
        // if entry point is in a llvm bitcode file, it must be preserved by LTO
        if ( state.entryPoint!= NULL ) {
-               if ( state.entryPoint->contentType() == ld::Atom::typeLTOtemporary ) 
+               if ( state.entryPoint->contentType() == ld::Atom::typeLTOtemporary )
                        nonLLVMRefs.insert(state.entryPoint->name());
        }
-       
+
        // deadAtoms are the atoms that the linker coalesced.  For instance weak or tentative definitions
        // overriden by another atom.  If any of these deadAtoms are llvm atoms and they were replaced
-       // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead 
+       // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead
        // atom so that the linker can replace it with the mach-o one later.
-       CStringToAtom deadllvmAtoms;
        for (std::vector<const ld::Atom*>::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) {
                const ld::Atom* atom = *it;
                if ( atom->coalescedAway() && (atom->contentType() == ld::Atom::typeLTOtemporary) ) {
@@ -680,7 +695,7 @@ bool Parser::optimize(  const std::vector<const ld::Atom*>& allAtoms,
                        if ( llvmAtom->coalescedAway()  ) {
                                const char* name = llvmAtom->name();
                                if ( deadllvmAtoms.find(name) == deadllvmAtoms.end() ) {
-                                       if ( logMustPreserve ) 
+                                       if ( logMustPreserve )
                                                fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because linker coalesce away and replace with a mach-o atom\n", name);
                                        ::lto_codegen_add_must_preserve_symbol(generator, name);
                                        deadllvmAtoms[name] = (Atom*)llvmAtom;
@@ -692,7 +707,7 @@ bool Parser::optimize(  const std::vector<const ld::Atom*>& allAtoms,
                        }
                }
        }
-       
+
        // tell code generator about symbols that must be preserved
        for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) {
                const char* name = it->first;
@@ -701,7 +716,7 @@ bool Parser::optimize(  const std::vector<const ld::Atom*>& allAtoms,
                // 1 - atom scope is global (and not linkage unit).
                // 2 - included in nonLLVMRefs set.
                // If a symbol is not listed in exportList then LTO is free to optimize it away.
-               if ( (atom->scope() == ld::Atom::scopeGlobal) && options.preserveAllGlobals ) { 
+               if ( (atom->scope() == ld::Atom::scopeGlobal) && options.preserveAllGlobals ) {
                        if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because global symbol\n", name);
                        ::lto_codegen_add_must_preserve_symbol(generator, name);
                }
@@ -715,15 +730,15 @@ bool Parser::optimize(  const std::vector<const ld::Atom*>&       allAtoms,
                        ::lto_codegen_add_must_preserve_symbol(generator, name);
                }
        }
-       
+
        // <rdar://problem/16165191> tell code generator to preserve initial undefines
        for( std::vector<const char*>::const_iterator it=options.initialUndefines->begin(); it != options.initialUndefines->end(); ++it) {
                if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because it is an initial undefine\n", *it);
                ::lto_codegen_add_must_preserve_symbol(generator, *it);
        }
 
-    // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o)
-    if ( options.relocatable && !hasNonllvmAtoms ) {
+       // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o)
+       if ( options.relocatable && !hasNonllvmAtoms ) {
 #if LTO_API_VERSION >= 15
                ::lto_codegen_set_should_embed_uselists(generator, false);
 #endif
@@ -732,44 +747,56 @@ bool Parser::optimize(  const std::vector<const ld::Atom*>&       allAtoms,
                        exit(0);
                }
                warning("could not produce merged bitcode file");
-    }
-    
-       // set code-gen model
-       lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
+       }
+
+}
+
+// Retrieve the codegen model from the options
+static lto_codegen_model getCodeModel(const OptimizeOptions& options) {
        if ( options.mainExecutable ) {
                if ( options.staticExecutable ) {
                        // x86_64 "static" or any "-static -pie" is really dynamic code model
                        if ( (options.arch == CPU_TYPE_X86_64) || options.pie )
-                               model = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
+                               return LTO_CODEGEN_PIC_MODEL_DYNAMIC;
                        else
-                               model = LTO_CODEGEN_PIC_MODEL_STATIC;
+                               return LTO_CODEGEN_PIC_MODEL_STATIC;
                }
                else {
                        if ( options.pie )
-                               model = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
+                               return LTO_CODEGEN_PIC_MODEL_DYNAMIC;
                        else
-                               model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC;
+                               return LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC;
                }
        }
        else {
                if ( options.allowTextRelocs )
-                       model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC;
+                       return LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC;
                else
-                       model = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
+                       return LTO_CODEGEN_PIC_MODEL_DYNAMIC;
        }
-       if ( ::lto_codegen_set_pic_model(generator, model) )
+
+}
+
+std::tuple<uint8_t *, size_t> Parser::codegen(const OptimizeOptions& options,
+                                                                                         ld::Internal&                  state,
+                                                                                         lto_code_gen_t                 generator,
+                                                                                         std::string&           object_path) {
+       uint8_t *machOFile;
+       size_t machOFileLen;
+
+       if ( ::lto_codegen_set_pic_model(generator, getCodeModel(options)) )
                throwf("could not create set codegen model: %s", lto_get_error_message());
 
-    // if requested, save off merged bitcode file
-    if ( options.saveTemps ) {
-        char tempBitcodePath[MAXPATHLEN];
-        strcpy(tempBitcodePath, options.outputFilePath);
-        strcat(tempBitcodePath, ".lto.bc");
+       // if requested, save off merged bitcode file
+       if ( options.saveTemps ) {
+               char tempBitcodePath[MAXPATHLEN];
+               strcpy(tempBitcodePath, options.outputFilePath);
+               strcat(tempBitcodePath, ".lto.bc");
 #if LTO_API_VERSION >= 15
-        ::lto_codegen_set_should_embed_uselists(generator, true);
+               ::lto_codegen_set_should_embed_uselists(generator, true);
 #endif
-        ::lto_codegen_write_merged_modules(generator, tempBitcodePath);
-    }
+               ::lto_codegen_write_merged_modules(generator, tempBitcodePath);
+       }
 
 #if LTO_API_VERSION >= 3
        // find assembler next to linker
@@ -794,13 +821,11 @@ bool Parser::optimize(  const std::vector<const ld::Atom*>&       allAtoms,
                useSplitAPI = true;
 #endif
 
-       size_t machOFileLen = 0;
-       const uint8_t* machOFile = NULL;
        if ( useSplitAPI) {
 #if LTO_API_VERSION >= 12
 #if LTO_API_VERSION >= 14
-       if ( ::lto_api_version() >= 14 && options.ltoCodegenOnly)
-          lto_codegen_set_should_internalize(generator, false);
+               if ( ::lto_api_version() >= 14 && options.ltoCodegenOnly)
+                       lto_codegen_set_should_internalize(generator, false);
 #endif
                // run optimizer
                if ( !options.ltoCodegenOnly && ::lto_codegen_optimize(generator) )
@@ -816,7 +841,7 @@ bool Parser::optimize(  const std::vector<const ld::Atom*>& allAtoms,
 #endif
                        ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath);
                        if ( options.bitcodeBundle )
-                               state.ltoBitcodePath = tempOptBitcodePath;
+                               state.ltoBitcodePath.push_back(tempOptBitcodePath);
                }
 
                // run code generator
@@ -841,13 +866,13 @@ bool Parser::optimize(  const std::vector<const ld::Atom*>&       allAtoms,
                        ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath);
                }
        }
-       
-    // if requested, save off temp mach-o file
-    if ( options.saveTemps ) {
-        char tempMachoPath[MAXPATHLEN];
-        strcpy(tempMachoPath, options.outputFilePath);
-        strcat(tempMachoPath, ".lto.o");
-        int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+
+       // if requested, save off temp mach-o file
+       if ( options.saveTemps ) {
+               char tempMachoPath[MAXPATHLEN];
+               strcpy(tempMachoPath, options.outputFilePath);
+               strcat(tempMachoPath, ".lto.o");
+               int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666);
                if ( fd != -1) {
                        ::write(fd, machOFile, machOFileLen);
                        ::close(fd);
@@ -855,20 +880,30 @@ bool Parser::optimize(  const std::vector<const ld::Atom*>&       allAtoms,
        }
 
        // if needed, save temp mach-o file to specific location
-       if ( options.tmpObjectFilePath != NULL ) {
-               int fd = ::open(options.tmpObjectFilePath, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+       if ( !object_path.empty() ) {
+               int fd = ::open(object_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
                if ( fd != -1) {
                        ::write(fd, machOFile, machOFileLen);
                        ::close(fd);
                }
                else {
-                       warning("could not write LTO temp file '%s', errno=%d", options.tmpObjectFilePath, errno);
+                       warning("could not write LTO temp file '%s', errno=%d", object_path.c_str(), errno);
                }
        }
-       
-       // parse generated mach-o file into a MachOReader
-       ld::relocatable::File* machoFile = parseMachOFile(machOFile, machOFileLen, options);
-       
+       return std::make_tuple(machOFile, machOFileLen);
+}
+
+/// Load the MachO located in buffer \p machOFile with size \p machOFileLen.
+/// The loaded atoms are sync'ed using all the supplied lists.
+void Parser::loadMachO(ld::relocatable::File*                          machoFile,
+                                          const OptimizeOptions&                               options,
+                                          ld::File::AtomHandler&                               handler,
+                                          std::vector<const ld::Atom*>&                newAtoms,
+                                          std::vector<const char*>&                    additionalUndefines,
+                                          CStringToAtom                                                &llvmAtoms,
+                                          CStringToAtom                                                &deadllvmAtoms) {
+       const bool logAtomsBeforeSync = false;
+
        // sync generated mach-o atoms with existing atoms ld knows about
        if ( logAtomsBeforeSync ) {
                fprintf(stderr, "llvmAtoms:\n");
@@ -886,11 +921,96 @@ bool Parser::optimize(  const std::vector<const ld::Atom*>&       allAtoms,
        }
        AtomSyncer syncer(additionalUndefines, newAtoms, llvmAtoms, deadllvmAtoms, options);
        machoFile->forEachAtom(syncer);
-                       
-       // Remove InternalAtoms from ld
-       for (std::vector<File*>::iterator it=_s_files.begin(); it != _s_files.end(); ++it) {
-               (*it)->internalAtom().setCoalescedAway();
+
+       // notify about file level attributes
+       handler.doFile(*machoFile);
+}
+
+// Full LTO processing
+bool Parser::optimizeLTO(const std::vector<File*>                              files,
+                                                const std::vector<const ld::Atom*>&    allAtoms,
+                                                ld::Internal&                                                  state,
+                                                const OptimizeOptions&                                 options,
+                                                ld::File::AtomHandler&                                 handler,
+                                                std::vector<const ld::Atom*>&                  newAtoms,
+                                                std::vector<const char*>&                              additionalUndefines) {
+       const bool logExtraOptions = false;
+       const bool logBitcodeFiles = false;
+
+       if (files.empty())
+               return true;
+
+       // create optimizer and add each Reader
+       lto_code_gen_t generator = NULL;
+#if LTO_API_VERSION >= 11
+       if ( File::sSupportsLocalContext )
+               generator = ::lto_codegen_create_in_local_context();
+       else
+#endif
+               generator = ::lto_codegen_create();
+#if LTO_API_VERSION >= 7
+       lto_codegen_set_diagnostic_handler(generator, ltoDiagnosticHandler, NULL);
+#endif
+
+       ld::File::Ordinal lastOrdinal;
+
+       // When flto_codegen_only is on and we have a single .bc file, use lto_codegen_set_module instead of
+       // lto_codegen_add_module, to make sure the the destination module will be the same as the input .bc file.
+       bool useSetModule = false;
+#if LTO_API_VERSION >= 13
+       useSetModule = (files.size() == 1) && options.ltoCodegenOnly && (::lto_api_version() >= 13);
+#endif
+       for (auto *f : files) {
+               assert(f->ordinal() > lastOrdinal);
+               if ( logBitcodeFiles && !useSetModule ) fprintf(stderr, "lto_codegen_add_module(%s)\n", f->path());
+               if ( logBitcodeFiles && useSetModule ) fprintf(stderr, "lto_codegen_set_module(%s)\n", f->path());
+               if ( f->mergeIntoGenerator(generator, useSetModule) )
+                       throwf("lto: could not merge in %s because '%s', using libLTO version '%s'", f->path(), ::lto_get_error_message(), ::lto_get_version());
+               lastOrdinal = f->ordinal();
+       }
+
+       // add any -mllvm command line options
+       if ( !_s_llvmOptionsProcessed ) {
+               for (const char* opt : *options.llvmOptions) {
+                       if ( logExtraOptions ) fprintf(stderr, "passing option to llvm: %s\n", opt);
+                       ::lto_codegen_debug_options(generator, opt);
+               }
+               _s_llvmOptionsProcessed = true;
        }
+
+       // <rdar://problem/13687397> Need a way for LTO to get cpu variants (until that info is in bitcode)
+       if ( options.mcpu != NULL )
+               ::lto_codegen_set_cpu(generator, options.mcpu);
+
+       // Compute the preserved symbols
+       CStringToAtom deadllvmAtoms, llvmAtoms;
+       setPreservedSymbols(allAtoms, state, options, deadllvmAtoms, llvmAtoms, generator);
+
+       size_t machOFileLen = 0;
+       const uint8_t* machOFile = NULL;
+
+       // mach-o parsing is done in-memory, but need path for debug notes
+       std::string object_path;
+       if ( options.tmpObjectFilePath != NULL ) {
+               object_path = options.tmpObjectFilePath;
+               // If the path exists and is a directory (for instance if some files
+               // were processed with ThinLTO before), we create the LTO file inside
+               // the directory.
+               struct stat statBuffer;
+               if( stat(object_path.c_str(), &statBuffer) == 0 && S_ISDIR(statBuffer.st_mode) ) {
+                       object_path += "/lto.o";
+               }
+       }
+
+       // Codegen Now
+       std::tie(machOFile, machOFileLen) = codegen(options, state, generator, object_path);
+
+       // parse generated mach-o file into a MachOReader
+       ld::relocatable::File* machoFile = parseMachOFile(machOFile, machOFileLen, object_path, options, ld::File::Ordinal::LTOOrdinal());
+
+       // Load the generated MachO file
+       loadMachO(machoFile, options, handler, newAtoms, additionalUndefines, llvmAtoms, deadllvmAtoms);
+
        // Remove Atoms from ld if code generator optimized them away
        for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) {
                // check if setRealAtom() called on this Atom
@@ -899,49 +1019,407 @@ bool Parser::optimize(  const std::vector<const ld::Atom*>&     allAtoms,
                        li->second->setCoalescedAway();
                }
        }
-       
-       // notify about file level attributes
-       handler.doFile(*machoFile);
-       
+
        // if final mach-o file has debug info, update original bitcode files to match
-       for (std::vector<File*>::iterator it=_s_files.begin(); it != _s_files.end(); ++it) {
-               (*it)->setDebugInfo(machoFile->debugInfo(), machoFile->path(),
-                                                       machoFile->modificationTime(), machoFile->cpuSubType());
+       for (auto *f : files) {
+               f->setDebugInfo(machoFile->debugInfo(), machoFile->path(), machoFile->modificationTime(), machoFile->cpuSubType());
        }
-       
+
        return true;
 }
 
+// Create the ThinLTO codegenerator
+thinlto_code_gen_t Parser::init_thinlto_codegen(const std::vector<File*>&           files,
+                                                                                   const std::vector<const ld::Atom*>& allAtoms,
+                                                                                               ld::Internal&                                  state,
+                                                                                               const OptimizeOptions&                     options,
+                                                                                           CStringToAtom&                      deadllvmAtoms,
+                                                                                           CStringToAtom&                      llvmAtoms) {
+       const bool logMustPreserve = false;
+
+    thinlto_code_gen_t thingenerator = ::thinlto_create_codegen();
+
+       // Caching control
+       if (options.ltoCachePath && !options.bitcodeBundle) {
+               struct stat statBuffer;
+               if( stat(options.ltoCachePath, &statBuffer) != 0 || !S_ISDIR(statBuffer.st_mode) ) {
+                       if ( mkdir(options.ltoCachePath, 0700) !=0 ) {
+                               warning("unable to create ThinLTO cache directory: %s", options.ltoCachePath);
+                       }
+               }
+               thinlto_codegen_set_cache_dir(thingenerator, options.ltoCachePath);
+               thinlto_codegen_set_cache_pruning_interval(thingenerator, options.ltoPruneInterval);
+               thinlto_codegen_set_cache_entry_expiration(thingenerator, options.ltoPruneAfter);
+               thinlto_codegen_set_final_cache_size_relative_to_available_space(thingenerator, options.ltoMaxCacheSize);
+       }
+
+       // if requested, ask the code generator to save off intermediate bitcode files
+       if ( options.saveTemps ) {
+               std::string tempPath = options.outputFilePath;
+               tempPath += ".thinlto.bcs/";
+               struct stat statBuffer;
+               if( stat(tempPath.c_str(), &statBuffer) != 0 || !S_ISDIR(statBuffer.st_mode) ) {
+                       if ( mkdir(tempPath.c_str(), 0700) !=0 ) {
+                               warning("unable to create ThinLTO output directory for temporary bitcode files: %s", tempPath.c_str());
+                       }
+               }
+               thinlto_codegen_set_savetemps_dir(thingenerator, tempPath.c_str());
+       }
+
+       // Set some codegen options
+       if ( thinlto_codegen_set_pic_model(thingenerator, getCodeModel(options)) )
+               throwf("could not create set codegen model: %s", lto_get_error_message());
+
+       // Expose reachability informations for internalization in LTO
+
+       // The atom graph uses directed edges (references). Collect all references where
+       // originating atom is not part of any LTO Reader. This allows optimizer to optimize an
+       // external (i.e. not originated from same .o file) reference if all originating atoms are also
+       // defined in llvm bitcode file.
+       CStringSet nonLLVMRefs;
+       CStringSet LLVMRefs;
+       for (std::vector<const ld::Atom*>::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) {
+               const ld::Atom* atom = *it;
+               const ld::Atom* target;
+               for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) {
+                       switch ( fit->binding ) {
+                               case ld::Fixup::bindingDirectlyBound:
+                                       // that reference a ThinLTO llvm atom
+                                       target = fit->u.target;
+                                       if ( target->contentType() == ld::Atom::typeLTOtemporary &&
+                                               ((lto::File *)target->file())->isThinLTO() &&
+                                               atom->file() != target->file()
+                                               ) {
+                                               if (atom->contentType() != ld::Atom::typeLTOtemporary ||
+                                                       !((lto::File *)atom->file())->isThinLTO())
+                                                       nonLLVMRefs.insert(target->name());
+                                               else
+                                                       LLVMRefs.insert(target->name());
+                                               if ( logMustPreserve )
+                                                       fprintf(stderr, "Found a reference from %s -> %s\n", atom->name(), target->name());
+                                       }
+                                       break;
+                               case ld::Fixup::bindingsIndirectlyBound:
+                                       target = state.indirectBindingTable[fit->u.bindingIndex];
+                                       if ( (target != NULL) && (target->contentType() == ld::Atom::typeLTOtemporary)  &&
+                                               ((lto::File *)target->file())->isThinLTO() &&
+                                               atom->file() != target->file()
+                                               ) {
+                                               if (atom->contentType() != ld::Atom::typeLTOtemporary ||
+                                                       !((lto::File *)atom->file())->isThinLTO())
+                                                       nonLLVMRefs.insert(target->name());
+                                               else
+                                                       LLVMRefs.insert(target->name());
+                                               if ( logMustPreserve )
+                                                       fprintf(stderr, "Found a reference from %s -> %s\n", atom->name(), target->name());
+                                       }
+                               default:
+                                       break;
+                       }
+               }
+               if (atom->contentType() == ld::Atom::typeLTOtemporary &&
+                       ((lto::File *)atom->file())->isThinLTO()) {
+                       llvmAtoms[atom->name()] = (Atom*)atom;
+               }
+       }
+       // if entry point is in a llvm bitcode file, it must be preserved by LTO
+       if ( state.entryPoint != NULL ) {
+               if ( state.entryPoint->contentType() == ld::Atom::typeLTOtemporary )
+                       nonLLVMRefs.insert(state.entryPoint->name());
+       }
+       for (auto file : files) {
+               for(uint32_t i=0; i < file->_atomArrayCount; ++i) {
+                       Atom* llvmAtom = &file->_atomArray[i];
+                       if ( llvmAtom->coalescedAway()  ) {
+                               const char* name = llvmAtom->name();
+                               if ( deadllvmAtoms.find(name) == deadllvmAtoms.end() ) {
+                                       if ( logMustPreserve )
+                                               fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because linker coalesce away and replace with a mach-o atom\n", name);
+                                       ::thinlto_codegen_add_must_preserve_symbol(thingenerator, name, strlen(name));
+                                       deadllvmAtoms[name] = (Atom*)llvmAtom;
+                               }
+                       }
+                       else if ( options.linkerDeadStripping && !llvmAtom->live() ) {
+                               const char* name = llvmAtom->name();
+                               deadllvmAtoms[name] = (Atom*)llvmAtom;
+                       }
+               }
+       }
+
+       // tell code generator about symbols that must be preserved
+       for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) {
+               const char* name = it->first;
+               Atom* atom = it->second;
+               // Include llvm Symbol in export list if it meets one of following two conditions
+               // 1 - atom scope is global (and not linkage unit).
+               // 2 - included in nonLLVMRefs set.
+               // If a symbol is not listed in exportList then LTO is free to optimize it away.
+               if ( (atom->scope() == ld::Atom::scopeGlobal) && options.preserveAllGlobals ) {
+                       if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because global symbol\n", name);
+                       ::thinlto_codegen_add_must_preserve_symbol(thingenerator, name, strlen(name));
+               }
+               else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() ) {
+                       if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because referenced from outside of ThinLTO\n", name);
+                       ::thinlto_codegen_add_must_preserve_symbol(thingenerator, name, strlen(name));
+               }
+               else if ( LLVMRefs.find(name) != LLVMRefs.end() ) {
+                       if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because referenced from another file\n", name);
+                       ::thinlto_codegen_add_cross_referenced_symbol(thingenerator, name, strlen(name));
+               } else {
+                       if ( logMustPreserve ) fprintf(stderr, "NOT preserving(%s)\n", name);
+               }
+// FIXME: to be implemented
+//             else if ( options.relocatable && hasNonllvmAtoms ) {
+//                     // <rdar://problem/14334895> ld -r mode but merging in some mach-o files, so need to keep libLTO from optimizing away anything
+//                     if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because -r mode disable LTO dead stripping\n", name);
+//                     ::thinlto_codegen_add_must_preserve_symbol(thingenerator, name, strlen(name));
+//             }
+       }
+
+       return thingenerator;
+}
+
+// Full LTO processing
+bool Parser::optimizeThinLTO(const std::vector<File*>&              files,
+                                                        const std::vector<const ld::Atom*>&    allAtoms,
+                                                        ld::Internal&                                                  state,
+                                                        const OptimizeOptions&                                 options,
+                                                        ld::File::AtomHandler&                                 handler,
+                                                        std::vector<const ld::Atom*>&                  newAtoms,
+                                                        std::vector<const char*>&                              additionalUndefines) {
+       const bool logBitcodeFiles = false;
+
+       if (files.empty())
+               return true;
+
+#if LTO_API_VERSION >= 18
+
+       if (::lto_api_version() < 18)
+               throwf("lto: could not use -thinlto because libLTO is too old (version '%d', >=18 is required)", ::lto_api_version());
+
+       // Handle -mllvm options
+       if ( !_s_llvmOptionsProcessed ) {
+               thinlto_debug_options(options.llvmOptions->data(), options.llvmOptions->size());
+               _s_llvmOptionsProcessed = true;
+       }
+
+       // Create the ThinLTO codegenerator
+       CStringToAtom                     deadllvmAtoms;
+       CStringToAtom                     llvmAtoms;
+       thinlto_code_gen_t thingenerator = init_thinlto_codegen(files, allAtoms, state, options, deadllvmAtoms, llvmAtoms);
+
+
+       ld::File::Ordinal lastOrdinal;
+       for (auto *f : files) {
+               if ( logBitcodeFiles) fprintf(stderr, "thinlto_codegen_add_module(%s)\n", f->path());
+               f->addToThinGenerator(thingenerator);
+               lastOrdinal = f->ordinal();
+       }
+
+#if LTO_API_VERSION >= 19
+       // In the bitcode bundle case, we first run the generator with codegen disabled
+       // and get the bitcode output. These files are added for later bundling, and a
+       // new codegenerator is setup with these as input, and the optimizer disabled.
+       if (options.bitcodeBundle) {
+               // Bitcode Bundle case
+               thinlto_codegen_disable_codegen(thingenerator, true);
+               // Process the optimizer only
+               thinlto_codegen_process(thingenerator);
+               auto numObjects = thinlto_module_get_num_objects(thingenerator);
+               // Save the codegenerator
+               thinlto_code_gen_t bitcode_generator = thingenerator;
+               // Create a new codegen generator for the codegen part.
+               thingenerator = init_thinlto_codegen(files, allAtoms, state, options, deadllvmAtoms, llvmAtoms);
+               // Disable the optimizer
+               thinlto_codegen_set_codegen_only(thingenerator, true);
+
+               // Save bitcode files for later, and add them to the codegen generator.
+               for (unsigned bufID = 0; bufID < numObjects; ++bufID) {
+                       auto machOFile = thinlto_module_get_object(bitcode_generator, bufID);
+                       std::string tempMachoPath = options.outputFilePath;
+                       tempMachoPath += ".";
+                       tempMachoPath += std::to_string(bufID);
+                       tempMachoPath += ".thinlto.o.bc";
+                       state.ltoBitcodePath.push_back(tempMachoPath);
+                       int fd = ::open(tempMachoPath.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
+                       if ( fd != -1 ) {
+                               ::write(fd, machOFile.Buffer, machOFile.Size);
+                               ::close(fd);
+                       } else {
+                               throwf("unable to write temporary ThinLTO output: %s", tempMachoPath.c_str());
+                       }
+
+                       // Add the optimized bitcode to the codegen generator now.
+                       ::thinlto_codegen_add_module(thingenerator, tempMachoPath.c_str(), (const char *)machOFile.Buffer, machOFile.Size);
+               }
+       }
+
+       if (options.ltoCodegenOnly)
+               // Disable the optimizer
+               thinlto_codegen_set_codegen_only(thingenerator, true);
+#endif
+
+       // run code generator
+       thinlto_codegen_process(thingenerator);
+       auto numObjects = thinlto_module_get_num_objects(thingenerator);
+       if (!numObjects)
+               throwf("could not do ThinLTO codegen (thinlto_codegen_process didn't produce any object): '%s', using libLTO version '%s'", ::lto_get_error_message(), ::lto_get_version());
+
+       // if requested, save off objects files
+       if ( options.saveTemps ) {
+               for (unsigned bufID = 0; bufID < numObjects; ++bufID) {
+                       auto machOFile = thinlto_module_get_object(thingenerator, bufID);
+                       std::string tempMachoPath = options.outputFilePath;
+                       tempMachoPath += ".";
+                       tempMachoPath += std::to_string(bufID);
+                       tempMachoPath += ".thinlto.o";
+                       int fd = ::open(tempMachoPath.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
+                       if ( fd != -1 ) {
+                               ::write(fd, machOFile.Buffer, machOFile.Size);
+                               ::close(fd);
+                       } else {
+                               warning("unable to write temporary ThinLTO output: %s", tempMachoPath.c_str());
+                       }
+               }
+       }
+
+
+       // mach-o parsing is done in-memory, but need path for debug notes
+       std::string macho_dirpath = "/tmp/thinlto.o";
+       if ( options.tmpObjectFilePath != NULL ) {
+               macho_dirpath = options.tmpObjectFilePath;
+               struct stat statBuffer;
+               if( stat(macho_dirpath.c_str(), &statBuffer) != 0 || !S_ISDIR(statBuffer.st_mode) ) {
+                       if ( mkdir(macho_dirpath.c_str(), 0700) !=0 ) {
+                               warning("unable to create ThinLTO output directory for temporary object files: %s", macho_dirpath.c_str());
+                       }
+               }
+       }
+
+       auto ordinal = ld::File::Ordinal::LTOOrdinal().nextFileListOrdinal();
+       for (unsigned bufID = 0; bufID < numObjects; ++bufID) {
+               auto machOFile = thinlto_module_get_object(thingenerator, bufID);
+               if (!machOFile.Size) {
+                       warning("Ignoring empty buffer generated by ThinLTO");
+                       continue;
+               }
+
+               // mach-o parsing is done in-memory, but need path for debug notes
+               std::string tmp_path = macho_dirpath + "/" + std::to_string(bufID) + ".o";
+
+               // parse generated mach-o file into a MachOReader
+               ld::relocatable::File* machoFile = parseMachOFile((const uint8_t *)machOFile.Buffer, machOFile.Size, tmp_path, options, ordinal);
+               ordinal = ordinal.nextFileListOrdinal();
+
+               // if needed, save temp mach-o file to specific location
+               if ( options.tmpObjectFilePath != NULL ) {
+                       int fd = ::open(tmp_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
+                       if ( fd != -1) {
+                               ::write(fd, (const uint8_t *)machOFile.Buffer, machOFile.Size);
+                               ::close(fd);
+                       }
+                       else {
+                               warning("could not write ThinLTO temp file '%s', errno=%d", tmp_path.c_str(), errno);
+                       }
+               }
+
+               // Load the generated MachO file
+               loadMachO(machoFile, options, handler, newAtoms, additionalUndefines, llvmAtoms, deadllvmAtoms);
+       }
+
+       // Remove Atoms from ld if code generator optimized them away
+       for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) {
+               // check if setRealAtom() called on this Atom
+               if ( li->second->compiledAtom() == NULL ) {
+                       //fprintf(stderr, "llvm optimized away %p %s\n", li->second, li->second->name());
+                       li->second->setCoalescedAway();
+               }
+       }
+
+       return true;
+#else // ! (LTO_API_VERSION >= 18)
+       throwf("lto: could not use -thinlto because ld was built against a version of libLTO too old (version '%d', >=18 is required)", LTO_API_VERSION);
+#endif
+}
+
+bool Parser::optimize(  const std::vector<const ld::Atom*>&    allAtoms,
+                                               ld::Internal&                                           state,
+                                               const OptimizeOptions&                          options,
+                                               ld::File::AtomHandler&                          handler,
+                                               std::vector<const ld::Atom*>&           newAtoms,
+                                               std::vector<const char*>&                       additionalUndefines)
+{
+
+       // exit quickly if nothing to do
+       if ( _s_files.size() == 0 )
+               return false;
+
+       // print out LTO version string if -v was used
+       if ( options.verbose )
+               fprintf(stderr, "%s\n", ::lto_get_version());
+
+       // <rdar://problem/12379604> The order that files are merged must match command line order
+       std::sort(_s_files.begin(), _s_files.end(), CommandLineOrderFileSorter());
+
+#if LTO_API_VERSION >= 19
+       // If ltoCodegenOnly is set, we don't want to merge any bitcode files and perform FullLTO
+       // we just take the ThinLTO path (optimization will be disabled anyway).
+       if (options.ltoCodegenOnly) {
+               for (auto *file : _s_files) {
+                       file->setIsThinLTO(true);
+               }
+       }
+#endif
+
+       std::vector<File *> theLTOFiles;
+       std::vector<File *> theThinLTOFiles;
+       for (auto *file : _s_files) {
+               if (file->isThinLTO()) {
+                       theThinLTOFiles.push_back(file);
+               } else {
+                       theLTOFiles.push_back(file);
+               }
+       }
+
+       auto result =  optimizeThinLTO(theThinLTOFiles, allAtoms, state, options, handler, newAtoms, additionalUndefines) &&
+                                  optimizeLTO(theLTOFiles, allAtoms, state, options, handler, newAtoms, additionalUndefines);
+
+       // Remove InternalAtoms from ld
+       for (std::vector<File*>::iterator it=_s_files.begin(); it != _s_files.end(); ++it) {
+               (*it)->internalAtom().setCoalescedAway();
+       }
+
+       return result;
+}
+
 
 void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom)
 {
        static const bool log = false;
-       static const ld::Atom* lastProxiedAtom = NULL;
-       static const ld::File* lastProxiedFile = NULL;
        // update proxy atoms to point to real atoms and find new atoms
        const char* name = machoAtom.name();
-       CStringToAtom::iterator pos = _llvmAtoms.find(name);
+       CStringToAtom::const_iterator pos = _llvmAtoms.find(name);
        if ( pos != _llvmAtoms.end() ) {
                // turn Atom into a proxy for this mach-o atom
                pos->second->setCompiledAtom(machoAtom);
-               lastProxiedAtom = &machoAtom;
-               lastProxiedFile = pos->second->file();
+               _lastProxiedAtom = &machoAtom;
+               _lastProxiedFile = pos->second->file();
                if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p synced to lto atom %p (name=%s)\n", &machoAtom, pos->second, machoAtom.name());
        }
        else {
                // an atom of this name was not in the allAtoms list the linker gave us
-               if ( _deadllvmAtoms.find(name) != _deadllvmAtoms.end() ) {
+               auto llvmAtom = _deadllvmAtoms.find(name);
+               if ( llvmAtom != _deadllvmAtoms.end() ) {
                        // this corresponding to an atom that the linker coalesced away or marked not-live
                        if ( _options.linkerDeadStripping ) {
                                // llvm seems to want this atom and -dead_strip is enabled, so it will be deleted if not needed, so add back
-                               Atom* llvmAtom = _deadllvmAtoms[name];
-                               llvmAtom->setCompiledAtom(machoAtom);
+                               llvmAtom->second->setCompiledAtom(machoAtom);
                                _newAtoms.push_back(&machoAtom);
-                               if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p matches dead lto atom %p but adding back (name=%s)\n", &machoAtom, llvmAtom, machoAtom.name());
+                               if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p matches dead lto atom %p but adding back (name=%s)\n", &machoAtom, llvmAtom->second, machoAtom.name());
                        }
                        else {
                                // Don't pass it back as a new atom
-                               if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p matches dead lto atom %p (name=%s)\n", &machoAtom, _deadllvmAtoms[name], machoAtom.name());
+                               if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p matches dead lto atom %p (name=%s)\n", &machoAtom, llvmAtom->second, machoAtom.name());
                        } 
                }
                else
@@ -949,9 +1427,10 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom)
                        // this is something new that lto conjured up, tell ld its new
                        _newAtoms.push_back(&machoAtom);
                        // <rdar://problem/15469363> if new static atom in same section as previous non-static atom, assign to same file as previous
-                       if ( (lastProxiedAtom != NULL) && (lastProxiedAtom->section() == machoAtom.section()) ) {
+                       if ( (_lastProxiedAtom != NULL) && (_lastProxiedAtom->section() == machoAtom.section()) ) {
                                ld::Atom* ma = const_cast<ld::Atom*>(&machoAtom);
-                               ma->setFile(lastProxiedFile);
+                               ma->setFile(_lastProxiedFile);
+                               if (log) fprintf(stderr, "AtomSyncer, mach-o atom %s is proxied to %s (path=%s)\n", machoAtom.name(), _lastProxiedAtom->name(), _lastProxiedFile->path());
                        }
                        if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p is totally new (name=%s)\n", &machoAtom, machoAtom.name());
                }
@@ -975,7 +1454,7 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom)
                                // llvm symbol references always go through Atom proxy.
                                {
                                        const char* targetName = fit->u.target->name();
-                                       CStringToAtom::iterator post = _llvmAtoms.find(targetName);
+                                       CStringToAtom::const_iterator post = _llvmAtoms.find(targetName);
                                        if ( post != _llvmAtoms.end() ) {
                                                const ld::Atom* t = post->second;
                                                if (log) fprintf(stderr, "    updating direct reference to %p to be ref to %p: %s\n", fit->u.target, t, targetName);
@@ -1023,6 +1502,21 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t ar
        return Parser::validFile(fileContent, fileLength, architecture, subarch);
 }
 
+//
+// Used by archive reader to see if member defines a Category (for -ObjC semantics)
+//
+bool hasObjCCategory(const uint8_t* fileContent, uint64_t fileLength)
+{
+#if LTO_API_VERSION >= 20
+       // note: if run with older libLTO.dylib that does not implement
+       // lto_module_has_objc_category, the call will return 0 which is "false"
+       return lto_module_has_objc_category(fileContent, fileLength);
+#else
+       return false;
+#endif
+}
+
+
 static ld::relocatable::File *parseImpl(
           const uint8_t *fileContent, uint64_t fileLength, const char *path,
           time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture,
index 38cfcd186f3d8cb442d2e0bc7a53648c9d7aaaea..b7601775de700824921f01f3a04eed42c45d1c26 100644 (file)
@@ -37,6 +37,8 @@ extern const char* archName(const uint8_t* fileContent, uint64_t fileLength);
 
 extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch);
 
+extern bool hasObjCCategory(const uint8_t* fileContent, uint64_t fileLength);
+
 extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength,
                                                                        const char* path, time_t modTime, ld::File::Ordinal ordinal,
                                                                        cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles,
@@ -45,6 +47,10 @@ extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLen
 struct OptimizeOptions {
        const char*                                                     outputFilePath;
        const char*                                                     tmpObjectFilePath;
+       const char*                                                     ltoCachePath;
+       int                                                                     ltoPruneInterval;
+       int                                                                     ltoPruneAfter;
+       unsigned                                                        ltoMaxCacheSize;
        bool                                                            preserveAllGlobals;
        bool                                                            verbose; 
        bool                                                            saveTemps; 
@@ -66,6 +72,7 @@ struct OptimizeOptions {
        cpu_type_t                                                      arch;
        const char*                                                     mcpu;
        Options::Platform                                       platform;
+       uint32_t                                                        minOSVersion;
        const std::vector<const char*>*         llvmOptions;
        const std::vector<const char*>*         initialUndefines;
 };
index 24d69c861e28d40004d171bea4fabd019306bf95..56b327ac097c595e803c963de111e75ae5209c85 100644 (file)
@@ -61,8 +61,8 @@ public:
                                                                                        File(const uint8_t* fileContent, uint64_t fileLength, const char* path,   
                                                                                                        time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace, 
                                                                                                        bool linkingMainExecutable, bool hoistImplicitPublicDylibs, 
-                                                                                                       Options::Platform platform, uint32_t linkMinOSVersion, bool allowSimToMacOSX,
-                                                                                                       bool addVers,  bool buildingForSimulator,
+                                                                                                       Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports,
+                                                                                                       bool allowSimToMacOSX, bool addVers,  bool buildingForSimulator,
                                                                                                        bool logAllFiles, const char* installPath,
                                                                                                        bool indirectDylib, bool ignoreMismatchPlatform, bool usingBitcode);
        virtual                                                                 ~File() noexcept {}
@@ -70,6 +70,7 @@ public:
 private:
        using P = typename A::P;
        using E = typename A::P::E;
+       using pint_t = typename A::P::uint_t;
 
        void                            addDyldFastStub();
        void                            buildExportHashTableFromExportInfo(const macho_dyld_info_command<P>* dyldInfo,
@@ -77,6 +78,7 @@ private:
        void                            buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P>* dynamicInfo,
                                                                                                                const macho_nlist<P>* symbolTable, const char* strings,
                                                                                                                const uint8_t* fileContent);
+       void                            addSymbol(const char* name, bool weakDef = false, bool tlv = false, pint_t address = 0);
        static const char*      objCInfoSegmentName();
        static const char*      objCInfoSectionName();
 
@@ -97,11 +99,11 @@ template <typename A> const char* File<A>::objCInfoSectionName() { return "__ima
 template <typename A>
 File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime,
                          ld::File::Ordinal ord, bool linkingFlatNamespace, bool linkingMainExecutable,
-                         bool hoistImplicitPublicDylibs, Options::Platform platform, uint32_t linkMinOSVersion,
+                         bool hoistImplicitPublicDylibs, Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports,
                          bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, bool logAllFiles,
                          const char* targetInstallPath, bool indirectDylib, bool ignoreMismatchPlatform, bool usingBitcode)
-       : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, linkingFlatNamespace,
-                  hoistImplicitPublicDylibs,   allowSimToMacOSX, addVers), _fileLength(fileLength), _linkeditStartOffset(0)
+       : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, allowWeakImports, linkingFlatNamespace,
+                  hoistImplicitPublicDylibs, allowSimToMacOSX, addVers), _fileLength(fileLength), _linkeditStartOffset(0)
 {
        const macho_header<P>* header = (const macho_header<P>*)fileContent;
        const uint32_t cmd_count = header->ncmds();
@@ -185,6 +187,9 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* path,
                                // <rdar://problem/20627554> Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked
                                this->_hasPublicInstallName = false;
                                break;
+                       case LC_RPATH:
+                               this->_rpaths.push_back(strdup(((macho_rpath_command<P>*)cmd)->path()));
+                               break;
                        case LC_VERSION_MIN_MACOSX:
                        case LC_VERSION_MIN_IPHONEOS:
                        case LC_VERSION_MIN_WATCHOS:
@@ -521,6 +526,67 @@ void File<A>::buildExportHashTableFromExportInfo(const macho_dyld_info_command<P
        }
 }
 
+template <typename A>
+void File<A>::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address)
+{
+       // symbols that start with $ld$ are meta-data to the static linker
+       // <rdar://problem/5182537> need way for ld and dyld to see different exported symbols in a dylib
+       if ( strncmp(name, "$ld$", 4) == 0 ) {
+               //    $ld$ <action> $ <condition> $ <symbol-name>
+               const char* symAction = &name[4];
+               const char* symCond = strchr(symAction, '$');
+               if ( symCond != nullptr ) {
+                       char curOSVers[16];
+                       sprintf(curOSVers, "$os%d.%d$", (this->_linkMinOSVersion >> 16), ((this->_linkMinOSVersion >> 8) & 0xFF));
+                       if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) {
+                               const char* symName = strchr(&symCond[1], '$');
+                               if ( symName != nullptr ) {
+                                       ++symName;
+                                       if ( strncmp(symAction, "hide$", 5) == 0 ) {
+                                               if ( this->_s_logHashtable )
+                                                       fprintf(stderr, "  adding %s to ignore set for %s\n", symName, this->path());
+                                               this->_ignoreExports.insert(strdup(symName));
+                                               return;
+                                       }
+                                       else if ( strncmp(symAction, "add$", 4) == 0 ) {
+                                               this->addSymbol(symName, weakDef);
+                                               return;
+                                       }
+                                       else if ( strncmp(symAction, "weak$", 5) == 0 ) {
+                                               if ( !this->_allowWeakImports )
+                                                       this->_ignoreExports.insert(strdup(symName));
+                                       }
+                                       else if ( strncmp(symAction, "install_name$", 13) == 0 ) {
+                                               this->_dylibInstallPath = symName;
+                                               this->_installPathOverride = true;
+                                               // <rdar://problem/14448206> CoreGraphics redirects to ApplicationServices, but with wrong compat version
+                                               if ( strcmp(this->_dylibInstallPath, "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices") == 0 )
+                                                       this->_dylibCompatibilityVersion = Options::parseVersionNumber32("1.0");
+                                               return;
+                                       }
+                                       else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) {
+                                               this->_dylibCompatibilityVersion = Options::parseVersionNumber32(symName);
+                                               return;
+                                       }
+                                       else {
+                                               warning("bad symbol action: %s in dylib %s", name, this->path());
+                                       }
+                               }
+                       }
+               }
+               else {
+                       warning("bad symbol condition: %s in dylib %s", name, this->path());
+               }
+       }
+
+       // add symbol as possible export if we are not supposed to ignore it
+       if ( this->_ignoreExports.count(name) == 0 ) {
+               typename Base::AtomAndWeak bucket = { nullptr, weakDef, tlv, address };
+               if ( this->_s_logHashtable )
+                       fprintf(stderr, "  adding %s to hash table for %s\n", name, this->path());
+               this->_atoms[strdup(name)] = bucket;
+       }
+}
 
 template <>
 void File<x86_64>::addDyldFastStub()
@@ -554,7 +620,7 @@ public:
        {
                return new File<A>(fileContent, fileLength, path, mTime, ordinal, opts.flatNamespace(),
                                                   opts.linkingMainExecutable(), opts.implicitlyLinkIndirectPublicDylibs(),
-                                                  opts.platform(), opts.minOSversion(),
+                                                  opts.platform(), opts.minOSversion(), opts.allowWeakImports(),
                                                   opts.allowSimulatorToLinkWithMacOSX(), opts.addVersionLoadCommand(),
                                                   opts.targetIOSSimulator(), opts.logAllFiles(), opts.installPath(),
                                                   indirectDylib, opts.outputKind() == Options::kPreload, opts.bundleBitcode());
@@ -755,6 +821,7 @@ const char* Parser<arm64>::fileKind(const uint8_t* fileContent)
 }
 #endif
 
+
 //
 // used by linker is error messages to describe mismatched files
 //
index ddf687a002122224546878647c0b9ac856d04d7a..776e695a3846fa229a9efd02ea30efb2691e7ff6 100644 (file)
@@ -86,7 +86,8 @@ public:
                                                                                                _minOSVersion(0),
                                                                                                _platform(0),
                                                                                                _canScatterAtoms(false),
-                                                                                               _srcKind(kSourceUnknown) {}
+                                                                                               _objcHasCategoryClassPropertiesField(false),
+                                                                                               _srcKind(kSourceUnknown) { }
        virtual                                                                 ~File();
 
        // overrides of ld::File
@@ -98,6 +99,8 @@ public:
 
        // overrides of ld::relocatable::File 
        virtual ObjcConstraint                                                          objCConstraint() const                  { return _objConstraint; }
+       virtual bool                                                                            objcHasCategoryClassPropertiesField() const 
+                                                                                                                                                                       { return _objcHasCategoryClassPropertiesField; }
        virtual uint32_t                                                                        cpuSubType() const                              { return _cpuSubType; }
        virtual DebugInfoKind                                                           debugInfo() const                               { return _debugInfoKind; }
        virtual const std::vector<ld::relocatable::File::Stab>* stabs() const                           { return &_stabs; }
@@ -140,6 +143,7 @@ private:
        uint32_t                                                                _minOSVersion;
        uint32_t                                                                _platform;
        bool                                                                    _canScatterAtoms;
+       bool                                                                    _objcHasCategoryClassPropertiesField;
        std::vector<std::vector<const char*> >  _linkerOptions;
        std::unique_ptr<ld::Bitcode>                    _bitcode;
        SourceKind                                                              _srcKind;
@@ -385,6 +389,10 @@ public:
                                                TLVDefsSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) :
                                                        SymboledSection<A>(parser, f, s) { }
 
+       typedef typename A::P::uint_t   pint_t;
+
+       virtual ld::Atom::Alignment             alignmentForAddress(pint_t addr)                { return ld::Atom::Alignment(log2(sizeof(pint_t))); }
+
 private:
 
 };
@@ -530,6 +538,7 @@ protected:
        typedef typename A::P::uint_t   pint_t;
        typedef typename A::P                   P;
 
+       virtual void                                    makeFixups(class Parser<A>& parser, const struct Parser<A>::CFI_CU_InfoArrays&);
        virtual ld::Atom::ContentType   contentType()                                                   { return ld::Atom::typeTLVPointer; }
        virtual ld::Atom::Alignment             alignmentForAddress(pint_t addr)                { return ld::Atom::Alignment(log2(sizeof(pint_t))); }
        virtual const char*                             unlabeledAtomName(Parser<A>&, pint_t)   { return "tlv_lazy_ptr"; }
@@ -930,6 +939,7 @@ void Atom<arm64>::verifyAlignment(const macho_section<P>& sect) const
 }
 #endif
 
+
 template <typename A>
 void Atom<A>::verifyAlignment(const macho_section<P>&) const
 {
@@ -1406,6 +1416,7 @@ const char* Parser<arm64>::fileKind(const uint8_t* fileContent)
 }
 #endif
 
+
 template <typename A>
 bool Parser<A>::hasObjC2Categories(const uint8_t* fileContent)
 {
@@ -2525,6 +2536,7 @@ void Parser<A>::makeSections()
                        // #define OBJC_IMAGE_SUPPORTS_GC   2
                        // #define OBJC_IMAGE_GC_ONLY       4
                        // #define OBJC_IMAGE_IS_SIMULATED  32
+                       // #define OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES  64
                        //
                        const uint32_t* contents = (uint32_t*)(_file->fileContent()+sect->offset());
                        if ( (sect->size() >= 8) && (contents[0] == 0) ) {
@@ -2538,6 +2550,7 @@ void Parser<A>::makeSections()
                                else
                                        _file->_objConstraint = ld::File::objcConstraintRetainRelease;
                                _file->_swiftVersion = ((flags >> 8) & 0xFF);
+                _file->_objcHasCategoryClassPropertiesField = (flags & 64);
                                if ( sect->size() > 8 ) {
                                        warning("section %s/%s has unexpectedly large size %llu in %s", 
                                                        sect->segname(), Section<A>::makeSectionName(sect), sect->size(), _file->path());
@@ -3167,10 +3180,10 @@ uint32_t TentativeDefinitionSection<A>::appendAtoms(class Parser<A>& parser, uin
                                alignP2 = 63 - (uint8_t)__builtin_clzll(size);
                                if ( size != (1ULL << alignP2) )
                                        ++alignP2;
+                               // <rdar://problem/24871389> limit default alignment of large commons
+                               if ( alignP2 > parser.maxDefaultCommonAlignment() )
+                                       alignP2 = parser.maxDefaultCommonAlignment();
                        }
-                       // limit alignment of extremely large commons to 2^15 bytes (8-page)
-                       if ( alignP2 > parser.maxDefaultCommonAlignment() )
-                               alignP2 = parser.maxDefaultCommonAlignment();
                        Atom<A>* allocatedSpace = (Atom<A>*)p;
                        new (allocatedSpace) Atom<A>(*this, parser.nameFromSymbol(sym), (pint_t)ULLONG_MAX, size,
                                                                                ld::Atom::definitionTentative,  ld::Atom::combineByName, 
@@ -3270,10 +3283,11 @@ uint32_t Parser<A>::symbolIndexFromIndirectSectionAddress(pint_t addr, const mac
                        break;
                case S_LAZY_SYMBOL_POINTERS:
                case S_NON_LAZY_SYMBOL_POINTERS:
+               case S_THREAD_LOCAL_VARIABLE_POINTERS:
                        elementSize = sizeof(pint_t);
                        break;
                default:
-                       throw "section does not use inirect symbol table";
+                       throw "section does not use indirect symbol table";
        }       
        uint32_t indexInSection = (addr - sect->addr()) / elementSize;
        uint32_t indexIntoIndirectTable = sect->reserved1() + indexInSection;
@@ -4387,6 +4401,7 @@ bool CFISection<arm64>::needsRelocating()
        return true;
 }
 
+
 template <typename A>
 bool CFISection<A>::needsRelocating()
 {
@@ -4611,7 +4626,6 @@ template <> bool CFISection<x86>::bigEndian() { return false; }
 template <> bool CFISection<arm>::bigEndian() { return false; }
 template <> bool CFISection<arm64>::bigEndian() { return false; }
 
-
 template <>
 void CFISection<x86_64>::addCiePersonalityFixups(class Parser<x86_64>& parser, const CFI_Atom_Info* cieInfo)
 {
@@ -4683,6 +4697,7 @@ void CFISection<arm64>::addCiePersonalityFixups(class Parser<arm64>& parser, con
 }
 #endif
 
+
 template <>
 void CFISection<arm>::addCiePersonalityFixups(class Parser<arm>& parser, const CFI_Atom_Info* cieInfo)
 {
@@ -5008,6 +5023,7 @@ const char* CUSection<arm64>::personalityName(class Parser<arm64>& parser, const
 }
 #endif
 
+
 #if SUPPORT_ARCH_arm_any
 template <>
 const char* CUSection<arm>::personalityName(class Parser<arm>& parser, const macho_relocation_info<arm::P>* reloc)
@@ -5072,6 +5088,7 @@ bool CUSection<arm64>::encodingMeansUseDwarf(compact_unwind_encoding_t enc)
 }
 #endif
 
+
 template <typename A>
 int CUSection<A>::infoSorter(const void* l, const void* r)
 {
@@ -5313,6 +5330,7 @@ ld::Atom::SymbolTableInclusion ImplicitSizeSection<arm64>::symbolTableInclusion(
        return ld::Atom::symbolTableInWithRandomAutoStripLabel;
 }
 
+
 template <typename A>
 ld::Atom::SymbolTableInclusion ImplicitSizeSection<A>::symbolTableInclusion()
 {
@@ -5753,6 +5771,47 @@ ld::Atom::Combine TLVPointerSection<A>::combine(Parser<A>& parser, pint_t addr)
        return ld::Atom::combineByNameAndReferences;
 }
 
+template <>
+void TLVPointerSection<arm>::makeFixups(class Parser<arm>& parser, const struct Parser<arm>::CFI_CU_InfoArrays&)
+{
+       // add references for each thread local pointer atom based on indirect symbol table
+       const macho_section<P>* sect = this->machoSection();
+       const pint_t endAddr = sect->addr() + sect->size();
+       for (pint_t addr = sect->addr(); addr < endAddr; addr += sizeof(pint_t)) {
+               typename Parser<arm>::SourceLocation    src;
+               typename Parser<arm>::TargetDesc                target;
+               src.atom = this->findAtomByAddress(addr);
+               src.offsetInAtom = 0;
+               uint32_t symIndex = parser.symbolIndexFromIndirectSectionAddress(addr, sect);
+               target.atom = NULL;
+               target.name = NULL;
+               target.weakImport = false;
+               target.addend = 0;
+               if ( symIndex == INDIRECT_SYMBOL_LOCAL ) {
+                       throwf("unexpected INDIRECT_SYMBOL_LOCAL in section %s", this->sectionName());
+               }
+               else {
+                       const macho_nlist<P>& sym = parser.symbolFromIndex(symIndex);
+                       // use direct reference for local symbols
+                       if ( ((sym.n_type() & N_TYPE) == N_SECT) && ((sym.n_type() & N_EXT) == 0) ) {
+                               throwf("unexpected pointer to local symbol in section %s", this->sectionName());
+                       }
+                       else {
+                               target.name = parser.nameFromSymbol(sym);
+                               target.weakImport = parser.weakImportFromSymbol(sym);
+                               assert(src.atom->combine() == ld::Atom::combineByNameAndReferences);
+                       }
+               }
+               parser.addFixups(src, ld::Fixup::kindStoreLittleEndian32, target);
+       }
+}
+
+template <typename A>
+void TLVPointerSection<A>::makeFixups(class Parser<A>& parser, const struct Parser<A>::CFI_CU_InfoArrays&)
+{
+       assert(0 && "should not have thread-local-pointer sections in .o files");
+}
+
 
 template <typename A>
 const char* TLVPointerSection<A>::targetName(const class Atom<A>* atom, const ld::IndirectBindingTable& ind, bool* isStatic)
@@ -7406,6 +7465,7 @@ bool Section<arm64>::addRelocFixup(class Parser<arm64>& parser, const macho_relo
 }
 #endif
 
+
 template <typename A>
 bool ObjC1ClassSection<A>::addRelocFixup(class Parser<A>& parser, const macho_relocation_info<P>* reloc)
 {
@@ -7557,6 +7617,7 @@ void Section<arm64>::addLOH(class Parser<arm64>& parser, int kind, int count, co
 }
 #endif
 
+
 template <typename A>
 void Section<A>::addLOH(class Parser<A>& parser, int kind, int count, const uint64_t addrs[]) {
 
index fb452d8b6fcb9590234b1fb2e2aebd1c216af69b..585446254e6bc3b33b4646e1f3d11a976c730900 100644 (file)
@@ -25,7 +25,7 @@
 
 #include <sys/param.h>
 #include <sys/mman.h>
-
+#include <tapi/tapi.h>
 #include <vector>
 
 #include "Architectures.hpp"
 #include "generic_dylib_file.hpp"
 #include "textstub_dylib_file.hpp"
 
-namespace {
-
-///
-/// A token is a light-weight reference to the content of an nmap'ed file. It
-/// doesn't own the data and it doesn't make a copy of it. The referenced data
-/// is only valid as long as the file is mapped in.
-///
-class Token {
-       const char* _p;
-       size_t _size;
-
-       int compareMemory(const char* lhs, const char* rhs, size_t size) const {
-               if (size == 0)
-                       return 0;
-               return ::memcmp(lhs, rhs, size);
-       }
-
-public:
-       Token() : _p(nullptr), _size(0) {}
-
-       Token(const char* p) : _p(p), _size(0) {
-               if (p)
-                       _size = ::strlen(p);
-       }
-
-       Token(const char* p, size_t s) : _p(p), _size(s) {}
-
-       const char* data() const { return _p; }
-
-       size_t size() const { return _size; }
-
-       std::string str() const { return std::string(_p, _size); }
-
-       bool empty() const { return _size == 0; }
-
-       bool operator==(Token other) const {
-               if (_size != other._size)
-                       return false;
-               return compareMemory(_p, other._p, _size) == 0;
-       }
-
-       bool operator!=(Token other) const {
-               return !(*this == other);
-       }
-};
-
-///
-/// Simple text-based dynamic library file tokenizer.
-///
-class Tokenizer {
-       const char* _start;
-       const char* _current;
-       const char* _end;
-       Token _currentToken;
-
-       void fetchNextToken();
-       void scanToNextToken();
-       void skip(unsigned distance) {
-               _current += distance;
-               assert(_current <= _end && "Skipped past the end");
-       }
-
-       const char* skipLineBreak(const char* pos) const;
-       bool isDelimiter(const char* pos) const;
-
-public:
-       Tokenizer(const char* data, uint64_t size) : _start(data), _current(data), _end(data + size) {}
-
-       void reset() {
-               _current = _start;
-               fetchNextToken();
-       }
-
-       Token peek() { return _currentToken; }
-       Token next() {
-               Token token = peek();
-               fetchNextToken();
-               return token;
-       }
-};
-
-const char* Tokenizer::skipLineBreak(const char* pos) const
-{
-       if ( pos == _end )
-               return pos;
-
-       // Carriage return.
-       if ( *pos == 0x0D ) {
-               // line feed.
-               if ( pos + 1 != _end && *(pos + 1) == 0x0A)
-                       return pos + 2;
-               return pos + 1;
-       }
-
-       // line feed.
-       if ( *pos == 0x0A )
-               return pos + 1;
-
-       return pos;
-}
-
-void Tokenizer::scanToNextToken() {
-       while (true) {
-               while ( isDelimiter(_current) )
-                       skip(1);
-
-               const char* i = skipLineBreak(_current);
-               if ( i == _current )
-                       break;
-
-               _current = i;
-       }
-}
-
-
-bool Tokenizer::isDelimiter(const char* pos) const {
-       if ( pos == _end )
-               return false;
-       if ( *pos == ' ' || *pos == '\t' || *pos == '\r' || *pos == '\n' || *pos == ',' || *pos == ':' || *pos == '\'' || *pos == '\"' )
-               return true;
-       return false;
-}
-
-void Tokenizer::fetchNextToken() {
-       scanToNextToken();
-
-       if (_current == _end) {
-               _currentToken = Token();
-               return;
-       }
-
-       auto start = _current;
-       while ( !isDelimiter(_current) ) {
-               ++_current;
-       }
-
-       _currentToken = Token(start, _current - start);
-}
-
-///
-/// Representation of a parsed text-based dynamic library file.
-///
-struct DynamicLibrary {
-       Token _installName;
-       uint32_t _currentVersion;
-       uint32_t _compatibilityVersion;
-       uint8_t _swiftVersion;
-       ld::File::ObjcConstraint _objcConstraint;
-       Options::Platform _platform;
-       std::vector<Token> _allowedClients;
-       std::vector<Token> _reexportedLibraries;
-       std::vector<Token> _symbols;
-       std::vector<Token> _classes;
-       std::vector<Token> _ivars;
-       std::vector<Token> _weakDefSymbols;
-       std::vector<Token> _tlvSymbols;
-
-       DynamicLibrary() : _currentVersion(0x10000), _compatibilityVersion(0x10000), _swiftVersion(0),
-               _objcConstraint(ld::File::objcConstraintNone)  {}
-};
-
-///
-/// A simple text-based dynamic library file parser.
-///
-class TBDFile {
-       Tokenizer _tokenizer;
-
-       Token peek() { return _tokenizer.peek(); }
-       Token next() { return _tokenizer.next(); }
-
-       void expectToken(Token str) {
-               Token token = next();
-               if (token != str)
-                       throwf("unexpected token: %s", token.str().c_str());
-       }
-
-       bool hasOptionalToken(Token str) {
-               auto token = peek();
-               if ( token == str ) {
-                       next();
-                       return true;
-               }
-               return false;
-       }
-
-
-       void parseFlowSequence(std::function<void (Token)> func) {
-               expectToken("[");
-
-               while ( true ) {
-                       auto token = peek();
-                       if ( token == "]" )
-                               break;
-
-                       token = next();
-                       func(token);
-               }
-
-               expectToken("]");
-       }
-
-       void parseAllowedClients(DynamicLibrary& lib) {
-               if ( !hasOptionalToken("allowed-clients") )
-                       return;
-               parseFlowSequence([&](Token name) {
-                       lib._allowedClients.emplace_back(name);
-               });
-       }
-
-       void parseReexportedDylibs(DynamicLibrary& lib) {
-               if ( !hasOptionalToken("re-exports") )
-                       return;
-               parseFlowSequence([&](Token name) {
-                       lib._reexportedLibraries.emplace_back(name);
-               });
-       }
-
-       void parseSymbols(DynamicLibrary& lib) {
-               if ( hasOptionalToken("symbols") ) {
-                       parseFlowSequence([&](Token name) {
-                               lib._symbols.emplace_back(name);
-                       });
-               }
-
-               if ( hasOptionalToken("objc-classes") ) {
-                       parseFlowSequence([&](Token name) {
-                               lib._classes.emplace_back(name);
-                       });
-               }
-
-               if ( hasOptionalToken("objc-ivars") ) {
-                       parseFlowSequence([&](Token name) {
-                               lib._ivars.emplace_back(name);
-                       });
-               }
-
-               if ( hasOptionalToken("weak-def-symbols") ) {
-                       parseFlowSequence([&](Token name) {
-                               lib._weakDefSymbols.emplace_back(name);
-                       });
-               }
-
-               if ( hasOptionalToken("thread-local-symbols") ) {
-                       parseFlowSequence([&](Token name) {
-                               lib._tlvSymbols.emplace_back(name);
-                       });
-               }
-       }
-
-       std::vector<std::string> parseArchFlowSequence() {
-               std::vector<std::string> availabledArchitectures;
-               expectToken("archs");
-               parseFlowSequence([&](Token name) {
-                       availabledArchitectures.emplace_back(name.str());
-               });
-               return availabledArchitectures;
-       }
-
-       bool parseArchFlowSequence(std::string &selectedArchName) {
-               auto availabledArchitectures = parseArchFlowSequence();
-
-               for (const auto &archName : availabledArchitectures) {
-                       if (archName == selectedArchName)
-                               return true;
-               }
-
-               return false;
-       }
-
-       void parsePlatform(DynamicLibrary& lib) {
-               expectToken("platform");
-
-               auto token =  next();
-               if (token == "macosx")
-                       lib._platform = Options::kPlatformOSX;
-               else if (token == "ios")
-                       lib._platform = Options::kPlatformiOS;
-               else if (token == "watchos")
-                       lib._platform = Options::kPlatformWatchOS;
-#if SUPPORT_APPLE_TV
-               else if (token == "tvos")
-                       lib._platform = Options::kPlatform_tvOS;
-#endif
-               else
-                       lib._platform = Options::kPlatformUnknown;
-       }
-
-       void parseInstallName(DynamicLibrary& lib) {
-               expectToken("install-name");
-
-               lib._installName = next();
-               if ( lib._installName.empty() )
-                       throwf("no install name specified");
-       }
-
-       uint32_t parseVersionNumber32(Token token) {
-               if ( token.size() >= 128 )
-                       throwf("malformed version number");
-
-               // Make a null-terminated string.
-               char buffer[128];
-               ::memcpy(buffer, token.data(), token.size());
-               buffer[token.size()] = '\0';
-
-               return Options::parseVersionNumber32(buffer);
-       }
-
-       void parseCurrentVersion(DynamicLibrary& lib) {
-               if ( !hasOptionalToken("current-version") )
-                       return;
-               lib._currentVersion = parseVersionNumber32(next());
-       }
-
-       void parseCompatibilityVersion(DynamicLibrary& lib) {
-               if ( !hasOptionalToken("compatibility-version") )
-                       return;
-               lib._compatibilityVersion = parseVersionNumber32(next());
-       }
-
-       void parseSwiftVersion(DynamicLibrary& lib) {
-               if ( !hasOptionalToken("swift-version") )
-                       return;
-               auto token = next();
-               if ( token == "1.0" )
-                       lib._swiftVersion = 1;
-               else if ( token == "1.1" )
-                       lib._swiftVersion = 2;
-               else if ( token == "2.0" )
-                       lib._swiftVersion = 3;
-               else
-                       throwf("unsupported Swift ABI version: %s", token.str().c_str());
-       }
-
-       void parseObjCConstraint(DynamicLibrary& lib) {
-               if ( !hasOptionalToken("objc-constraint") )
-                       return;
-               auto token = next();
-               if ( token == "none" )
-                       lib._objcConstraint = ld::File::objcConstraintNone;
-               else if ( token == "retain_release" )
-                       lib._objcConstraint = ld::File::objcConstraintRetainRelease;
-               else if ( token == "retain_release_for_simulator" )
-                       lib._objcConstraint = ld::File::objcConstraintRetainReleaseForSimulator;
-               else if ( token == "retain_release_or_gc" )
-                       lib._objcConstraint = ld::File::objcConstraintRetainReleaseOrGC;
-               else if ( token == "gc" )
-                       lib._objcConstraint = ld::File::objcConstraintGC;
-               else
-                       throwf("unexpected token: %s", token.str().c_str());
-       }
-       void parseExportsBlock(DynamicLibrary& lib, std::string &selectedArchName) {
-               if ( !hasOptionalToken("exports") )
-                       return;
-
-               if ( !hasOptionalToken("-") )
-                       return;
-
-               while ( true ) {
-                       if ( !parseArchFlowSequence(selectedArchName) ) {
-                               Token token;
-                               while ( true ) {
-                                       token = peek();
-                                       if ( token == "archs" || token == "..." || token.empty() )
-                                               break;
-                                       next();
-                               }
-                               if (token == "..." || token.empty() )
-                                       break;
-
-                               continue;
-                       }
-
-                       parseAllowedClients(lib);
-                       parseReexportedDylibs(lib);
-                       parseSymbols(lib);
-                       if ( !hasOptionalToken("-") )
-                               break;
-               }
-       }
-
-       std::vector<std::string> getCompatibleArchList(std::string &requestedArchName) {
-               if (requestedArchName == "i386")
-                       return {"i386"};
-               else if (requestedArchName == "x86_64" || requestedArchName == "x86_64h")
-                       return {"x86_64", "x86_64h"};
-               else if (requestedArchName == "armv7" || requestedArchName == "armv7s")
-                       return {"armv7", "armv7s"};
-               else if (requestedArchName == "armv7k")
-                       return {"armv7k"};
-               else if (requestedArchName == "arm64")
-                       return {"arm64"};
-               else
-                       return {};
-       }
-
-       std::string parseAndSelectArchitecture(std::string &requestedArchName) {
-               auto availabledArchitectures = parseArchFlowSequence();
-
-               // First try to find an exact match (cpu type and sub-cpu type).
-               if (std::find(availabledArchitectures.begin(), availabledArchitectures.end(), requestedArchName)
-                       != availabledArchitectures.end())
-                       return requestedArchName;
-
-               // If there is no exact match, then try to find an ABI compatible slice.
-               auto compatibleArchitectures = getCompatibleArchList(requestedArchName);
-               std::vector<std::string> result;
-               std::sort(availabledArchitectures.begin(), availabledArchitectures.end());
-               std::sort(compatibleArchitectures.begin(), compatibleArchitectures.end());
-               std::set_intersection(availabledArchitectures.begin(), availabledArchitectures.end(),
-                                                         compatibleArchitectures.begin(), compatibleArchitectures.end(),
-                                                         std::back_inserter(result));
-
-               if (result.empty())
-                       return std::string();
-               else
-                       return result.front();
-       }
-
-       void parseDocument(DynamicLibrary& lib, std::string &requestedArchName) {
-               auto selectedArchName = parseAndSelectArchitecture(requestedArchName);
-               if (selectedArchName.empty())
-                       throwf("invalid arch");
-
-               parsePlatform(lib);
-               parseInstallName(lib);
-               parseCurrentVersion(lib);
-               parseCompatibilityVersion(lib);
-               parseSwiftVersion(lib);
-               parseObjCConstraint(lib);
-               parseExportsBlock(lib, selectedArchName);
-       }
-
-public:
-       TBDFile(const char* data, uint64_t size) : _tokenizer(data, size) {}
-
-       DynamicLibrary parseFileForArch(std::string requestedArchName) {
-               _tokenizer.reset();
-               DynamicLibrary lib;
-               expectToken("---");
-               parseDocument(lib, requestedArchName);
-               expectToken("...");
-               return lib;
-       }
-
-       bool validForArch(std::string requestedArchName) {
-               _tokenizer.reset();
-               auto token = next();
-               if ( token != "---" )
-                       return false;
-               return !parseAndSelectArchitecture(requestedArchName).empty();
-       }
-
-       void dumpTokens() {
-               _tokenizer.reset();
-               Token token;
-               do {
-                       token = next();
-                       printf("token: %s\n", token.str().c_str());
-               } while ( !token.empty() );
-       }
-};
-
-} // end anonymous namespace
 
 namespace textstub {
 namespace dylib {
@@ -513,51 +50,94 @@ class File final : public generic::dylib::File<A>
        using Base = generic::dylib::File<A>;
 
 public:
-       static bool             validFile(const uint8_t* fileContent, bool executableOrDylib);
-                                       File(const uint8_t* fileContent, uint64_t fileLength, const char* path,
+                                       File(const char* path, const uint8_t* fileContent, uint64_t fileLength,
                                                 time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace,
-                                                bool hoistImplicitPublicDylibs, Options::Platform platform,
-                                                cpu_type_t cpuType, const char* archName, uint32_t linkMinOSVersion,
+                                                bool linkingMainExecutable, bool hoistImplicitPublicDylibs,
+                                                Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports,
+                                                cpu_type_t cpuType, cpu_subtype_t cpuSubType, bool enforceDylibSubtypesMatch,
                                                 bool allowSimToMacOSX, bool addVers, bool buildingForSimulator,
                                                 bool logAllFiles, const char* installPath, bool indirectDylib);
        virtual                 ~File() noexcept {}
 
 private:
-       void                    buildExportHashTable(const DynamicLibrary &lib);
-
-       cpu_type_t              _cpuType;
+       void                    buildExportHashTable(const tapi::LinkerInterfaceFile* file);
 };
 
+static ld::File::ObjcConstraint mapObjCConstraint(tapi::ObjCConstraint constraint) {
+       switch (constraint) {
+       case tapi::ObjCConstraint::None:
+               return ld::File::objcConstraintNone;
+       case tapi::ObjCConstraint::Retain_Release:
+               return ld::File::objcConstraintRetainRelease;
+       case tapi::ObjCConstraint::Retain_Release_For_Simulator:
+               return ld::File::objcConstraintRetainReleaseForSimulator;
+       case tapi::ObjCConstraint::Retain_Release_Or_GC:
+               return ld::File::objcConstraintRetainReleaseOrGC;
+       case tapi::ObjCConstraint::GC:
+               return ld::File::objcConstraintGC;
+       }
+
+       return ld::File::objcConstraintNone;
+}
+
+static Options::Platform mapPlatform(tapi::Platform platform) {
+       switch (platform) {
+       case tapi::Platform::Unknown:
+               return Options::kPlatformUnknown;
+       case tapi::Platform::OSX:
+               return Options::kPlatformOSX;
+       case tapi::Platform::iOS:
+               return Options::kPlatformiOS;
+       case tapi::Platform::watchOS:
+               return Options::kPlatformWatchOS;
+       case tapi::Platform::tvOS:
+               return Options::kPlatform_tvOS;
+       }
+
+       return Options::kPlatformUnknown;
+}
+
 template <typename A>
-File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime,
-                         ld::File::Ordinal ord, bool linkingFlatNamespace, bool hoistImplicitPublicDylibs,
-                         Options::Platform platform, cpu_type_t cpuType, const char* archName,
-                         uint32_t linkMinOSVersion, bool allowSimToMacOSX, bool addVers,
+       File<A>::File(const char* path, const uint8_t* fileContent, uint64_t fileLength,
+                         time_t mTime, ld::File::Ordinal ord, bool linkingFlatNamespace,
+                         bool linkingMainExecutable, bool hoistImplicitPublicDylibs, Options::Platform platform,
+                         uint32_t linkMinOSVersion, bool allowWeakImports, cpu_type_t cpuType, cpu_subtype_t cpuSubType,
+                               bool enforceDylibSubtypesMatch, bool allowSimToMacOSX, bool addVers,
                          bool buildingForSimulator, bool logAllFiles, const char* targetInstallPath,
                          bool indirectDylib)
-       : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, linkingFlatNamespace,
-                  hoistImplicitPublicDylibs, allowSimToMacOSX, addVers),
-         _cpuType(cpuType)
+       : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, allowWeakImports, linkingFlatNamespace,
+                  hoistImplicitPublicDylibs, allowSimToMacOSX, addVers)
 {
-       this->_bitcode = std::unique_ptr<ld::Bitcode>(new ld::Bitcode(nullptr, 0));
-       // Text stubs are implicit app extension safe.
-       this->_appExtensionSafe = true;
+       auto matchingType = enforceDylibSubtypesMatch ?
+                       tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible;
+
+       std::string errorMessage;
+       auto file = std::unique_ptr<tapi::LinkerInterfaceFile>(
+               tapi::LinkerInterfaceFile::create(path, fileContent, fileLength, cpuType,
+                                                                                 cpuSubType, matchingType,
+                                                                                 tapi::PackedVersion32(linkMinOSVersion), errorMessage));
+
+       if (file == nullptr)
+               throw strdup(errorMessage.c_str());
+
+       // unmap file - it is no longer needed.
+       munmap((caddr_t)fileContent, fileLength);
 
        // write out path for -t option
        if ( logAllFiles )
                printf("%s\n", path);
 
-       TBDFile stub((const char*)fileContent, fileLength);
-       auto lib = stub.parseFileForArch(archName);
-
-       this->_noRexports = lib._reexportedLibraries.empty();
-       this->_hasWeakExports = !lib._weakDefSymbols.empty();
-       this->_dylibInstallPath = strdup(lib._installName.str().c_str());
-       this->_dylibCurrentVersion = lib._currentVersion;
-       this->_dylibCompatibilityVersion = lib._compatibilityVersion;
-       this->_swiftVersion = lib._swiftVersion;
-       this->_objcConstraint = lib._objcConstraint;
-       this->_hasPublicInstallName = this->isPublicLocation(this->_dylibInstallPath);
+       this->_bitcode = std::unique_ptr<ld::Bitcode>(new ld::Bitcode(nullptr, 0));
+       this->_noRexports = !file->hasReexportedLibraries();
+       this->_hasWeakExports = file->hasWeakDefinedExports();
+       this->_dylibInstallPath = strdup(file->getInstallName().c_str());
+       this->_installPathOverride = file->isInstallNameVersionSpecific();
+       this->_dylibCurrentVersion = file->getCurrentVersion();
+       this->_dylibCompatibilityVersion = file->getCompatibilityVersion();
+       this->_swiftVersion = file->getSwiftVersion();
+       this->_objcConstraint = mapObjCConstraint(file->getObjCConstraint());
+       this->_parentUmbrella = file->getParentFrameworkName().empty() ? nullptr : strdup(file->getParentFrameworkName().c_str());
+       this->_appExtensionSafe = file->isApplicationExtensionSafe();
 
        // if framework, capture framework name
        const char* lastSlash = strrchr(this->_dylibInstallPath, '/');
@@ -571,100 +151,69 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* path,
                        this->_frameworkName = leafName;
        }
 
-  // TEMPORARY HACK BEGIN: Support ancient re-export command LC_SUB_FRAMEWORK.
-       // <rdar://problem/23614899> [TAPI] Support LC_SUB_FRAMEWORK as re-export indicator.
-       auto installName = std::string(this->_dylibInstallPath);
-
-       // All sub-frameworks of ApplicationServices use LC_SUB_FRAMEWORK.
-       if (installName.find("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/") == 0 &&
-                       installName.find(".dylib") == std::string::npos) {
-               this->_parentUmbrella = "ApplicationServices";
-       } else if (installName.find("/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/") == 0) {
-               this->_parentUmbrella = "Carbon";
-       } else if (installName.find("/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/") == 0 &&
-                                        installName.find(".dylib") == std::string::npos) {
-               this->_parentUmbrella = "CoreServices";
-       } else if (installName.find("/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib") == 0 ||
-                                        installName.find("/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib") == 0 ||
-                                        installName.find("System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib") == 0) {
-               this->_parentUmbrella = "vecLib";
-       } else if (installName.find("/System/Library/Frameworks/WebKit.framework/Versions/A/Frameworks/WebCore.framework/Versions/A/WebCore") == 0) {
-               this->_parentUmbrella = "WebKit";
-       } else if (installName.find("/usr/lib/system/") == 0 &&
-                          installName != "/usr/lib/system/libkxld.dylib") {
-               this->_parentUmbrella = "System";
-       }
-       // TEMPORARY HACK END
-
-       for (auto &client : lib._allowedClients) {
-               if ((this->_parentUmbrella != nullptr) && (client.str() != this->_parentUmbrella))
-                       this->_allowableClients.push_back(strdup(client.str().c_str()));
-       }
+       for (auto &client : file->allowableClients())
+               this->_allowableClients.push_back(strdup(client.c_str()));
 
        // <rdar://problem/20659505> [TAPI] Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked
-       if ( !this->_allowableClients.empty() )
-               this->_hasPublicInstallName = false;
+       this->_hasPublicInstallName = file->hasAllowableClients() ? false : this->isPublicLocation(file->getInstallName().c_str());
+
+       for (const auto &client : file->allowableClients())
+               this->_allowableClients.emplace_back(strdup(client.c_str()));
 
-       if ( (lib._platform != platform) && (platform != Options::kPlatformUnknown) ) {
+       auto dylibPlatform = mapPlatform(file->getPlatform());
+       if ( (dylibPlatform != platform) && (platform != Options::kPlatformUnknown) ) {
                this->_wrongOS = true;
                if ( this->_addVersionLoadCommand && !indirectDylib ) {
                        if ( buildingForSimulator ) {
                                if ( !this->_allowSimToMacOSXLinking )
                                        throwf("building for %s simulator, but linking against dylib built for %s (%s).",
-                                                       Options::platformName(platform), Options::platformName(lib._platform), path);
+                                                       Options::platformName(platform), Options::platformName(dylibPlatform), path);
                        } else {
                                throwf("building for %s, but linking against dylib built for %s (%s).",
-                                               Options::platformName(platform), Options::platformName(lib._platform), path);
+                                               Options::platformName(platform), Options::platformName(dylibPlatform), path);
                        }
                }
        }
 
-       this->_dependentDylibs.reserve(lib._reexportedLibraries.size());
-       for ( const auto& reexport : lib._reexportedLibraries ) {
-               const char *path = strdup(reexport.str().c_str());
+       for (const auto& reexport : file->reexportedLibraries()) {
+               const char *path = strdup(reexport.c_str());
                if ( (targetInstallPath == nullptr) || (strcmp(targetInstallPath, path) != 0) )
                        this->_dependentDylibs.emplace_back(path, true);
        }
 
-       // build hash table
-       buildExportHashTable(lib);
+       for (const auto& symbol : file->ignoreExports())
+               this->_ignoreExports.insert(strdup(symbol.c_str()));
 
-       munmap((caddr_t)fileContent, fileLength);
+       // if linking flat and this is a flat dylib, create one atom that references all imported symbols.
+       if ( linkingFlatNamespace && linkingMainExecutable && (file->hasTwoLevelNamespace() == false) ) {
+               std::vector<const char*> importNames;
+               importNames.reserve(file->undefineds().size());
+               // We do not need to strdup the name, because that will be done by the
+               // ImportAtom constructor.
+               for (const auto &sym : file->undefineds())
+                       importNames.emplace_back(sym.getName().c_str());
+               this->_importAtom = new generic::dylib::ImportAtom<A>(*this, importNames);
+       }
+
+       // build hash table
+       buildExportHashTable(file.get());
 }
 
 template <typename A>
-void File<A>::buildExportHashTable(const DynamicLibrary& lib) {
+void File<A>::buildExportHashTable(const tapi::LinkerInterfaceFile* file) {
        if (this->_s_logHashtable )
                fprintf(stderr, "ld: building hashtable from text-stub info in %s\n", this->path());
 
-       for (auto &sym : lib._symbols)
-               this->addSymbol(sym.str().c_str());
+       for (const auto &sym : file->exports()) {
+               const char* name = sym.getName().c_str();
+               bool weakDef = sym.isWeakDefined();
+               bool tlv = sym.isThreadLocalValue();
 
-#if SUPPORT_ARCH_i386
-       if (this->_platform == Options::kPlatformOSX && _cpuType == CPU_TYPE_I386) {
-               for (auto &sym : lib._classes)
-                       this->addSymbol((".objc_class_name" + sym.str()).c_str());
-       } else {
-               for (auto &sym : lib._classes) {
-                       this->addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str());
-                       this->addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str());
-               }
+               typename Base::AtomAndWeak bucket = { nullptr, weakDef, tlv, 0 };
+               if ( this->_s_logHashtable )
+                       fprintf(stderr, "  adding %s to hash table for %s\n", name, this->path());
+               this->_atoms[strdup(name)] = bucket;
        }
-#else
-       for (auto &sym : lib._classes) {
-               this->addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str());
-               this->addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str());
-       }
-#endif
-
-       for (auto &sym : lib._ivars)
-               this->addSymbol(("_OBJC_IVAR_$" + sym.str()).c_str());
-
-       for (auto &sym : lib._weakDefSymbols)
-               this->addSymbol(sym.str().c_str(), /*weak=*/true);
-
-       for (auto &sym : lib._tlvSymbols)
-               this->addSymbol(sym.str().c_str(), /*weak=*/false, /*tlv=*/true);
 }
 
 template <typename A>
@@ -673,19 +222,21 @@ class Parser
 public:
        using P = typename A::P;
 
-       static bool                             validFile(const uint8_t* fileContent, uint64_t fileLength,
-                                                                         const std::string &path, const char* archName);
-       static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path,
-                                                                 time_t mTime, ld::File::Ordinal ordinal, const Options& opts,
+       static ld::dylib::File* parse(const char* path, const uint8_t* fileContent,
+                                                                 uint64_t fileLength, time_t mTime,
+                                                                 ld::File::Ordinal ordinal, const Options& opts,
                                                                  bool indirectDylib)
        {
-               return new File<A>(fileContent, fileLength, path, mTime, ordinal,
+               return new File<A>(path, fileContent, fileLength,mTime, ordinal,
                                                   opts.flatNamespace(),
+                                                  opts.linkingMainExecutable(),
                                                   opts.implicitlyLinkIndirectPublicDylibs(),
                                                   opts.platform(),
-                                                  opts.architecture(),
-                                                  opts.architectureName(),
                                                   opts.minOSversion(),
+                                                  opts.allowWeakImports(),
+                                                  opts.architecture(),
+                                                  opts.subArchitecture(),
+                                                  opts.enforceDylibSubtypesMatch(),
                                                   opts.allowSimulatorToLinkWithMacOSX(),
                                                   opts.addVersionLoadCommand(),
                                                   opts.targetIOSSimulator(),
@@ -695,20 +246,6 @@ public:
        }
 };
 
-template <typename A>
-bool Parser<A>::validFile(const uint8_t* fileContent, uint64_t fileLength, const std::string &path,
-                                                 const char* archName)
-{
-       if ( path.find(".tbd", path.size()-4) == std::string::npos )
-               return false;
-
-       TBDFile stub((const char*)fileContent, fileLength);
-       if ( !stub.validForArch(archName) )
-               throwf("missing required architecture %s in file %s", archName, path.c_str());
-
-       return true;
-}
-
 //
 // main function used by linker to instantiate ld::Files
 //
@@ -716,30 +253,27 @@ ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const ch
                                           time_t modTime, const Options& opts, ld::File::Ordinal ordinal,
                                           bool bundleLoader, bool indirectDylib)
 {
+
        switch ( opts.architecture() ) {
 #if SUPPORT_ARCH_x86_64
                case CPU_TYPE_X86_64:
-                       if ( Parser<x86_64>::validFile(fileContent, fileLength, path, opts.architectureName()) )
-                               return Parser<x86_64>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
-                       break;
+               if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
+                       return Parser<x86_64>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
 #endif
 #if SUPPORT_ARCH_i386
                case CPU_TYPE_I386:
-                       if ( Parser<x86>::validFile(fileContent, fileLength, path, opts.architectureName()) )
-                               return Parser<x86>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
-                       break;
+               if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
+                       return Parser<x86>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
 #endif
 #if SUPPORT_ARCH_arm_any
                case CPU_TYPE_ARM:
-                       if ( Parser<arm>::validFile(fileContent, fileLength, path, opts.architectureName()) )
-                               return Parser<arm>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
-                       break;
+               if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
+                       return Parser<arm>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
 #endif
 #if SUPPORT_ARCH_arm64
                case CPU_TYPE_ARM64:
-                       if ( Parser<arm64>::validFile(fileContent, fileLength, path, opts.architectureName()) )
-                               return Parser<arm64>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
-                       break;
+               if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
+                       return Parser<arm64>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
 #endif
        }
        return nullptr;
index dce37836fb3187d496afe3dce909993d2ef731b5..527869a839632191f537f1f2df96c7fe907cb445 100644 (file)
@@ -657,10 +657,17 @@ void BitcodeBundle::doPass()
                 bh->populateMustPreserveSymbols(obfuscator);
                 handlerMap.emplace(std::string(f->path()), bh);
             } else if ( ld::LLVMBitcode* bitcode = dynamic_cast<ld::LLVMBitcode*>(f->getBitcode()) ) {
-                BitcodeHandler* bitcodeHandler = new BitcodeHandler((char*)bitcode->getContent(), bitcode->getSize());
-                bitcodeHandler->populateMustPreserveSymbols(obfuscator);
+                BitcodeHandler bitcodeHandler((char*)bitcode->getContent(), bitcode->getSize());
+                bitcodeHandler.populateMustPreserveSymbols(obfuscator);
             }
         }
+        // add must preserve symbols from lto input.
+        for ( auto &f : _state.ltoBitcodePath ) {
+            BitcodeTempFile ltoTemp(f.c_str(), false); // Keep the temp file because it needs to be read in later in the pass.
+            BitcodeHandler bitcodeHandler((char*)ltoTemp.getContent(), ltoTemp.getSize());
+            bitcodeHandler.populateMustPreserveSymbols(obfuscator);
+        }
+
         // special symbols supplied by linker
         obfuscator->addMustPreserveSymbols("___dso_handle");
         obfuscator->addMustPreserveSymbols("__mh_execute_header");
@@ -762,26 +769,30 @@ void BitcodeBundle::doPass()
         }
     }
 
-    // Write merged LTO bitcode
+    // Write merged LTO bitcode files
     if ( !_state.ltoBitcodePath.empty() ) {
-        xar_file_t ltoFile = NULL;
-        BitcodeTempFile* ltoTemp = new BitcodeTempFile(_state.ltoBitcodePath.c_str(), !_options.saveTempFiles());
-        if ( _options.hideSymbols() ) {
+        int count = 0;
+        for (auto &path : _state.ltoBitcodePath) {
+          std::string xar_name = "lto.o." + std::to_string(count++);
+          xar_file_t ltoFile = NULL;
+          BitcodeTempFile* ltoTemp = new BitcodeTempFile(path.c_str(), !_options.saveTempFiles());
+          if ( _options.hideSymbols() ) {
             ld::Bitcode ltoBitcode(ltoTemp->getContent(), ltoTemp->getSize());
             char ltoTempFile[PATH_MAX];
             sprintf(ltoTempFile, "%s/lto.bc", tempdir);
-            obfuscator->bitcodeHideSymbols(&ltoBitcode, _state.ltoBitcodePath.c_str(), ltoTempFile);
+            obfuscator->bitcodeHideSymbols(&ltoBitcode, path.c_str(), ltoTempFile);
             BitcodeTempFile* ltoStrip = new BitcodeTempFile(ltoTempFile, !_options.saveTempFiles());
-            ltoFile = xar_add_frombuffer(x, NULL, "lto.o", (char*)ltoStrip->getContent(), ltoStrip->getSize());
+            ltoFile = xar_add_frombuffer(x, NULL, xar_name.c_str(), (char*)ltoStrip->getContent(), ltoStrip->getSize());
             delete ltoStrip;
-        } else {
-            ltoFile = xar_add_frombuffer(x, NULL, "lto.o", (char*)ltoTemp->getContent(), ltoTemp->getSize());
+          } else {
+            ltoFile = xar_add_frombuffer(x, NULL, xar_name.c_str(), (char*)ltoTemp->getContent(), ltoTemp->getSize());
+          }
+          if ( ltoFile == NULL )
+            throwf("could not add lto file %s to bitcode bundle", path.c_str());
+          if ( xar_prop_set(ltoFile, "file-type", "LTO") != 0 )
+            throwf("could not set bitcode property for %s in bitcode bundle", path.c_str());
+          delete ltoTemp;
         }
-        if ( ltoFile == NULL )
-            throwf("could not add lto file %s to bitcode bundle", _state.ltoBitcodePath.c_str());
-        if ( xar_prop_set(ltoFile, "file-type", "LTO") != 0 )
-            throwf("could not set bitcode property for %s in bitcode bundle", _state.ltoBitcodePath.c_str());
-        delete ltoTemp;
     }
 
     // Common LinkOptions
index d2847bb98d16a84ab176e96110214f849fe1ae02..8b2afbcc2bcb949a1d61ad6b910bc497a5b875ae 100644 (file)
@@ -303,6 +303,7 @@ bool UnwindInfoAtom<arm64>::encodingMeansUseDwarf(compact_unwind_encoding_t enc)
        return ((enc & UNWIND_ARM64_MODE_MASK) == UNWIND_ARM64_MODE_DWARF);
 }
 
+
 template <>
 bool UnwindInfoAtom<arm>::encodingMeansUseDwarf(compact_unwind_encoding_t enc)
 {
@@ -424,6 +425,7 @@ void UnwindInfoAtom<arm64>::addCompressedAddressOffsetFixup(uint32_t offset, con
        _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndianLow24of32));
 }
 
+
 template <>
 void UnwindInfoAtom<arm>::addCompressedAddressOffsetFixup(uint32_t offset, const ld::Atom* func, const ld::Atom* fromFunc)
 {
@@ -461,6 +463,7 @@ void UnwindInfoAtom<arm64>::addCompressedEncodingFixup(uint32_t offset, const ld
        _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32));
 }
 
+
 template <>
 void UnwindInfoAtom<arm>::addCompressedEncodingFixup(uint32_t offset, const ld::Atom* fde)
 {
@@ -489,6 +492,7 @@ void UnwindInfoAtom<arm64>::addRegularAddressFixup(uint32_t offset, const ld::At
        _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32));
 }
 
+
 template <>
 void UnwindInfoAtom<arm>::addRegularAddressFixup(uint32_t offset, const ld::Atom* func)
 {
@@ -517,6 +521,7 @@ void UnwindInfoAtom<arm64>::addRegularFDEOffsetFixup(uint32_t offset, const ld::
        _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32));
 }
 
+
 template <>
 void UnwindInfoAtom<arm>::addRegularFDEOffsetFixup(uint32_t offset, const ld::Atom* fde)
 {
@@ -545,6 +550,7 @@ void UnwindInfoAtom<arm64>::addImageOffsetFixup(uint32_t offset, const ld::Atom*
        _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32));
 }
 
+
 template <>
 void UnwindInfoAtom<arm>::addImageOffsetFixup(uint32_t offset, const ld::Atom* targ)
 {
@@ -576,6 +582,7 @@ void UnwindInfoAtom<arm64>::addImageOffsetFixupPlusAddend(uint32_t offset, const
        _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndian32));
 }
 
+
 template <>
 void UnwindInfoAtom<arm>::addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend)
 {
index f77f5cd6f50753b00fd9ff6b1d3778a442cab146..7edc85ccc1beb6eb3b348ebdbc4aa2bfde3716d2 100644 (file)
@@ -94,9 +94,11 @@ void doPass(const Options& opts, ld::Internal& state)
                                                targetIsWeakImport = fit->weakImport;
                                                break;
                     default:
-                        break;   
+                        break;
                                }
                                if ( (target != NULL) && (target->definition() == ld::Atom::definitionProxy) ) {
+                                       if ( targetIsWeakImport && !opts.allowWeakImports() )
+                                               throwf("weak import of symbol '%s' not supported because of option: -no_weak_imports", target->name());
                                        ld::Atom::WeakImportState curWI = target->weakImportState();
                                        if ( curWI == ld::Atom::weakImportUnset ) {
                                                // first use of this proxy, set weak-import based on this usage
index 693976cef69b27c15a3b60e6944b62cf3d5a75d7..3797a23d6c95f9cb9e5e66b197ce705a09a8b17b 100644 (file)
@@ -47,10 +47,53 @@ public:
        }
 };
 
+class DataPadAtom : public ld::Atom {
+public:
+                                                                                       DataPadAtom(ld::Internal& state)
+                                                                                       : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+                                                                                                               ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified,
+                                                                                                               symbolTableNotIn, false, false, false, ld::Atom::Alignment(3))
+                                                                                       { state.addAtom(*this); }
+
+
+       virtual const ld::File*                                 file() const                                    { return NULL; }
+       virtual const char*                                             name() const                                    { return "padding"; }
+       virtual uint64_t                                                size() const                                    { return 8; }
+       virtual uint64_t                                                objectAddress() const                   { return 0; }
+       virtual void                                                    copyRawContent(uint8_t buffer[]) const { }
+
+protected:
+       virtual                                                                 ~DataPadAtom() {}
+
+       static ld::Section                                              _s_section;
+};
+
+ld::Section DataPadAtom::_s_section("__DATA", "__data", ld::Section::typeUnclassified);
+
+
 void doPass(const Options& opts, ld::Internal& state)
 {
        const bool log = false;
-       
+
+       // <rdar://problem/26015603> add __data section if __DATA segment was gutted by dirty data removal
+       if ( (opts.outputKind() == Options::kDynamicLibrary) && opts.useDataConstSegment() && opts.hasDataSymbolMoves() ) {
+               uint64_t dataAtomsSize = 0;
+               bool foundSegmentDATA_DIRTY = false;
+               for (ld::Internal::FinalSection* sect : state.sections) {
+                       if ( strcmp(sect->segmentName(), "__DATA") == 0 ) {
+                               for (const ld::Atom* atom : sect->atoms) {
+                                       dataAtomsSize += atom->size();
+                               }
+                       }
+                       else if ( strcmp(sect->segmentName(), "__DATA_DIRTY") == 0 ) {
+                               foundSegmentDATA_DIRTY = true;
+                       }
+               }
+               if ( foundSegmentDATA_DIRTY && (dataAtomsSize == 0) ) {
+                       new DataPadAtom(state);
+               }
+       }
+
        // only make make __huge section in final linked images
        if ( opts.outputKind() == Options::kObjectFile )
                return;
index c65ca2122282e5cc2abf7c5e82a99bd1a882ff0f..e2afca1e2187d11a42ecc3981fc1c1408dc2a710 100644 (file)
@@ -50,11 +50,12 @@ struct objc_image_info  {
        uint32_t        flags;
 };
 
-#define OBJC_IMAGE_SUPPORTS_GC                 (1<<1)
-#define OBJC_IMAGE_REQUIRES_GC                 (1<<2)
-#define OBJC_IMAGE_OPTIMIZED_BY_DYLD   (1<<3)
-#define OBJC_IMAGE_SUPPORTS_COMPACTION (1<<4)
-#define OBJC_IMAGE_IS_SIMULATED                        (1<<5)
+#define OBJC_IMAGE_SUPPORTS_GC                                         (1<<1)
+#define OBJC_IMAGE_REQUIRES_GC                                         (1<<2)
+#define OBJC_IMAGE_OPTIMIZED_BY_DYLD                           (1<<3)
+#define OBJC_IMAGE_SUPPORTS_COMPACTION                         (1<<4)
+#define OBJC_IMAGE_IS_SIMULATED                                                (1<<5)
+#define OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES       (1<<6)
 
 
 
@@ -65,7 +66,7 @@ template <typename A>
 class ObjCImageInfoAtom : public ld::Atom {
 public:
                                                                                        ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, 
-                                                                                                                       bool compaction, bool abi2, uint8_t swiftVersion);
+                                                                                                                         bool compaction, bool abi2, bool hasCategoryClassProperties, uint8_t swiftVersion);
 
        virtual const ld::File*                                 file() const                                    { return NULL; }
        virtual const char*                                             name() const                                    { return "objc image info"; }
@@ -89,7 +90,7 @@ template <typename A> ld::Section ObjCImageInfoAtom<A>::_s_sectionABI2("__DATA",
 
 template <typename A>
 ObjCImageInfoAtom<A>::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, bool compaction, 
-                                                                               bool abi2, uint8_t swiftVersion)
+                                                                               bool abi2, bool hasCategoryClassProperties, uint8_t swiftVersion)
        : ld::Atom(abi2 ? _s_sectionABI2 : _s_sectionABI1, ld::Atom::definitionRegular, ld::Atom::combineNever,
                                                        ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, 
                                                        symbolTableNotIn, false, false, false, ld::Atom::Alignment(2))
@@ -117,6 +118,10 @@ ObjCImageInfoAtom<A>::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint,
                        break;
        }
 
+       if ( hasCategoryClassProperties ) {
+               value |= OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES;
+       }
+
        // provide swift language version in final binary for runtime to inspect
        value |= (swiftVersion << 8);
 
@@ -206,9 +211,12 @@ ld::Section ProtocolListAtom<A>::_s_section("__DATA", "__objc_const", ld::Sectio
 template <typename A>
 class PropertyListAtom : public ld::Atom {
 public:
+       enum class PropertyKind { ClassProperties, InstanceProperties };
+
                                                                                        PropertyListAtom(ld::Internal& state, const ld::Atom* baseProtocolList, 
-                                                                                                                       const std::vector<const ld::Atom*>* categories, 
-                                                                                                                       std::set<const ld::Atom*>& deadAtoms);
+                                                                                                        const std::vector<const ld::Atom*>* categories, 
+                                                                                                        std::set<const ld::Atom*>& deadAtoms, 
+                                                                                                        PropertyKind kind);
 
        virtual const ld::File*                                 file() const                                    { return _file; }
        virtual const char*                                             name() const                                    { return "objc merged property list"; }
@@ -273,6 +281,8 @@ public:
 private:       
        typedef typename A::P::uint_t                   pint_t;
 
+       void addFixupAtOffset(uint32_t offset);
+
        const ld::Atom*                                                 _atom;
        std::vector<ld::Fixup>                                  _fixups;
 };
@@ -314,7 +324,7 @@ const ld::Atom* ObjCData<A>::getPointerInContent(ld::Internal& state, const ld::
        if ( hasAddend != NULL )
                *hasAddend = false;
        for (ld::Fixup::iterator fit=contentAtom->fixupsBegin(); fit != contentAtom->fixupsEnd(); ++fit) {
-               if ( fit->offsetInAtom == offset ) {
+               if ( (fit->offsetInAtom == offset) && (fit->kind != ld::Fixup::kindNoneFollowOn) ) {
                        switch ( fit->binding ) {
                                case ld::Fixup::bindingsIndirectlyBound:
                                        target = state.indirectBindingTable[fit->u.bindingIndex];
@@ -369,7 +379,8 @@ public:
        static const ld::Atom*  getInstanceMethods(ld::Internal& state, const ld::Atom* contentAtom);
        static const ld::Atom*  getClassMethods(ld::Internal& state, const ld::Atom* contentAtom);
        static const ld::Atom*  getProtocols(ld::Internal& state, const ld::Atom* contentAtom);
-       static const ld::Atom*  getProperties(ld::Internal& state, const ld::Atom* contentAtom);
+       static const ld::Atom*  getInstanceProperties(ld::Internal& state, const ld::Atom* contentAtom);
+       static const ld::Atom*  getClassProperties(ld::Internal& state, const ld::Atom* contentAtom);
        static uint32_t         size() { return 6*sizeof(pint_t); }
 private:       
        typedef typename A::P::uint_t                   pint_t;
@@ -401,11 +412,20 @@ const ld::Atom*   Category<A>::getProtocols(ld::Internal& state, const ld::Atom* c
 }
 
 template <typename A>
-const ld::Atom*        Category<A>::getProperties(ld::Internal& state, const ld::Atom* contentAtom)
+const ld::Atom*        Category<A>::getInstanceProperties(ld::Internal& state, const ld::Atom* contentAtom)
 {
        return ObjCData<A>::getPointerInContent(state, contentAtom, 5*sizeof(pint_t)); // category_t.instanceProperties
 }
 
+template <typename A>
+const ld::Atom*        Category<A>::getClassProperties(ld::Internal& state, const ld::Atom* contentAtom)
+{
+       // Only specially-marked files have this field.
+       if (!contentAtom->file()->objcHasCategoryClassPropertiesField()) return NULL;
+
+       return ObjCData<A>::getPointerInContent(state, contentAtom, 6*sizeof(pint_t)); // category_t.classProperties
+}
+
 
 template <typename A>
 class MethodList : public ObjCData<A> {
@@ -446,10 +466,12 @@ private:
 template <typename A>
 class Class : public ObjCData<A> {
 public:
+       static const ld::Atom*  getMetaClass(ld::Internal& state, const ld::Atom* classAtom);
        static const ld::Atom*  getInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom);
        static const ld::Atom*  getInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom);
        static const ld::Atom*  getInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom);
        static const ld::Atom*  getClassMethodList(ld::Internal& state, const ld::Atom* classAtom);
+       static const ld::Atom*  getClassPropertyList(ld::Internal& state, const ld::Atom* classAtom);
        static const ld::Atom*  setInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom, 
                                                                                                const ld::Atom* methodListAtom, std::set<const ld::Atom*>& deadAtoms);
        static const ld::Atom*  setInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom, 
@@ -460,74 +482,117 @@ public:
                                                                                                const ld::Atom* methodListAtom, std::set<const ld::Atom*>& deadAtoms);
        static const ld::Atom*  setClassProtocolList(ld::Internal& state, const ld::Atom* classAtom, 
                                                                                                const ld::Atom* protocolListAtom, std::set<const ld::Atom*>& deadAtoms);
-       static uint32_t         size() { return 5*sizeof(pint_t); }
-       static unsigned int             class_ro_header_size();
+       static const ld::Atom*  setClassPropertyList(ld::Internal& state, const ld::Atom* classAtom, 
+                                                                                               const ld::Atom* propertyListAtom, std::set<const ld::Atom*>& deadAtoms);
+       static uint32_t         size() { return sizeof(Content); }
+
 private:
+       friend class ClassROOverlayAtom<A>;
+
        typedef typename A::P::uint_t                   pint_t;
+
        static const ld::Atom*  getROData(ld::Internal& state, const ld::Atom* classAtom);
+
+       struct Content {
+               pint_t isa;
+               pint_t superclass;
+               pint_t method_cache;
+               pint_t vtable;
+               pint_t data;
+       };
+
+       struct ROContent {
+               uint32_t flags;
+               uint32_t instanceStart;
+               // Note there is 4-bytes of alignment padding between instanceSize 
+               // and ivarLayout on 64-bit archs, but no padding on 32-bit archs.
+               // This union is a way to model that.
+               union {
+                       uint32_t instanceSize;
+                       pint_t pad;
+               } instanceSize;
+               pint_t ivarLayout;
+               pint_t name;
+               pint_t baseMethods;
+               pint_t baseProtocols;
+               pint_t ivars;
+               pint_t weakIvarLayout;
+               pint_t baseProperties;
+       };
 };
 
-template <> unsigned int Class<x86_64>::class_ro_header_size() { return 16; }
-template <> unsigned int Class<arm>::class_ro_header_size() { return 12;}
-template <> unsigned int Class<x86>::class_ro_header_size() { return 12; }
+#define GET_FIELD(state, classAtom, field) \
+       ObjCData<A>::getPointerInContent(state, classAtom, offsetof(Content, field))
+#define SET_FIELD(state, classAtom, field, valueAtom) \
+       ObjCData<A>::setPointerInContent(state, classAtom, offsetof(Content, field), valueAtom)
 
+#define GET_RO_FIELD(state, classAtom, field) \
+       ObjCData<A>::getPointerInContent(state, getROData(state, classAtom), offsetof(ROContent, field))
+#define SET_RO_FIELD(state, classROAtom, field, valueAtom) \
+       ObjCData<A>::setPointerInContent(state, getROData(state, classAtom), offsetof(ROContent, field), valueAtom)
 
 template <typename A>
-const ld::Atom*        Class<A>::getROData(ld::Internal& state, const ld::Atom* classAtom)
+const ld::Atom*        Class<A>::getMetaClass(ld::Internal& state, const ld::Atom* classAtom)
 {
-       return ObjCData<A>::getPointerInContent(state, classAtom, 4*sizeof(pint_t)); // class_t.data
+    const ld::Atom* metaClassAtom = GET_FIELD(state, classAtom, isa);
+    assert(metaClassAtom != NULL);
+    return metaClassAtom;
+}
 
+template <typename A>
+const ld::Atom*        Class<A>::getROData(ld::Internal& state, const ld::Atom* classAtom)
+{
+    const ld::Atom* classROAtom = GET_FIELD(state, classAtom, data);
+    assert(classROAtom != NULL);
+    return classROAtom;
 }
 
 template <typename A>
 const ld::Atom*        Class<A>::getInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom)
 {
-       const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data
-       assert(classROAtom != NULL);
-       return ObjCData<A>::getPointerInContent(state, classROAtom, class_ro_header_size() + 2*sizeof(pint_t)); // class_ro_t.baseMethods
+       return GET_RO_FIELD(state, classAtom, baseMethods);
 }
 
 template <typename A>
 const ld::Atom*        Class<A>::getInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom)
 {
-       const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data
-       assert(classROAtom != NULL);
-       return ObjCData<A>::getPointerInContent(state, classROAtom, class_ro_header_size() + 3*sizeof(pint_t)); // class_ro_t.baseProtocols
+       return GET_RO_FIELD(state, classAtom, baseProtocols);
 }
 
 template <typename A>
 const ld::Atom*        Class<A>::getInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom)
 {
-       const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data
-       assert(classROAtom != NULL);
-       return ObjCData<A>::getPointerInContent(state, classROAtom, class_ro_header_size() + 6*sizeof(pint_t)); // class_ro_t.baseProperties
+       return GET_RO_FIELD(state, classAtom, baseProperties);
 }
 
 template <typename A>
 const ld::Atom*        Class<A>::getClassMethodList(ld::Internal& state, const ld::Atom* classAtom)
 {
-       const ld::Atom* metaClassAtom = ObjCData<A>::getPointerInContent(state, classAtom, 0); // class_t.isa
-       assert(metaClassAtom != NULL);
-       return Class<A>::getInstanceMethodList(state, metaClassAtom);
+       return Class<A>::getInstanceMethodList(state, getMetaClass(state, classAtom));
+}
+
+template <typename A>
+const ld::Atom*        Class<A>::getClassPropertyList(ld::Internal& state, const ld::Atom* classAtom)
+{
+    return Class<A>::getInstancePropertyList(state, getMetaClass(state, classAtom));
 }
 
 template <typename A>
 const ld::Atom* Class<A>::setInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom, 
                                                                                                const ld::Atom* methodListAtom, std::set<const ld::Atom*>& deadAtoms)
 {
-       const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data
-       assert(classROAtom != NULL);
        // if the base class does not already have a method list, we need to create an overlay
        if ( getInstanceMethodList(state, classAtom) == NULL ) {
-               ClassROOverlayAtom<A>* overlay = new ClassROOverlayAtom<A>(classROAtom);
+               const ld::Atom* oldROAtom = getROData(state, classAtom);
+               deadAtoms.insert(oldROAtom);
+               ClassROOverlayAtom<A>* overlay = new ClassROOverlayAtom<A>(oldROAtom);
                //fprintf(stderr, "replace class RO atom %p with %p for method list in class atom %s\n", classROAtom, overlay, classAtom->name());
                overlay->addMethodListFixup();
-               ObjCData<A>::setPointerInContent(state, classAtom, 4*sizeof(pint_t), overlay); // class_t.data
-               deadAtoms.insert(classROAtom);
-               ObjCData<A>::setPointerInContent(state, overlay, class_ro_header_size() + 2*sizeof(pint_t), methodListAtom); // class_ro_t.baseMethods
+               SET_FIELD(state, classAtom, data, overlay);
+               SET_RO_FIELD(state, classAtom, baseMethods, methodListAtom);
                return overlay;
        }
-       ObjCData<A>::setPointerInContent(state, classROAtom, class_ro_header_size() + 2*sizeof(pint_t), methodListAtom); // class_ro_t.baseMethods
+       SET_RO_FIELD(state, classAtom, baseMethods, methodListAtom);
        return NULL; // means classRO atom was not replaced
 }
 
@@ -535,20 +600,19 @@ template <typename A>
 const ld::Atom* Class<A>::setInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom, 
                                                                        const ld::Atom* protocolListAtom, std::set<const ld::Atom*>& deadAtoms)
 {
-       const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data
-       assert(classROAtom != NULL);
        // if the base class does not already have a protocol list, we need to create an overlay
        if ( getInstanceProtocolList(state, classAtom) == NULL ) {
-               ClassROOverlayAtom<A>* overlay = new ClassROOverlayAtom<A>(classROAtom);
+               const ld::Atom* oldROAtom = getROData(state, classAtom);
+               deadAtoms.insert(oldROAtom);
+               ClassROOverlayAtom<A>* overlay = new ClassROOverlayAtom<A>(oldROAtom);
                //fprintf(stderr, "replace class RO atom %p with %p for protocol list in class atom %s\n", classROAtom, overlay, classAtom->name());
                overlay->addProtocolListFixup();
-               ObjCData<A>::setPointerInContent(state, classAtom, 4*sizeof(pint_t), overlay); // class_t.data
-               deadAtoms.insert(classROAtom);
-               ObjCData<A>::setPointerInContent(state, overlay, class_ro_header_size() + 3*sizeof(pint_t), protocolListAtom); // class_ro_t.baseProtocols
+               SET_FIELD(state, classAtom, data, overlay);
+               SET_RO_FIELD(state, classAtom, baseProtocols, protocolListAtom);
                return overlay;
        }
        //fprintf(stderr, "set class RO atom %p protocol list in class atom %s\n", classROAtom, classAtom->name());
-       ObjCData<A>::setPointerInContent(state, classROAtom, class_ro_header_size() + 3*sizeof(pint_t), protocolListAtom); // class_ro_t.baseProtocols
+       SET_RO_FIELD(state, classAtom, baseProtocols, protocolListAtom);
        return NULL;  // means classRO atom was not replaced
 }
 
@@ -557,9 +621,8 @@ const ld::Atom* Class<A>::setClassProtocolList(ld::Internal& state, const ld::At
                                                                        const ld::Atom* protocolListAtom, std::set<const ld::Atom*>& deadAtoms)
 {
        // meta class also points to same protocol list as class
-       const ld::Atom* metaClassAtom = ObjCData<A>::getPointerInContent(state, classAtom, 0); // class_t.isa
+       const ld::Atom* metaClassAtom = getMetaClass(state, classAtom);
        //fprintf(stderr, "setClassProtocolList(), classAtom=%p %s, metaClass=%p %s\n", classAtom, classAtom->name(), metaClassAtom, metaClassAtom->name());
-       assert(metaClassAtom != NULL);
        return setInstanceProtocolList(state, metaClassAtom, protocolListAtom, deadAtoms);
 }
 
@@ -569,19 +632,18 @@ template <typename A>
 const ld::Atom*  Class<A>::setInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom, 
                                                                                                const ld::Atom* propertyListAtom, std::set<const ld::Atom*>& deadAtoms)
 {
-       const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data
-       assert(classROAtom != NULL);
        // if the base class does not already have a property list, we need to create an overlay
        if ( getInstancePropertyList(state, classAtom) == NULL ) {
-               ClassROOverlayAtom<A>* overlay = new ClassROOverlayAtom<A>(classROAtom);
+               const ld::Atom* oldROAtom = getROData(state, classAtom);
+               deadAtoms.insert(oldROAtom);
+               ClassROOverlayAtom<A>* overlay = new ClassROOverlayAtom<A>(oldROAtom);
                //fprintf(stderr, "replace class RO atom %p with %p for property list in class atom %s\n", classROAtom, overlay, classAtom->name());
                overlay->addPropertyListFixup();
-               ObjCData<A>::setPointerInContent(state, classAtom, 4*sizeof(pint_t), overlay); // class_t.data
-               deadAtoms.insert(classROAtom);
-               ObjCData<A>::setPointerInContent(state, overlay, class_ro_header_size() + 6*sizeof(pint_t), propertyListAtom); // class_ro_t.baseProperties
+               SET_FIELD(state, classAtom, data, overlay);
+               SET_RO_FIELD(state, classAtom, baseProperties, propertyListAtom);
                return overlay;
        }
-       ObjCData<A>::setPointerInContent(state, classROAtom, class_ro_header_size() + 6*sizeof(pint_t), propertyListAtom); // class_ro_t.baseProperties
+       SET_RO_FIELD(state, classAtom, baseProperties, propertyListAtom);
        return NULL;  // means classRO atom was not replaced
 }
 
@@ -590,86 +652,67 @@ const ld::Atom* Class<A>::setClassMethodList(ld::Internal& state, const ld::Atom
                                                                                        const ld::Atom* methodListAtom, std::set<const ld::Atom*>& deadAtoms)
 {
        // class methods is just instance methods of metaClass
-       const ld::Atom* metaClassAtom = ObjCData<A>::getPointerInContent(state, classAtom, 0); // class_t.isa
-       assert(metaClassAtom != NULL);
-       return setInstanceMethodList(state, metaClassAtom, methodListAtom, deadAtoms);
+       return setInstanceMethodList(state, getMetaClass(state, classAtom), methodListAtom, deadAtoms);
 }
 
-
-
-template <>
-void ClassROOverlayAtom<x86_64>::addMethodListFixup()
-{
-       const ld::Atom* targetAtom = this; // temporary
-       uint32_t offset = Class<x86_64>::class_ro_header_size() + 2*8; // class_ro_t.baseMethods
-       _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, targetAtom));
-}
-
-template <>
-void ClassROOverlayAtom<arm>::addMethodListFixup()
+template <typename A>
+const ld::Atom* Class<A>::setClassPropertyList(ld::Internal& state, const ld::Atom* classAtom, 
+                                                                                       const ld::Atom* propertyListAtom, std::set<const ld::Atom*>& deadAtoms)
 {
-       const ld::Atom* targetAtom = this; // temporary
-       uint32_t offset = Class<arm>::class_ro_header_size() + 2*4; // class_ro_t.baseMethods
-       _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom));
+       // class properties is just instance properties of metaClass
+       return setInstancePropertyList(state, getMetaClass(state, classAtom), propertyListAtom, deadAtoms);
 }
 
-template <>
-void ClassROOverlayAtom<x86>::addMethodListFixup()
-{
-       const ld::Atom* targetAtom = this; // temporary
-       uint32_t offset = Class<x86>::class_ro_header_size() + 2*4; // class_ro_t.baseMethods
-       _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom));
-}
+#undef GET_FIELD
+#undef SET_FIELD
+#undef GET_RO_FIELD
+#undef SET_RO_FIELD
 
 
+template <typename P>
+ld::Fixup::Kind pointerFixupKind();
 
-template <>
-void ClassROOverlayAtom<x86_64>::addProtocolListFixup()
-{
-       const ld::Atom* targetAtom = this; // temporary
-       uint32_t offset = Class<x86_64>::class_ro_header_size() + 3*8; // class_ro_t.baseProtocols
-       _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, targetAtom));
+template <> 
+ld::Fixup::Kind pointerFixupKind<Pointer32<BigEndian>>() { 
+       return ld::Fixup::kindStoreTargetAddressBigEndian32; 
 }
-
-template <>
-void ClassROOverlayAtom<arm>::addProtocolListFixup()
-{
-       const ld::Atom* targetAtom = this; // temporary
-       uint32_t offset = Class<arm>::class_ro_header_size() + 3*4; // class_ro_t.baseProtocols
-       _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom));
+template <> 
+ld::Fixup::Kind pointerFixupKind<Pointer64<BigEndian>>() { 
+       return ld::Fixup::kindStoreTargetAddressBigEndian64; 
+}
+template <> 
+ld::Fixup::Kind pointerFixupKind<Pointer32<LittleEndian>>() { 
+       return ld::Fixup::kindStoreTargetAddressLittleEndian32; 
+}
+template <> 
+ld::Fixup::Kind pointerFixupKind<Pointer64<LittleEndian>>() { 
+       return ld::Fixup::kindStoreTargetAddressLittleEndian64; 
 }
 
-template <>
-void ClassROOverlayAtom<x86>::addProtocolListFixup()
+template <typename A>
+void ClassROOverlayAtom<A>::addFixupAtOffset(uint32_t offset)
 {
        const ld::Atom* targetAtom = this; // temporary
-       uint32_t offset = Class<x86>::class_ro_header_size() + 3*4; // class_ro_t.baseProtocols
-       _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom));
+       _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, pointerFixupKind<typename A::P>(), targetAtom));
 }
 
 
-template <>
-void ClassROOverlayAtom<x86_64>::addPropertyListFixup()
+template <typename A>
+void ClassROOverlayAtom<A>::addMethodListFixup()
 {
-       const ld::Atom* targetAtom = this; // temporary
-       uint32_t offset = Class<x86_64>::class_ro_header_size() + 6*8; // class_ro_t.baseProperties
-       _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, targetAtom));
+       addFixupAtOffset(offsetof(typename Class<A>::ROContent, baseMethods));
 }
 
-template <>
-void ClassROOverlayAtom<arm>::addPropertyListFixup()
+template <typename A>
+void ClassROOverlayAtom<A>::addProtocolListFixup()
 {
-       const ld::Atom* targetAtom = this; // temporary
-       uint32_t offset = Class<arm>::class_ro_header_size() + 6*4; // class_ro_t.baseProperties
-       _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom));
+       addFixupAtOffset(offsetof(typename Class<A>::ROContent, baseProtocols));
 }
 
-template <>
-void ClassROOverlayAtom<x86>::addPropertyListFixup()
+template <typename A>
+void ClassROOverlayAtom<A>::addPropertyListFixup()
 {
-       const ld::Atom* targetAtom = this; // temporary
-       uint32_t offset = Class<x86>::class_ro_header_size() + 6*4; // class_ro_t.baseProperties
-       _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom));
+       addFixupAtOffset(offsetof(typename Class<A>::ROContent, baseProperties));
 }
 
 
@@ -685,8 +728,8 @@ public:
        static bool                             hasInstanceMethods(ld::Internal& state, const std::vector<const ld::Atom*>* categories);
        static bool                             hasClassMethods(ld::Internal& state, const std::vector<const ld::Atom*>* categories);
        static bool                             hasProtocols(ld::Internal& state, const std::vector<const ld::Atom*>* categories);
-       static bool                             hasProperties(ld::Internal& state, const std::vector<const ld::Atom*>* categories);
-       
+       static bool                             hasInstanceProperties(ld::Internal& state, const std::vector<const ld::Atom*>* categories);
+       static bool                             hasClassProperties(ld::Internal& state, const std::vector<const ld::Atom*>* categories);
        
        static unsigned int             class_ro_baseMethods_offset();
 private:
@@ -741,11 +784,26 @@ bool OptimizeCategories<A>::hasProtocols(ld::Internal& state, const std::vector<
 
 
 template <typename A>
-bool OptimizeCategories<A>::hasProperties(ld::Internal& state, const std::vector<const ld::Atom*>* categories)
+bool OptimizeCategories<A>::hasInstanceProperties(ld::Internal& state, const std::vector<const ld::Atom*>* categories)
 {
        for (std::vector<const ld::Atom*>::const_iterator it=categories->begin(); it != categories->end(); ++it) {
                const ld::Atom* categoryAtom = *it;
-               const ld::Atom* propertyListAtom = Category<A>::getProperties(state, categoryAtom);
+               const ld::Atom* propertyListAtom = Category<A>::getInstanceProperties(state, categoryAtom);
+               if ( propertyListAtom != NULL ) {
+                       if ( PropertyList<A>::count(state, propertyListAtom) > 0 )
+                               return true;
+               }
+       }
+       return false;
+}
+
+
+template <typename A>
+bool OptimizeCategories<A>::hasClassProperties(ld::Internal& state, const std::vector<const ld::Atom*>* categories)
+{
+       for (std::vector<const ld::Atom*>::const_iterator it=categories->begin(); it != categories->end(); ++it) {
+               const ld::Atom* categoryAtom = *it;
+               const ld::Atom* propertyListAtom = Category<A>::getClassProperties(state, categoryAtom);
                if ( propertyListAtom != NULL ) {
                        if ( PropertyList<A>::count(state, propertyListAtom) > 0 )
                                return true;
@@ -813,6 +871,7 @@ private:
                std::sort(atoms.begin(), atoms.end(), AtomSorter());
        }
 
+
 template <typename A>
 void OptimizeCategories<A>::doit(const Options& opts, ld::Internal& state)
 {
@@ -941,10 +1000,10 @@ void OptimizeCategories<A>::doit(const Options& opts, ld::Internal& state)
                                        state.atomToSection[newMetaClassRO] = methodListSection;
                                }
                        }
-                       // if any category adds properties, generate new merged property list, and replace
-                       if ( OptimizeCategories<A>::hasProperties(state, categories) ) { 
+                       // if any category adds instance properties, generate new merged property list, and replace
+                       if ( OptimizeCategories<A>::hasInstanceProperties(state, categories) ) { 
                                const ld::Atom* basePropertyListAtom = Class<A>::getInstancePropertyList(state, classAtom); 
-                               const ld::Atom* newPropertyListAtom = new PropertyListAtom<A>(state, basePropertyListAtom, categories, deadAtoms);
+                               const ld::Atom* newPropertyListAtom = new PropertyListAtom<A>(state, basePropertyListAtom, categories, deadAtoms, PropertyListAtom<A>::PropertyKind::InstanceProperties);
                                const ld::Atom* newClassRO = Class<A>::setInstancePropertyList(state, classAtom, newPropertyListAtom, deadAtoms);
                                // add new property list to final sections
                                methodListSection->atoms.push_back(newPropertyListAtom);
@@ -955,7 +1014,20 @@ void OptimizeCategories<A>::doit(const Options& opts, ld::Internal& state)
                                        state.atomToSection[newClassRO] = methodListSection;
                                }
                        }
-                
+                       // if any category adds class properties, generate new merged property list, and replace
+                       if ( OptimizeCategories<A>::hasClassProperties(state, categories) ) { 
+                               const ld::Atom* basePropertyListAtom = Class<A>::getClassPropertyList(state, classAtom); 
+                               const ld::Atom* newPropertyListAtom = new PropertyListAtom<A>(state, basePropertyListAtom, categories, deadAtoms, PropertyListAtom<A>::PropertyKind::ClassProperties);
+                               const ld::Atom* newClassRO = Class<A>::setClassPropertyList(state, classAtom, newPropertyListAtom, deadAtoms);
+                               // add new property list to final sections
+                               methodListSection->atoms.push_back(newPropertyListAtom);
+                               state.atomToSection[newPropertyListAtom] = methodListSection;
+                               if ( newClassRO != NULL ) {
+                                       assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0);
+                                       methodListSection->atoms.push_back(newClassRO);
+                                       state.atomToSection[newClassRO] = methodListSection;
+                               }
+                       }                
                }
 
                // remove dead atoms
@@ -967,6 +1039,7 @@ void OptimizeCategories<A>::doit(const Options& opts, ld::Internal& state)
 }
 
 
+
 template <typename A> 
 MethodListAtom<A>::MethodListAtom(ld::Internal& state, const ld::Atom* baseMethodList, bool meta, 
                                                                        const std::vector<const ld::Atom*>* categories, std::set<const ld::Atom*>& deadAtoms)
@@ -1120,7 +1193,7 @@ ProtocolListAtom<A>::ProtocolListAtom(ld::Internal& state, const ld::Atom* baseP
 
 template <typename A> 
 PropertyListAtom<A>::PropertyListAtom(ld::Internal& state, const ld::Atom* basePropertyList, 
-                                                                       const std::vector<const ld::Atom*>* categories, std::set<const ld::Atom*>& deadAtoms)
+                                     const std::vector<const ld::Atom*>* categories, std::set<const ld::Atom*>& deadAtoms, PropertyKind kind)
   : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
                        ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, 
                        symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _file(NULL), _propertyCount(0) 
@@ -1135,7 +1208,7 @@ PropertyListAtom<A>::PropertyListAtom(ld::Internal& state, const ld::Atom* baseP
                fixupCount = basePropertyList->fixupsEnd() - basePropertyList->fixupsBegin();
        }
        for (std::vector<const ld::Atom*>::const_iterator ait=categories->begin(); ait != categories->end(); ++ait) {
-               const ld::Atom* categoryPropertyListAtom = Category<A>::getProperties(state, *ait);
+               const ld::Atom* categoryPropertyListAtom = kind == PropertyKind::ClassProperties ? Category<A>::getClassProperties(state, *ait) : Category<A>::getInstanceProperties(state, *ait);
                if ( categoryPropertyListAtom != NULL ) {
                        _propertyCount += PropertyList<A>::count(state, categoryPropertyListAtom);
                        fixupCount += (categoryPropertyListAtom->fixupsEnd() - categoryPropertyListAtom->fixupsBegin());
@@ -1152,7 +1225,7 @@ PropertyListAtom<A>::PropertyListAtom(ld::Internal& state, const ld::Atom* baseP
        _fixups.reserve(fixupCount);
        uint32_t slide = 0;
        for (std::vector<const ld::Atom*>::const_iterator it=categories->begin(); it != categories->end(); ++it) {
-               const ld::Atom* categoryPropertyListAtom = Category<A>::getProperties(state, *it);
+               const ld::Atom* categoryPropertyListAtom = kind == PropertyKind::ClassProperties ? Category<A>::getClassProperties(state, *it) : Category<A>::getInstanceProperties(state, *it);
                if ( categoryPropertyListAtom != NULL ) {
                        for (ld::Fixup::iterator fit=categoryPropertyListAtom->fixupsBegin(); fit != categoryPropertyListAtom->fixupsEnd(); ++fit) {
                                ld::Fixup fixup = *fit;
@@ -1179,85 +1252,112 @@ PropertyListAtom<A>::PropertyListAtom(ld::Internal& state, const ld::Atom* baseP
 }
 
 
+template <typename A>
+void scanCategories(ld::Internal& state, 
+                                       bool& haveCategoriesWithNonNullClassProperties, 
+                                       bool& haveCategoriesWithoutClassPropertyStorage)
+{
+       haveCategoriesWithNonNullClassProperties = false;
+       haveCategoriesWithoutClassPropertyStorage = false;
+       
+       for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
+               ld::Internal::FinalSection* sect = *sit;
+               if ( sect->type() == ld::Section::typeObjC2CategoryList ) {
+                       for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+                               const ld::Atom* categoryListElementAtom = *ait;
+                               bool hasAddend;
+                               const ld::Atom* categoryAtom = ObjCData<A>::getPointerInContent(state, categoryListElementAtom, 0, &hasAddend);
+                               
+                               if (Category<A>::getClassProperties(state, categoryAtom)) {
+                                       haveCategoriesWithNonNullClassProperties = true;
+                                       // fprintf(stderr, "category in file %s has non-null class properties\n", categoryAtom->file()->path());
+                               }
+                               
+                               if (!categoryAtom->file()->objcHasCategoryClassPropertiesField()) {
+                                       haveCategoriesWithoutClassPropertyStorage = true;
+                                       // fprintf(stderr, "category in file %s has no storage for class properties\n", categoryAtom->file()->path());
+                               }
+                       }
+               }
+       }
+}
 
 
+template <typename A, bool isObjC2>
 void doPass(const Options& opts, ld::Internal& state)
-{      
-       // only make image info section if objc was used
-       if ( state.objcObjectConstraint != ld::File::objcConstraintNone ) {
-
-               // verify dylibs are GC compatible with object files
-               if ( state.objcObjectConstraint != state.objcDylibConstraint ) {
-                       if (   (state.objcDylibConstraint == ld::File::objcConstraintRetainRelease)
-                               && (state.objcObjectConstraint == ld::File::objcConstraintGC) ) {
-                                       throw "Linked dylibs built for retain/release but object files built for GC-only";
-                       }
-                       else if (   (state.objcDylibConstraint == ld::File::objcConstraintGC)
-                                    && (state.objcObjectConstraint == ld::File::objcConstraintRetainRelease) ) {
-                                       throw "Linked dylibs built for GC-only but object files built for retain/release";
-                       }
+{
+       // Do nothing if the output has no ObjC content.
+       if ( state.objcObjectConstraint == ld::File::objcConstraintNone ) {
+               return;
+       }
+
+       // verify dylibs are GC compatible with object files
+       if ( state.objcObjectConstraint != state.objcDylibConstraint ) {
+               if (   (state.objcDylibConstraint == ld::File::objcConstraintRetainRelease)
+                       && (state.objcObjectConstraint == ld::File::objcConstraintGC) ) {
+                               throw "Linked dylibs built for retain/release but object files built for GC-only";
+               }
+               else if (   (state.objcDylibConstraint == ld::File::objcConstraintGC)
+                            && (state.objcObjectConstraint == ld::File::objcConstraintRetainRelease) ) {
+                               throw "Linked dylibs built for GC-only but object files built for retain/release";
                }
-       
-               const bool compaction = opts.objcGcCompaction();
-               
-               // add image info atom
-               switch ( opts.architecture() ) {
-#if SUPPORT_ARCH_x86_64
-                       case CPU_TYPE_X86_64:
-                               state.addAtom(*new ObjCImageInfoAtom<x86_64>(state.objcObjectConstraint, compaction, 
-                                                         true, state.swiftVersion));
-                               break;
-#endif
-#if SUPPORT_ARCH_i386
-                       case CPU_TYPE_I386:
-                               state.addAtom(*new ObjCImageInfoAtom<x86>(state.objcObjectConstraint, compaction, 
-                                                         opts.objCABIVersion2POverride() ? true : false, state.swiftVersion));
-                               break;
-#endif
-#if SUPPORT_ARCH_arm_any
-                       case CPU_TYPE_ARM:
-                               state.addAtom(*new ObjCImageInfoAtom<arm>(state.objcObjectConstraint, compaction, 
-                                                         true, state.swiftVersion));
-                               break;
-#endif
-#if SUPPORT_ARCH_arm64
-                       case CPU_TYPE_ARM64:
-                               state.addAtom(*new ObjCImageInfoAtom<arm64>(state.objcObjectConstraint, compaction, 
-                                                         true, state.swiftVersion));
-                               break;
-#endif
-                       default:
-                               assert(0 && "unknown objc arch");
-               }       
        }
-       
+
        if ( opts.objcCategoryMerging() ) {
                // optimize classes defined in this linkage unit by merging in categories also in this linkage unit
-               switch ( opts.architecture() ) {
+               OptimizeCategories<A>::doit(opts, state);
+       }
+
+       // Search for surviving categories that have a non-null class properties field.
+       // Search for surviving categories that do not have storage for the class properties field.
+       bool haveCategoriesWithNonNullClassProperties;
+       bool haveCategoriesWithoutClassPropertyStorage;
+       scanCategories<A>(state, haveCategoriesWithNonNullClassProperties, haveCategoriesWithoutClassPropertyStorage);
+
+       // Complain about mismatched category ABI.
+       // These can't be combined into a single linkage unit because there is only one size indicator for all categories in the file.
+       // If there is a mismatch then we don't set the HasCategoryClassProperties bit in the output file, 
+       // which has at runtime causes any class property metadata that was present to be ignored.
+       if (haveCategoriesWithNonNullClassProperties  &&  haveCategoriesWithoutClassPropertyStorage) {
+               warning("Some object files have incompatible Objective-C category definitions. Some category metadata may be lost. All files containing Objective-C categories should be built using the same compiler.");
+       }
+
+       // add image info atom
+       // The HasCategoryClassProperties bit is set as often as possible.
+       state.addAtom(*new ObjCImageInfoAtom<A>(state.objcObjectConstraint, opts.objcGcCompaction(), isObjC2,
+                                                                                       !haveCategoriesWithoutClassPropertyStorage, state.swiftVersion));
+}
+
+
+void doPass(const Options& opts, ld::Internal& state)
+{              
+       switch ( opts.architecture() ) {
 #if SUPPORT_ARCH_x86_64
-                       case CPU_TYPE_X86_64:
-                               OptimizeCategories<x86_64>::doit(opts, state);
-                               break;
+               case CPU_TYPE_X86_64:
+                       doPass<x86_64, true>(opts, state);
+                       break;
 #endif
 #if SUPPORT_ARCH_i386
-                       case CPU_TYPE_I386:
-                               if ( opts.objCABIVersion2POverride() )
-                    OptimizeCategories<x86>::doit(opts, state);
-                               break;
+               case CPU_TYPE_I386:
+                       if (opts.objCABIVersion2POverride()) {
+                               doPass<x86, true>(opts, state);
+                       } else {
+                               doPass<x86, false>(opts, state);
+                       }
+                       break;
 #endif
 #if SUPPORT_ARCH_arm_any
-                       case CPU_TYPE_ARM:
-                               OptimizeCategories<arm>::doit(opts, state);
-                               break;
+               case CPU_TYPE_ARM:
+                       doPass<arm, true>(opts, state);
+                       break;
 #endif
 #if SUPPORT_ARCH_arm64
-                       case CPU_TYPE_ARM64:
-                               // disabled until tested
-                               break;
+               case CPU_TYPE_ARM64:
+                       doPass<arm64, true>(opts, state);
+                       break;
 #endif
-                       default:
-                               assert(0 && "unknown objc arch");
-               }       
+               default:
+                       assert(0 && "unknown objc arch");
        }
 }
 
index 09de1fa108cea34a52c534dac24bdebaa44f2480..815e28a53b1a80184a44e4f3b86d631c1b0ff7b5 100644 (file)
@@ -406,5 +406,5 @@ private:
 ld::Section KextStubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeCode);
 
 
-} // namespace x86_64 
+} // namespace arm64
 
index abed7e3b62f64bb49076115f5f4ff77df9becd27..fb5b9f3b6d2d6ea51cdf60fb2f78f02e45b936dc 100644 (file)
@@ -213,7 +213,7 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport)
                                return new ld::passes::stubs::arm::StubPICKextAtom(*this, target);
                        }
                        else if ( usingCompressedLINKEDIT() && !forLazyDylib ) {
-                               if ( (_stubCount < 900) && !_mightBeInSharedRegion && !_largeText )
+                               if ( (_stubCount < 900) && !_mightBeInSharedRegion && !_largeText && !_options.makeEncryptable() )
                                        return new ld::passes::stubs::arm::StubCloseAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport);
                                else if ( _pic )
                                        return new ld::passes::stubs::arm::StubPICAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport, usingDataConst);
index 867f3133439febfd6556511420adc96fb7223756..77399863b14a82c69e18c0e405a125d4b9c0e933 100644 (file)
@@ -843,6 +843,9 @@ void dumper::dumpFixup(const ld::Fixup* ref)
                case ld::Fixup::kindStoreARM64PCRelToGOT:
                        printf(", then store as 32-bit delta to GOT entry");
                        break;
+               case ld::Fixup::kindStoreARM64PointerToGOT32:
+                       printf(", then store as 32-bit pointer to GOT entry");
+                       break;
                case ld::Fixup::kindDtraceExtra:
                        printf("dtrace static probe extra info");
                        break;
index f19538f1533ef31385a1fedb4d1cf0036493efa5..83d601e6596db73778ce8cd1d9f7e9e716b80cca 100644 (file)
@@ -236,6 +236,7 @@ bool DyldInfoPrinter<x86_64>::validFile(const uint8_t* fileContent)
                case MH_DYLIB_STUB:
                case MH_BUNDLE:
                case MH_DYLINKER:
+               case MH_KEXT_BUNDLE:
                        return true;
        }
        return false;
@@ -256,6 +257,7 @@ bool DyldInfoPrinter<arm>::validFile(const uint8_t* fileContent)
                case MH_DYLIB_STUB:
                case MH_BUNDLE:
                case MH_DYLINKER:
+               case MH_KEXT_BUNDLE:
                        return true;
        }
        return false;
@@ -276,12 +278,14 @@ bool DyldInfoPrinter<arm64>::validFile(const uint8_t* fileContent)
                case MH_DYLIB:
                case MH_BUNDLE:
                case MH_DYLINKER:
+               case MH_KEXT_BUNDLE:
                        return true;
        }
        return false;
 }
 #endif
 
+
 template <typename A>
 DyldInfoPrinter<A>::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool printArch)
  : fHeader(NULL), fLength(fileLength), 
@@ -1652,7 +1656,10 @@ void DyldInfoPrinter<A>::printSharedRegionInfo()
                                uint64_t toOffsetCount = read_uleb128(p, infoEnd);
                                const macho_section<P>* fromSection = fSections[fromSectionIndex];
                                const macho_section<P>* toSection = fSections[toSectionIndex];
-                               printf("from sect=%s, to sect=%s, count=%lld:\n", fromSection->sectname(), toSection->sectname(), toOffsetCount);
+                               char fromSectionName[20];
+                               strncpy(fromSectionName, fromSection->sectname(), 16);
+                               fromSectionName[16] = '\0';
+                               printf("from sect=%s/%s, to sect=%s/%s, count=%lld:\n", fromSection->segname(), fromSectionName, toSection->segname(), toSection->sectname(), toOffsetCount);
                                uint64_t toSectionOffset = 0;
                                const char* lastFromSymbol = NULL;
                                for (uint64_t j=0; j < toOffsetCount; ++j) {
@@ -1933,6 +1940,7 @@ arm64::P::uint_t DyldInfoPrinter<arm64>::relocBase()
 }
 #endif
 
+
 template <>
 const char*    DyldInfoPrinter<ppc>::relocTypeName(uint8_t r_type)
 {
@@ -1994,6 +2002,7 @@ const char*       DyldInfoPrinter<arm64>::relocTypeName(uint8_t r_type)
 }
 #endif
 
+
 template <typename A>
 void DyldInfoPrinter<A>::printRelocRebaseInfo()
 {
@@ -2324,7 +2333,7 @@ static void dump(const char* path)
                                                if ( DyldInfoPrinter<arm64>::validFile(p + offset) )
                                                        DyldInfoPrinter<arm64>::make(p + offset, size, path, (sPreferredArch == 0));
                                                else
-                                                       throw "in universal file, arm64 slice does not contain arm mach-o";
+                                                       throw "in universal file, arm64 slice does not contain arm64 mach-o";
                                                break;
 #endif
                                        default:
index 0465cd579c9f68a0718dc3ac37e8c0b6cf0d1f3d..97138e85d6e5c9258bcc7c5739210d6a628f5872 100644 (file)
@@ -258,13 +258,15 @@ bool MachOChecker<arm64>::validFile(const uint8_t* fileContent)
 }
 #endif
 
+
+template <> uint8_t MachOChecker<ppc>::loadCommandSizeMask()   { return 0x03; }
+template <> uint8_t MachOChecker<ppc64>::loadCommandSizeMask() { return 0x07; }
 template <> uint8_t MachOChecker<x86>::loadCommandSizeMask()   { return 0x03; }
 template <> uint8_t MachOChecker<x86_64>::loadCommandSizeMask() { return 0x07; }
 template <> uint8_t MachOChecker<arm>::loadCommandSizeMask()   { return 0x03; }
 template <> uint8_t MachOChecker<arm64>::loadCommandSizeMask() { return 0x07; }
 
 
-
 template <>
 x86::P::uint_t MachOChecker<x86>::getInitialStackPointer(const macho_thread_command<x86::P>* threadInfo)
 {
@@ -290,6 +292,13 @@ arm64::P::uint_t MachOChecker<arm64>::getInitialStackPointer(const macho_thread_
 }
 
 
+template <>
+ppc::P::uint_t MachOChecker<ppc>::getEntryPoint(const macho_thread_command<ppc::P>* threadInfo)
+{
+       return threadInfo->thread_register(0);
+}
+
+
 template <>
 x86::P::uint_t MachOChecker<x86>::getEntryPoint(const macho_thread_command<x86::P>* threadInfo)
 {
@@ -343,7 +352,6 @@ const char* MachOChecker<A>::archName()
 }
 
 
-
 template <typename A>
 MachOChecker<A>::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path, const char* verifierDstRoot)
  : fHeader(NULL), fLength(fileLength), fInstallName(NULL), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fDynamicSymbolTable(NULL), fIndirectTableCount(0),
@@ -661,8 +669,12 @@ void MachOChecker<A>::checkLoadCommands()
 
        // verify encryption info
        if ( encryption_info != NULL ) {
-               if ( fHeader->filetype() != MH_EXECUTE )
-                       throw "LC_ENCRYPTION_INFO load command is only legal in main executables";
+               switch ( fHeader->filetype() ) {
+                       case MH_EXECUTE: case MH_DYLIB: case MH_BUNDLE: 
+                               break;  // okay
+                       default: 
+                               throw "LC_ENCRYPTION_INFO load command is not allowed in this file type";
+               }
                if ( encryption_info->cryptoff() <  (sizeof(macho_header<P>) + fHeader->sizeofcmds()) )
                        throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands";
                if ( (encryption_info->cryptoff() % 4096) != 0 )
@@ -712,8 +724,6 @@ void MachOChecker<A>::checkLoadCommands()
                                                throw "string pool extends beyond __LINKEDIT";
                                        if ( (symtab->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed
                                                throw "string pool start not pointer aligned";
-                                       if ( (symtab->strsize() % sizeof(pint_t)) != 0 )        
-                                               throw "string pool size not a multiple of pointer size";
                                }
                                break;
                        case LC_DYSYMTAB:
@@ -1131,6 +1141,7 @@ arm64::P::uint_t MachOChecker<arm64>::relocBase()
        return fFirstWritableSegment->vmaddr();
 }
 
+
 template <typename A>
 bool MachOChecker<A>::addressInWritableSegment(pint_t address)
 {
@@ -1274,6 +1285,7 @@ void MachOChecker<arm64>::checkLocalReloation(const macho_relocation_info<P>* re
 }
 #endif
 
+
 template <typename A>
 void MachOChecker<A>::checkRelocations()
 {
diff --git a/src/other/objcimageinfo.cpp b/src/other/objcimageinfo.cpp
new file mode 100644 (file)
index 0000000..0609d3b
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * Copyright (c) 2007-2009 Apple 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@
+ */
+
+// objcimageinfo.cpp
+// Print or edit ObjC image info bits.
+// This is used to verify ld's handling of these bits
+// for values that are not emitted by current compilers.
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <mach-o/fat.h>
+#include <mach-o/arch.h>
+#include <mach-o/loader.h>
+
+#include "MachOFileAbstraction.hpp"
+
+#if __BIG_ENDIAN__
+    typedef BigEndian CurrentEndian;
+    typedef LittleEndian OtherEndian;
+#elif __LITTLE_ENDIAN__
+    typedef LittleEndian CurrentEndian;
+    typedef BigEndian OtherEndian;
+#else
+#   error unknown endianness
+#endif
+
+static const bool debug = false;
+
+static bool processFile(const char *filename, uint32_t set, uint32_t clear);
+
+// fixme use objc/objc-abi.h instead
+struct objc_image_info {
+    uint32_t version;
+    uint32_t flags;
+
+    enum : uint32_t {
+        IsReplacement       = 1<<0,  // used for Fix&Continue, now ignored
+        SupportsGC          = 1<<1,  // image supports GC
+        RequiresGC          = 1<<2,  // image requires GC
+        OptimizedByDyld     = 1<<3,  // image is from an optimized shared cache
+        CorrectedSynthesize = 1<<4,  // used for an old workaround, now ignored
+        IsSimulated         = 1<<5,  // image compiled for a simulator platform
+        HasCategoryClassProperties  = 1<<6,  // class properties in category_t
+       
+        SwiftVersionMask    = 0xff << 8  // Swift ABI version
+    };
+};
+
+// objc_image_info flags and their names
+static const struct {
+    const char *name;
+    uint32_t value;
+} Flags[] = {
+    { "supports-gc", objc_image_info::SupportsGC }, 
+    { "requires-gc", objc_image_info::RequiresGC }, 
+    { "has-category-class-properties", objc_image_info::HasCategoryClassProperties }, 
+    { 0, 0 }
+};
+
+static void usage(const char *self)
+{
+    printf("usage: %s [+FLAG|-FLAG ...] file ...\n", self);
+    printf("Use +FLAG to set and -FLAG to clear.\n");
+    printf("Known flags: ");
+    for (int i = 0; Flags[i].name != 0; i++) {
+        printf("%s ", Flags[i].name);
+    }
+    printf("\n");
+}
+
+static uint32_t flagForName(const char *name)
+{
+    for (int i = 0; Flags[i].name != 0; i++) {
+        if (0 == strcmp(Flags[i].name, name)) {
+            return Flags[i].value;
+        }
+    }
+    return 0;
+}
+
+static const char *nameForFlag(uint32_t flag)
+{
+    for (int i = 0; Flags[i].name != 0; i++) {
+        if (Flags[i].value == flag) {
+            return Flags[i].name;
+        }
+    }
+    return 0;
+}
+
+static void printFlags(uint32_t flags)
+{
+    printf("0x%x", flags);
+
+    // Print flags and unknown bits
+    for (int i = 0; i < 24; i++) {
+        uint32_t flag = 1<<i;
+        if (flag & objc_image_info::SwiftVersionMask) continue;
+        if (flags & flag) {
+            const char *name = nameForFlag(flag);
+            if (name) printf(" %s", name);
+            else printf(" unknown-%u", flag);
+        }
+    }
+
+    // Print Swift version
+    uint32_t mask = objc_image_info::SwiftVersionMask;
+    uint32_t shift = __builtin_ctzl(mask);
+    uint32_t swift = (flags & mask) >> shift;
+    if (swift > 0) {
+        printf(" swift-version=%u", swift);
+    }
+}
+
+static bool isFlagArgument(const char *arg)
+{
+    return (arg  &&  (arg[0] == '+' || arg[0] == '-'));
+}
+
+int main(int argc, const char *argv[]) {
+    uint32_t set = 0;
+    uint32_t clear = 0;
+
+    // Find flag arguments (which are +FLAG or -FLAG).
+    int i;
+    for (i = 1; i < argc && isFlagArgument(argv[i]); i++) {
+        const char *arg = argv[i];
+        uint32_t flag = flagForName(arg+1);
+        if (flag) {
+            if (arg[0] == '+') {
+                set |= flag;
+            } else {
+                clear |= flag;
+            }
+        } else {
+            printf("error: unrecognized ObjC flag '%s'\n", arg);
+            usage(argv[0]);
+            return 1;
+        }
+    }
+
+    // Complain if +FLAG and -FLAG are both set for some flag.
+    uint32_t overlap = set & clear;
+    if (overlap) {
+        printf("error: conflicting changes specified: ");
+        printFlags(overlap);
+        printf("\n");
+        usage(argv[0]);
+        return 1;
+    }
+
+    // Complain if there are no filenames.
+    if (i == argc) {
+        printf("error: no files specified\n");
+        usage(argv[0]);
+        return 1;
+    }
+
+    // Process files.
+    for (; i < argc; i++) {
+        if (!processFile(argv[i], set, clear)) return 1;
+    }
+    return 0;
+}
+
+
+// Segment and section names are 16 bytes and may be un-terminated.
+static bool segnameEquals(const char *lhs, const char *rhs)
+{
+    return 0 == strncmp(lhs, rhs, 16);
+}
+
+static bool segnameStartsWith(const char *segname, const char *prefix)
+{
+    return 0 == strncmp(segname, prefix, strlen(prefix));
+}
+
+static bool sectnameEquals(const char *lhs, const char *rhs)
+{
+    return segnameEquals(lhs, rhs);
+}
+
+
+template <typename P>
+static void dosect(const char *filename, uint8_t *start, macho_section<P> *sect,
+                   uint32_t set, uint32_t clear)
+{
+    if (debug) printf("section %.16s from segment %.16s\n",
+                      sect->sectname(), sect->segname());
+
+    if ((segnameStartsWith(sect->segname(),  "__DATA")  &&
+         sectnameEquals(sect->sectname(), "__objc_imageinfo"))  ||
+        (segnameEquals(sect->segname(),  "__OBJC")  &&
+         sectnameEquals(sect->sectname(), "__image_info")))
+    {
+        objc_image_info *ii = (objc_image_info *)(start + sect->offset());
+        uint32_t oldFlags = P::E::get32(ii->flags);
+        uint32_t newFlags = (oldFlags | set) & ~clear;
+        if (oldFlags != newFlags) {
+            P::E::set32(ii->flags, newFlags);
+            if (debug) printf("changed flags from 0x%x to 0x%x\n", 
+                              oldFlags, newFlags);
+        }
+
+        printf("%s: ", filename);
+        printFlags(newFlags);
+        printf("\n");
+    }
+}
+
+template <typename P>
+static void doseg(const char *filename, 
+                  uint8_t *start, macho_segment_command<P> *seg,
+                  uint32_t set, uint32_t clear)
+{
+    if (debug) printf("segment name: %.16s, nsects %u\n",
+                      seg->segname(), seg->nsects());
+    macho_section<P> *sect = (macho_section<P> *)(seg + 1);
+    for (uint32_t i = 0; i < seg->nsects(); ++i) {
+        dosect(filename, start, &sect[i], set, clear);
+    }
+}
+
+
+template<typename P>
+static bool parse_macho(const char *filename, uint8_t *buffer, 
+                        uint32_t set, uint32_t clear)
+{
+    macho_header<P>* mh = (macho_header<P>*)buffer;
+    uint8_t *cmds = (uint8_t *)(mh + 1);
+    for (uint32_t c = 0; c < mh->ncmds(); c++) {
+        macho_load_command<P>* cmd = (macho_load_command<P>*)cmds;
+        cmds += cmd->cmdsize();
+        if (cmd->cmd() == LC_SEGMENT  ||  cmd->cmd() == LC_SEGMENT_64) {
+            doseg(filename, buffer, (macho_segment_command<P>*)cmd, set, clear);
+        }
+    }
+
+    return true;
+}
+
+
+static bool parse_macho(const char *filename, uint8_t *buffer, 
+                        uint32_t set, uint32_t clear)
+{
+    uint32_t magic = *(uint32_t *)buffer;
+
+    switch (magic) {
+    case MH_MAGIC_64:
+        return parse_macho<Pointer64<CurrentEndian>>
+            (filename, buffer, set, clear);
+    case MH_MAGIC:
+        return parse_macho<Pointer32<CurrentEndian>>
+            (filename, buffer, set, clear);
+    case MH_CIGAM_64:
+        return parse_macho<Pointer64<OtherEndian>>
+            (filename, buffer, set, clear);
+    case MH_CIGAM:
+        return parse_macho<Pointer32<OtherEndian>>
+            (filename, buffer, set, clear);
+    default:
+        printf("error: file '%s' is not mach-o (magic %x)\n", filename, magic);
+        return false;
+    }
+}
+
+
+static bool parse_fat(const char *filename, uint8_t *buffer, size_t size, 
+                      uint32_t set, uint32_t clear)
+{
+    uint32_t magic;
+
+    if (size < sizeof(magic)) {
+        printf("error: file '%s' is too small\n", filename);
+        return false;
+    }
+
+    magic = *(uint32_t *)buffer;
+    if (magic != FAT_MAGIC && magic != FAT_CIGAM) {
+        /* Not a fat file */
+        return parse_macho(filename, buffer, set, clear);
+    } else {
+        struct fat_header *fh;
+        uint32_t fat_magic, fat_nfat_arch;
+        struct fat_arch *archs;
+        
+        if (size < sizeof(struct fat_header)) {
+            printf("error: file '%s' is too small\n", filename);
+            return false;
+        }
+
+        fh = (struct fat_header *)buffer;
+        fat_magic = OSSwapBigToHostInt32(fh->magic);
+        fat_nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch);
+
+        if (size < (sizeof(struct fat_header) + fat_nfat_arch * sizeof(struct fat_arch))) {
+            printf("error: file '%s' is too small\n", filename);
+            return false;
+        }
+
+        archs = (struct fat_arch *)(buffer + sizeof(struct fat_header));
+
+        /* Special case hidden CPU_TYPE_ARM64 */
+        if (size >= (sizeof(struct fat_header) + (fat_nfat_arch + 1) * sizeof(struct fat_arch))) {
+            if (fat_nfat_arch > 0
+                && OSSwapBigToHostInt32(archs[fat_nfat_arch].cputype) == CPU_TYPE_ARM64) {
+                fat_nfat_arch++;
+            }
+        }
+        /* End special case hidden CPU_TYPE_ARM64 */
+
+        if (debug) printf("%d fat architectures\n", fat_nfat_arch);
+
+        for (uint32_t i = 0; i < fat_nfat_arch; i++) {
+            uint32_t arch_cputype = OSSwapBigToHostInt32(archs[i].cputype);
+            uint32_t arch_cpusubtype = OSSwapBigToHostInt32(archs[i].cpusubtype);
+            uint32_t arch_offset = OSSwapBigToHostInt32(archs[i].offset);
+            uint32_t arch_size = OSSwapBigToHostInt32(archs[i].size);
+
+            if (debug) printf("cputype %d cpusubtype %d\n", 
+                              arch_cputype, arch_cpusubtype);
+
+            /* Check that slice data is after all fat headers and archs */
+            if (arch_offset < (sizeof(struct fat_header) + fat_nfat_arch * sizeof(struct fat_arch))) {
+                printf("error: file is badly formed\n");
+                return false;
+            }
+
+            /* Check that the slice ends before the file does */
+            if (arch_offset > size) {
+                printf("error: file '%s' is badly formed\n", filename);
+                return false;
+            }
+
+            if (arch_size > size) {
+                printf("error: file '%s' is badly formed\n", filename);
+                return false;
+            }
+
+            if (arch_offset > (size - arch_size)) {
+                printf("error: file '%s' is badly formed\n", filename);
+                return false;
+            }
+
+            bool ok = parse_macho(filename, buffer + arch_offset, set, clear);
+            if (!ok) return false;
+        }
+        return true;
+    }
+}
+
+static bool processFile(const char *filename, uint32_t set, uint32_t clear)
+{
+    if (debug) printf("file %s\n", filename);
+    int openPerm = O_RDONLY;
+    int mmapPerm = PROT_READ;
+    if (set || clear) {
+        openPerm = O_RDWR;
+        mmapPerm = PROT_READ | PROT_WRITE;
+    }
+
+    int fd = open(filename, openPerm);
+    if (fd < 0) {
+        printf("error: open %s: %s\n", filename, strerror(errno));
+        return false;
+    }
+    
+    struct stat st;
+    if (fstat(fd, &st) < 0) {
+        printf("error: fstat %s: %s\n", filename, strerror(errno));
+        return false;
+    }
+
+    void *buffer = mmap(NULL, (size_t)st.st_size, mmapPerm, 
+                        MAP_FILE|MAP_SHARED, fd, 0);
+    if (buffer == MAP_FAILED) {
+        printf("error: mmap %s: %s\n", filename, strerror(errno));
+        return false;
+    }
+
+    bool result = 
+        parse_fat(filename, (uint8_t *)buffer, (size_t)st.st_size, set, clear);
+    munmap(buffer, (size_t)st.st_size);
+    close(fd);
+    return result;
+}
index bc925fbbdb6c0a0a16e7e477b1ebd2b0dc25f514..5b264d1b3d0ba2b7246d8f51de6af1fd1794dcbb 100644 (file)
@@ -163,6 +163,7 @@ bool UnwindPrinter<arm64>::validFile(const uint8_t* fileContent)
 }
 #endif
 
+
 template <>
 bool UnwindPrinter<arm>::validFile(const uint8_t* fileContent)
 {      
@@ -756,6 +757,7 @@ void UnwindPrinter<arm64>::decode(uint32_t encoding, const uint8_t* funcStart, c
 }
 #endif
 
+
 template <>
 void UnwindPrinter<arm>::decode(uint32_t encoding, const uint8_t* funcStart, char* str)
 {
@@ -872,6 +874,7 @@ const char* UnwindPrinter<arm64>::personalityName(const macho_relocation_info<ar
 }
 #endif
 
+
 template <>
 const char* UnwindPrinter<arm>::personalityName(const macho_relocation_info<arm::P>* reloc)
 {
@@ -1138,7 +1141,7 @@ static void dump(const char* path, const std::set<cpu_type_t>& onlyArchs, bool s
                else if ( UnwindPrinter<arm64>::validFile(p) && onlyArchs.count(CPU_TYPE_ARM64) ) {
                        UnwindPrinter<arm64>::make(p, length, path, showFunctionNames);
                }
-#endif         
+#endif
                else if ( UnwindPrinter<arm>::validFile(p) && onlyArchs.count(CPU_TYPE_ARM) ) {
                        UnwindPrinter<arm>::make(p, length, path, showFunctionNames);
                }
index f0cbbf177147d5872859f2e6d7a49003c1f564ea..c3a7864532d19c7a5576e09fe3b0b4417f12c409 100644 (file)
@@ -10,24 +10,26 @@ VALID_ARCHS ?= "i386 x86_64 armv6"
 
 
 MYDIR=$(shell cd ../../bin;pwd)
-LD                     = ld
-OBJECTDUMP     = ObjectDump
-MACHOCHECK     = machocheck
-OTOOL =   xcrun otool
-REBASE         = rebase
-DYLDINFO       = dyldinfo
+LD                             = ld
+OBJECTDUMP             = ObjectDump
+OBJCIMAGEINFO  = objcimageinfo
+MACHOCHECK             = machocheck
+OTOOL                  = xcrun otool
+REBASE                 = rebase
+DYLDINFO               = dyldinfo
 
 ifdef BUILT_PRODUCTS_DIR
        # if run within Xcode, add the just built tools to the command path
        PATH := ${BUILT_PRODUCTS_DIR}:${MYDIR}:${PATH}
        COMPILER_PATH := ${BUILT_PRODUCTS_DIR}:${COMPILER_PATH}
-       LD_PATH     = ${BUILT_PRODUCTS_DIR}
-       LD                      = ${BUILT_PRODUCTS_DIR}/ld
-       OBJECTDUMP      = ${BUILT_PRODUCTS_DIR}/ObjectDump
-       MACHOCHECK      = ${BUILT_PRODUCTS_DIR}/machocheck
-       REBASE          = ${BUILT_PRODUCTS_DIR}/rebase
-       UNWINDDUMP  = ${BUILT_PRODUCTS_DIR}/unwinddump
-       DYLDINFO        = ${BUILT_PRODUCTS_DIR}/dyldinfo
+       LD_PATH         = ${BUILT_PRODUCTS_DIR}
+       LD                              = ${BUILT_PRODUCTS_DIR}/ld
+       OBJECTDUMP              = ${BUILT_PRODUCTS_DIR}/ObjectDump
+       OBJCIMAGEINFO   = ${BUILT_PRODUCTS_DIR}/objcimageinfo
+       MACHOCHECK              = ${BUILT_PRODUCTS_DIR}/machocheck
+       REBASE                  = ${BUILT_PRODUCTS_DIR}/rebase
+       UNWINDDUMP      = ${BUILT_PRODUCTS_DIR}/unwinddump
+       DYLDINFO                = ${BUILT_PRODUCTS_DIR}/dyldinfo
 else
        ifneq "$(findstring /unit-tests/test-cases/, $(shell pwd))" ""
                # if run from Terminal inside unit-test directory
@@ -35,13 +37,14 @@ else
                DEBUGDIR=$(shell cd ../../../build/Debug;pwd)
                PATH := ${RELEASEADIR}:${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${PATH}
                COMPILER_PATH := ${RELEASEADIR}:${RELEASEDIR}:${DEBUGDIR}:${COMPILER_PATH}
-               LD_PATH     = ${DEBUGDIR}
-               LD                      = ${DEBUGDIR}/ld
-               OBJECTDUMP      = ${DEBUGDIR}/ObjectDump
-               MACHOCHECK      = ${DEBUGDIR}/machocheck
-               REBASE          = ${DEBUGDIR}/rebase
-               UNWINDDUMP  = ${DEBUGDIR}/unwinddump
-               DYLDINFO        = ${DEBUGDIR}/dyldinfo
+               LD_PATH         = ${DEBUGDIR}
+               LD                              = ${DEBUGDIR}/ld
+               OBJECTDUMP              = ${DEBUGDIR}/ObjectDump
+               OBJCIMAGEINFO   = ${DEBUGDIR}/objcimageinfo
+               MACHOCHECK              = ${DEBUGDIR}/machocheck
+               REBASE                  = ${DEBUGDIR}/rebase
+               UNWINDDUMP      = ${DEBUGDIR}/unwinddump
+               DYLDINFO                = ${DEBUGDIR}/dyldinfo
        else
                PATH := ${MYDIR}:${PATH}:
                COMPILER_PATH := ${MYDIR}:${COMPILER_PATH}:
@@ -51,22 +54,23 @@ export PATH
 export COMPILER_PATH
 export GCC_EXEC_PREFIX=garbage
 
+IOS_SDK = $(shell xcodebuild -sdk iphoneos.internal -version Path  2>/dev/null)
+OSX_SDK = $(shell xcodebuild -sdk macosx.internal -version Path  2>/dev/null)
 ifeq ($(ARCH),ppc)
-       SDKExtra = -isysroot /Developer/SDKs/MacOSX10.6.sdk
+       OSX_SDK = /Developer/SDKs/MacOSX10.6.sdk
 endif
 
-CC              = $(shell xcrun -find clang) -arch ${ARCH} ${SDKExtra} -mmacosx-version-min=10.8
+CC              = $(shell xcrun -find clang) -arch ${ARCH} -mmacosx-version-min=10.8 -isysroot $(OSX_SDK)
 CCFLAGS = -Wall 
+LDFLAGS = -syslibroot $(OSX_SDK)
 ASMFLAGS =
 VERSION_NEW_LINKEDIT = -mmacosx-version-min=10.6
 VERSION_OLD_LINKEDIT = -mmacosx-version-min=10.4
 LD_NEW_LINKEDIT = -macosx_version_min 10.6
 
-CXX              = $(shell xcrun -find clang++) -arch ${ARCH} ${SDKExtra}
+CXX              = $(shell xcrun -find clang++) -arch ${ARCH} -isysroot $(OSX_SDK)
 CXXFLAGS = -Wall -stdlib=libc++ 
 
-IOS_SDK = $(shell xcodebuild -sdk iphoneos.internal -version Path  2>/dev/null)
-
 ifeq ($(ARCH),armv6)
   LDFLAGS := -syslibroot $(IOS_SDK)
   override FILEARCH = arm
index e457aebe217a5f4771a4e8a5e45f3681df5f2334..74275ace87d2f97148d059cd4cd1182256ae1521 100644 (file)
@@ -27,8 +27,6 @@ include ${TESTROOT}/include/common.makefile
 # Verify that code and data references can be redirected via aliases. 
 #
 
-CC=/Volumes/my/src/puzzlebox/build/Debug+Asserts/bin/clang
-
 run: all
 
 all:
index 7b7667d1de7a3bda2870a75ccf2da5f9429ed624..4f4ec6e7d1885e8d74e5e12b605970b10d8623e7 100644 (file)
@@ -31,7 +31,6 @@ include ${TESTROOT}/include/common.makefile
 # No differences means this test passes
 #
 
-CC=/Volumes/my/src/puzzlebox/build/Debug+Asserts/bin/clang
 
 run: all
 
index ae87948046f73b18cef819b2a43e167940b6bdbe..a13a0890a912d403fda5387b0b11fe8ecf3ad65a 100644 (file)
@@ -43,7 +43,7 @@ all:
        ${CC} ${CCFLAGS} main.m -c -o main.o
        libtool -static main.o -o libmain.a
        ${CC} ${CCFLAGS} libmain.a -o main -framework Foundation
-       size -l main | grep ${IMAGE_INFO} | ${FAIL_IF_EMPTY}
+       size -m -l main | grep ${IMAGE_INFO} | ${FAIL_IF_EMPTY}
        ${PASS_IFF_GOOD_MACHO} main
 
 clean:
index c8b16ee79c0034c332c4fb2816c2f9d2d7f46844..2b1df9f06e8a8ae83e535f7a2d15929df5437dde 100644 (file)
@@ -43,10 +43,10 @@ run: all
 
 all:
        ${CC} ${CCFLAGS} foo.c bar.c -o foo -framework CoreFoundation -dead_strip
-       size -l foo | grep "__cfstring: ${CFSTRING_SIZE}" | ${FAIL_IF_EMPTY}
+       size -m -l foo | grep "__cfstring: ${CFSTRING_SIZE}" | ${FAIL_IF_EMPTY}
        # now try with -fwritable-strings
        ${CC} ${CCFLAGS} foo.c bar.c -o foo_writable -framework CoreFoundation -dead_strip -fwritable-strings 2>/dev/null
-       size -l foo_writable | grep "__cfstring: ${CFSTRING_SIZE}" | ${PASS_IFF_STDIN}
+       size -m -l foo_writable | grep "__cfstring: ${CFSTRING_SIZE}" | ${PASS_IFF_STDIN}
 
 clean:
        rm -rf foo foo_writable
index 272cb9f60b65bc0c23785a93185d77ba1f5fcea6..a707ba564950ea19fe41a5bcc1d4c6c4ae6e63e0 100644 (file)
@@ -42,8 +42,8 @@ run: all
 
 all:
        ${CC} ${CCFLAGS} foo.m bar.m -o foo -framework CoreFoundation -dead_strip
-       size -l foo | grep "__cfstring: ${CFSTRING_SIZE}" | ${FAIL_IF_EMPTY}
-       size -l foo | grep "__ustring: ${USTRING_SIZE}" | ${FAIL_IF_EMPTY}
+       size -m -l foo | grep "__cfstring: ${CFSTRING_SIZE}" | ${FAIL_IF_EMPTY}
+       size -m -l foo | grep "__ustring: ${USTRING_SIZE}" | ${FAIL_IF_EMPTY}
        ${PASS_IFF_GOOD_MACHO} foo
 
 clean:
index ff0919bb63341f5fd9ab2f7a493de91541046fb7..8679b88f387229513429154954eae734069fc879 100644 (file)
@@ -32,8 +32,8 @@ run: all
 
 all:
        ${CC} ${CCFLAGS} main.c custom.s -o main 
-       size -l main | grep __cstring | wc -l | grep 2 | ${FAIL_IF_EMPTY}
-       size -l main | grep -A2 __MYSEG | grep __cstring | grep " 10 " | ${FAIL_IF_EMPTY}
+       size -m -l main | grep __cstring | wc -l | grep 2 | ${FAIL_IF_EMPTY}
+       size -m -l main | grep -A2 __MYSEG | grep __cstring | grep " 10 " | ${FAIL_IF_EMPTY}
        ${PASS_IFF_GOOD_MACHO} main
 
 clean:
index 628d045bab254d5d3c014c1d6983ee6ce24ff9e4..b3e8f41e9b59d4b0230d37c0ba2f6b55dbd9f072 100644 (file)
@@ -34,12 +34,12 @@ all:
        ${CC} ${CCFLAGS} -c foo.s -o foo.o
        ${CC} ${CCFLAGS} -c bar.s -o bar.o
        ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o
-       size -l foobar.o | grep "(__TEXT, __cstring): 13" | ${FAIL_IF_EMPTY}
-       size -l foobar.o | grep "(__TEXT, __mystring): 15" | ${FAIL_IF_EMPTY}
+       size -m -l foobar.o | grep "(__TEXT, __cstring): 13" | ${FAIL_IF_EMPTY}
+       size -m -l foobar.o | grep "(__TEXT, __mystring): 15" | ${FAIL_IF_EMPTY}
        otool -lv foobar.o | grep -A10 __mystring | grep S_CSTRING_LITERALS | ${FAIL_IF_EMPTY}
        ${CC} ${CCFLAGS} foo.o bar.o -dynamiclib -o libfoobar.dylib
-       size -l libfoobar.dylib | grep "__cstring: 13" | ${FAIL_IF_EMPTY}
-       size -l libfoobar.dylib | grep "__mystring: 15" | ${FAIL_IF_EMPTY}
+       size -m -l libfoobar.dylib | grep "__cstring: 13" | ${FAIL_IF_EMPTY}
+       size -m -l libfoobar.dylib | grep "__mystring: 15" | ${FAIL_IF_EMPTY}
        otool -lv libfoobar.dylib | grep -A10 __mystring | grep S_CSTRING_LITERALS | ${FAIL_IF_EMPTY}
        ${PASS_IFF_GOOD_MACHO} libfoobar.dylib
 
index a2746940ce77b4b23691b9ede616807b75c3310b..62c1bbee2cb5aa5e80fa68d3ad615963ce4c346e 100644 (file)
@@ -35,7 +35,7 @@ run: all
 
 all:
        ${CC} ${CCFLAGS} main.c deadwood.c -dead_strip -o main-${ARCH}
-       size -l main-${ARCH} | grep __PAGEZERO | ${FAIL_IF_EMPTY}
+       size -m -l main-${ARCH} | grep __PAGEZERO | ${FAIL_IF_EMPTY}
        ${FAIL_IF_BAD_MACHO} main-${ARCH}
        nm -j main-${ARCH} | egrep 'dead_wood|dead_door' | ${FAIL_IF_STDIN}
        ${CC} ${CCFLAGS} -dynamiclib main.c deadwood.c -dead_strip -exported_symbols_list main.exp -o dylib-${ARCH}
diff --git a/unit-tests/test-cases/dirty-data-alt-entry/1.dirty b/unit-tests/test-cases/dirty-data-alt-entry/1.dirty
new file mode 100644 (file)
index 0000000..cf6a8eb
--- /dev/null
@@ -0,0 +1 @@
+_sym1
diff --git a/unit-tests/test-cases/dirty-data-alt-entry/2.dirty b/unit-tests/test-cases/dirty-data-alt-entry/2.dirty
new file mode 100644 (file)
index 0000000..3da0279
--- /dev/null
@@ -0,0 +1 @@
+_sym2
diff --git a/unit-tests/test-cases/dirty-data-alt-entry/Makefile b/unit-tests/test-cases/dirty-data-alt-entry/Makefile
new file mode 100644 (file)
index 0000000..621a997
--- /dev/null
@@ -0,0 +1,47 @@
+##
+# Copyright (c) 2016 Apple 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
+
+#
+# Test that -dirty_data_list will move alt-entry symbols
+##
+
+
+run: all
+
+all:
+       ${CC} ${CCFLAGS} -dynamiclib test.s -o libtest1.dylib -Wl,-dirty_data_list,1.dirty
+       ${FAIL_IF_BAD_MACHO} libtest1.dylib
+       nm -nm libtest1.dylib | grep __DATA_DIRTY | grep _sym1 | ${FAIL_IF_EMPTY}
+       nm -nm libtest1.dylib | grep __DATA_DIRTY | grep _sym2 | ${FAIL_IF_EMPTY}
+       ${CC} ${CCFLAGS} -dynamiclib test.s -o libtest2.dylib -Wl,-dirty_data_list,2.dirty
+       ${FAIL_IF_BAD_MACHO} libtest2.dylib
+       nm -nm libtest2.dylib | grep __DATA_DIRTY | grep _sym1 | ${FAIL_IF_EMPTY}
+        nm -nm libtest2.dylib | grep __DATA_DIRTY | grep _sym2 | ${FAIL_IF_EMPTY}
+       ${PASS_IFF} /usr/bin/true
+
+
+clean:
+
+       rm -rf   libtest1.dylib libtest2.dylib
diff --git a/unit-tests/test-cases/dirty-data-alt-entry/test.s b/unit-tests/test-cases/dirty-data-alt-entry/test.s
new file mode 100644 (file)
index 0000000..abac28d
--- /dev/null
@@ -0,0 +1,23 @@
+
+
+
+
+
+    .data
+_foo:  .quad 0,0,0
+
+# note: the .alt_entry means sym2 needs to stay pinned to sym1
+    .globl _sym1
+    .globl _sym2
+    .alt_entry _sym2
+
+_sym1:  .quad 0,0
+_sym2:  .quad 0,0
+
+    .globl _sym3
+_sym3:  .quad 0,0
+
+
+    .subsections_via_symbols
+
+
index 94b026cb2fe7e1df6dad11001658673e5be5a8b1..1427912169d458529a51b42b3a51ffbab099b4d2 100644 (file)
@@ -33,7 +33,7 @@ run: all
 all:
        ${CC} ${CCFLAGS} -gdwarf-2 hello.c -o dwarf-hello-${ARCH}
        ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH}
-       size -l dwarf-hello-${ARCH} | grep __DWARF | ${PASS_IFF_EMPTY}
+       size -m -l dwarf-hello-${ARCH} | grep __DWARF | ${PASS_IFF_EMPTY}
 
 clean:
        rm -rf dwarf-hello-*
index 866d1786a96f0a580962a7c7b05f25aa7405f1a4..cc236346a65f162a20462840d664261fbc71914d 100644 (file)
@@ -21,7 +21,7 @@ run: all
 all:
        ${CC} ${CCFLAGS} -gdwarf-2 hello.m -c -o hello.o
        ${LD} -r -S hello.o -o hello-r.o
-       size -l hello-r.o | grep ${IMAGE_INFO} | ${PASS_IFF_STDIN}
+       size -m -l hello-r.o | grep ${IMAGE_INFO} | ${PASS_IFF_STDIN}
        
        
 
index 0c7fb32f3b7e2d5d08d46bb044d43a8b9556241d..7f6111624a849eededcf527229d437a26c37c02b 100644 (file)
@@ -44,7 +44,7 @@ all:
        # verify .eh symbol is missing or is from bar.o (file 3)
        grep '\[  2\] __Z4funcv.eh' libfoobar.map | ${FAIL_IF_STDIN}
        # verify no LSDA
-       size -l libfoobar.dylib | grep __gcc_except_tab | ${PASS_IFF_EMPTY}
+       size -m -l libfoobar.dylib | grep __gcc_except_tab | ${PASS_IFF_EMPTY}
 
 clean:
        rm  foo.o foo2.o bar.o  libfoobar.map  libfoobar.dylib
diff --git a/unit-tests/test-cases/lto-archive-objc/Makefile b/unit-tests/test-cases/lto-archive-objc/Makefile
new file mode 100644 (file)
index 0000000..419c199
--- /dev/null
@@ -0,0 +1,23 @@
+
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Check that -ObjC works with LTO
+#
+
+run: all
+
+all:
+       ${CC} ${CCFLAGS} -flto foo.c -c -o foo.o
+       ${CC} ${CCFLAGS} -flto bar.m -c -o bar.o
+       ${CC} ${CCFLAGS} -flto baz.m -c -o baz.o
+       libtool -static foo.o bar.o baz.o -o libstuff.a
+       ${CC} ${CCFLAGS} -flto main.c -c -o main.o
+       ${CC} ${CCFLAGS} main.o -o main libstuff.a -framework Foundation -ObjC 
+       nm main | grep _Bar | ${FAIL_IF_EMPTY}
+       nm main | grep MyCategory | grep doit | ${FAIL_IF_EMPTY}
+       ${PASS_IFF_GOOD_MACHO} main
+       
+clean:
+       rm -rf main *.o libstuff.a main.o main
diff --git a/unit-tests/test-cases/lto-archive-objc/bar.m b/unit-tests/test-cases/lto-archive-objc/bar.m
new file mode 100644 (file)
index 0000000..0de8ec3
--- /dev/null
@@ -0,0 +1,9 @@
+
+#include <Foundation/Foundation.h>
+
+@interface Bar : NSObject
+@end
+
+@implementation Bar
+@end
+
diff --git a/unit-tests/test-cases/lto-archive-objc/baz.m b/unit-tests/test-cases/lto-archive-objc/baz.m
new file mode 100644 (file)
index 0000000..913e9c2
--- /dev/null
@@ -0,0 +1,11 @@
+#include <Foundation/Foundation.h>
+
+@interface NSObject(MyCategory)
+- (void) doit;
+@end
+
+@implementation NSObject(MyCategory)
+- (void) doit
+{
+}
+@end
diff --git a/unit-tests/test-cases/lto-archive-objc/foo.c b/unit-tests/test-cases/lto-archive-objc/foo.c
new file mode 100644 (file)
index 0000000..53d0713
--- /dev/null
@@ -0,0 +1,8 @@
+
+extern void neverFindMe();
+
+__attribute__((constructor))
+void myinit()
+{
+    neverFindMe();
+}
diff --git a/unit-tests/test-cases/lto-archive-objc/main.c b/unit-tests/test-cases/lto-archive-objc/main.c
new file mode 100644 (file)
index 0000000..b2ccbdc
--- /dev/null
@@ -0,0 +1,6 @@
+
+int main()
+{
+   return 0;
+}
+
index 1a6f05906c7da958bb300600084c5386660e7010..2ab56f7738519eaef374b53a9feddf64b996b2ac 100644 (file)
@@ -43,7 +43,7 @@ run: all
 all:
        ${CC} ${CCFLAGS} -flto main.m -c -o main.o
        ${CC} ${CCFLAGS} main.o -o main -framework Foundation
-       size -l main | grep ${IMAGE_INFO} | ${FAIL_IF_EMPTY}
+       size -m -l main | grep ${IMAGE_INFO} | ${FAIL_IF_EMPTY}
        ${PASS_IFF_GOOD_MACHO} main
        
 clean:
index 17ba9c3643d13b43c00af4c3f632da5a0865d9f0..8ca0a2ac571e0c5815eed00aa0d74f6ff23ebfb1 100644 (file)
@@ -39,11 +39,11 @@ all:
                -Wl,-rename_section,__TEXT,__text,__ROM,__code \
                -Wl,-rename_section,__TEXT,__eh_frame,__ROM,__eh_frame \
                -Wl,-rename_section,__TEXT,__cstring,__ROM,__const 
-       size -l main.preload | grep __TEXT  | ${FAIL_IF_STDIN}
-       size -l main.preload | grep __DATA  | ${FAIL_IF_STDIN}
+       size -m -l main.preload | grep __TEXT  | ${FAIL_IF_STDIN}
+       size -m -l main.preload | grep __DATA  | ${FAIL_IF_STDIN}
        nm -m main.preload  | grep __ROM | grep __code | grep _entry | ${FAIL_IF_EMPTY}
        nm -m main.preload  | grep __RAM | grep __vars | grep _mystring | ${FAIL_IF_EMPTY}
-       size -l main.preload | grep __ROM  | ${PASS_IFF_STDIN}
+       size -m -l main.preload | grep __ROM  | ${PASS_IFF_STDIN}
 
 
 
index 2f3286c869bee244c6509380ef471efa63a03f49..c3cc884fe7a52b908e125daa17c7c18530aaa2a6 100644 (file)
@@ -39,13 +39,13 @@ all:
                -Wl,-rename_segment,__DATA,__RAM \
                -Wl,-rename_section,__DATA,__data_extra,__RAM2,__data \
                -Wl,-exported_symbol,_get
-       size -l main.preload | grep __TEXT  | ${FAIL_IF_STDIN}
-       size -l main.preload | grep __DATA  | ${FAIL_IF_STDIN}
+       size -m -l main.preload | grep __TEXT  | ${FAIL_IF_STDIN}
+       size -m -l main.preload | grep __DATA  | ${FAIL_IF_STDIN}
        nm -m main.preload  | grep __ROM | grep __text | grep _entry | ${FAIL_IF_EMPTY}
        nm -m main.preload  | grep __ROM | grep __text | grep _get | ${FAIL_IF_EMPTY}
        nm -m main.preload  | grep __RAM | grep __data | grep _mystring | ${FAIL_IF_EMPTY}
        nm -m main.preload  | grep __RAM2 | grep __data | grep _param | ${FAIL_IF_EMPTY}
-       size -l main.preload | grep __ROM  | ${PASS_IFF_STDIN}
+       size -m -l main.preload | grep __ROM  | ${PASS_IFF_STDIN}
 
 
 clean:
index bec9e7662a03daa53cecb12c61aed75e7e1f8793..9841cf3576d867dbd18c4373aa6aa42e85f072d2 100755 (executable)
@@ -31,9 +31,9 @@ run: all
 
 all:
        ${CC} ${CCFLAGS} main.c -o main -Wl,-merge_zero_fill_sections 
-       size -l main | grep __bss  | ${FAIL_IF_STDIN}
-       size -l main | grep __common | ${FAIL_IF_STDIN}
-       size -l main | grep __zerofill | grep "offset 0" | ${FAIL_IF_EMPTY}
+       size -m -l main | grep __bss  | ${FAIL_IF_STDIN}
+       size -m -l main | grep __common | ${FAIL_IF_STDIN}
+       size -m -l main | grep __zerofill | grep "offset 0" | ${FAIL_IF_EMPTY}
        ${PASS_IFF_GOOD_MACHO} main
 
 clean:
index 8db13a720f3106beb81d02c29475ae85af528bf7..c876c382da65d703ce89f95eecf130a8e7782766 100755 (executable)
@@ -31,8 +31,8 @@ run: all
 
 all:
        ${CC} ${CCFLAGS} main.c -o main -Wl,-no_zero_fill_sections 
-       size -l main | grep __bss | grep "offset 0" | ${FAIL_IF_STDIN}
-       size -l main | grep __common | grep "offset 0" | ${FAIL_IF_STDIN}
+       size -m -l main | grep __bss | grep "offset 0" | ${FAIL_IF_STDIN}
+       size -m -l main | grep __common | grep "offset 0" | ${FAIL_IF_STDIN}
        ${PASS_IFF_GOOD_MACHO} main
 
 clean:
index d563cd969f67f50949605fb7f5d11fb65a002f8b..464011268a65e1db638dcdcc8c507af60a103d95 100644 (file)
@@ -39,11 +39,10 @@ all:
 
 all-i386:
        ${CC} ${CCFLAGS} test.m -framework Foundation -o test1 -Wno-objc-root-class
-       size -l test1 | grep __image_info | ${FAIL_IF_EMPTY}
+       size -m -l test1 | grep __image_info | ${FAIL_IF_EMPTY}
        ${CC} ${CCFLAGS} test.m -Wno-objc-root-class -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -framework Foundation -o test2
-       size -l test2 | grep __objc_imageinfo | ${FAIL_IF_EMPTY}
+       size -m -l test2 | grep __objc_imageinfo | ${FAIL_IF_EMPTY}
        ${PASS_IFF_GOOD_MACHO} test2
-       
-       
+
 clean:
        rm -rf test1 test2
diff --git a/unit-tests/test-cases/objc-category-class-property-mismatch/Makefile b/unit-tests/test-cases/objc-category-class-property-mismatch/Makefile
new file mode 100644 (file)
index 0000000..54697fb
--- /dev/null
@@ -0,0 +1,132 @@
+##
+# Copyright (c) 2010 Apple 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
+
+#
+# Verify handling of object files with mismatched category class properties.
+#
+
+LD_R = ${LD} -arch ${ARCH} -r
+
+# check output of objcimageinfo
+FAIL_IF_OLD = grep -v has-category-class-properties | ${FAIL_IF_STDIN}
+FAIL_IF_NEW = grep has-category-class-properties | ${FAIL_IF_STDIN}
+REALLY_FAIL_IF_NEW = grep has-category-class-properties | ${FAIL_IF_STDIN}
+
+# check diagnostics from ld
+FAIL_IF_DIAGNOSTICS = ${FAIL_IF_STDIN}
+FAIL_IF_NO_DIAGNOSTICS = egrep 'warning.*category metadata' | ${FAIL_IF_EMPTY}
+
+# Hack: Old ABI doesn't need the has-category-class-properties bit. 
+# There should instead be no diagnostics and no "OLD" output anywhere.
+ifeq (${ARCH},i386)
+       FAIL_IF_NEW = ${FAIL_IF_OLD}
+       FAIL_IF_NO_DIAGNOSTICS = ${FAIL_IF_DIAGNOSTICS}
+       # REALLY_FAIL_IF_NEW is unchanged to sanity-check objcimageinfo itself.
+endif
+
+all:
+       # Generate files with has-category-class-properties bit ("NEW").
+       ${CC} ${CCFLAGS} -c class.m -o class.o
+       ${CC} ${CCFLAGS} -c -DCATEGORY=1 -DCLASS_PROPERTY=1 cat.m -o cat-with-class-prop.o
+       ${CC} ${CCFLAGS} -c -DCATEGORY=1 -DCLASS_PROPERTY=0 cat.m -o cat-without-class-prop.o
+       ${CC} ${CCFLAGS} -c -DCATEGORY=0 -DCLASS_PROPERTY=0 cat.m -o nocat.o
+       ${CC} ${CCFLAGS} -c -DCATEGORY=0 -DCLASS_PROPERTY=0 -x c cat.m -o noobjc.o
+       ${OBJCIMAGEINFO} class.o cat-with-class-prop.o cat-without-class-prop.o nocat.o | ${FAIL_IF_OLD}
+       # nocat.o must have objc_image_info; noobjc.o must not
+       size -m -l nocat.o | egrep '(__image_info|__objc_imageinfo)' | ${FAIL_IF_EMPTY}
+       size -m -l noobjc.o | egrep '(__image_info|__objc_imageinfo)' | ${FAIL_IF_STDIN}
+
+       # Generate files without has-category-class-properties bit ("OLD").
+       cp -f cat-without-class-prop.o old-cat.o
+       cp -f nocat.o old-nocat.o
+       cp -f class.o old-class.o
+       ${OBJCIMAGEINFO} -has-category-class-properties old-cat.o old-class.o old-nocat.o | ${REALLY_FAIL_IF_NEW}
+
+       # Link each pair of class and category.
+       # Verify diagnostics and output's has-category-class-properties bit.
+
+       ############
+       # Class: NEW
+
+       # Category: not OLD. No diagnostics. Result should be marked NEW.
+       # Category: OLD but no category. No diagnostics. Result should be marked NEW.
+       ${LD_R} class.o cat-with-class-prop.o -o class__cat-with-class-prop.o 2>&1 | ${FAIL_IF_DIAGNOSTICS}
+       ${LD_R} class.o cat-without-class-prop.o -o class__cat-without-class-prop.o 2>&1 | ${FAIL_IF_DIAGNOSTICS}
+       ${LD_R} class.o nocat.o -o class__nocat.o 2>&1 | ${FAIL_IF_DIAGNOSTICS}
+       ${LD_R} class.o noobjc.o -o class__noobjc.o 2>&1 | ${FAIL_IF_DIAGNOSTICS}
+       ${LD_R} class.o old-nocat.o -o class__old-nocat.o 2>&1 | ${FAIL_IF_DIAGNOSTICS}
+       ${OBJCIMAGEINFO} class__cat-with-class-prop.o | ${FAIL_IF_OLD}
+       ${OBJCIMAGEINFO} class__cat-without-class-prop.o | ${FAIL_IF_OLD}
+       ${OBJCIMAGEINFO} class__nocat.o | ${FAIL_IF_OLD}
+       ${OBJCIMAGEINFO} class__noobjc.o | ${FAIL_IF_OLD}
+       ${OBJCIMAGEINFO} class__old-nocat.o | ${FAIL_IF_OLD}
+
+       # Category: OLD. No diagnostics. Result should be marked OLD.
+       ${LD_R} class.o old-cat.o -o class__old-cat.o 2>&1 | ${FAIL_IF_DIAGNOSTICS}
+       ${OBJCIMAGEINFO} class__old-cat.o | ${FAIL_IF_NEW}
+
+       # Two categories: OLD + NEW with class properties. Has diagnostics. Result should be marked OLD.
+       ${LD_R} class.o old-cat.o cat-with-class-prop.o -o class__old-cat__cat-with-class-prop.o 2>&1 | ${FAIL_IF_NO_DIAGNOSTICS}
+       ${OBJCIMAGEINFO} class__old-cat__cat-with-class-prop.o | ${FAIL_IF_NEW}
+
+       # Two categories: OLD + NEW without class properties. No diagnostics. Result should be marked OLD.
+       ${LD_R} class.o old-cat.o cat-without-class-prop.o -o class__old-cat__cat-without-class-prop.o 2>&1 | ${FAIL_IF_DIAGNOSTICS}
+       ${OBJCIMAGEINFO} class__old-cat__cat-without-class-prop.o | ${FAIL_IF_NEW}
+
+
+       ############
+       # Class: OLD
+
+       # Category: OLD. No diagnostics. Result should be marked OLD.
+       ${LD_R} old-class.o old-cat.o -o old-class__old-cat.o 2>&1 | ${FAIL_IF_DIAGNOSTICS}
+       ${OBJCIMAGEINFO} old-class__old-cat.o | ${FAIL_IF_NEW}
+
+       # Category: none. No diagnostics. Result should be marked NEW.
+       ${LD_R} old-class.o nocat.o -o old-class__nocat.o 2>&1 | ${FAIL_IF_DIAGNOSTICS}
+       ${LD_R} old-class.o noobjc.o -o old-class__noobjc.o 2>&1 | ${FAIL_IF_DIAGNOSTICS}
+       ${LD_R} old-class.o old-nocat.o -o old-class__old-nocat.o 2>&1 | ${FAIL_IF_DIAGNOSTICS}
+       ${OBJCIMAGEINFO} old-class__nocat.o | ${FAIL_IF_OLD}
+       ${OBJCIMAGEINFO} old-class__noobjc.o | ${FAIL_IF_OLD}
+       ${OBJCIMAGEINFO} old-class__old-nocat.o | ${FAIL_IF_OLD}
+
+       # Category: NEW. No diagnostics. Result should be marked NEW.
+       ${LD_R} old-class.o cat-without-class-prop.o -o old-class__cat-without-class-prop.o 2>&1 | ${FAIL_IF_DIAGNOSTICS}
+       ${LD_R} old-class.o cat-with-class-prop.o -o old-class__cat-with-class-prop.o 2>&1 | ${FAIL_IF_DIAGNOSTICS}
+       ${OBJCIMAGEINFO} old-class__cat-without-class-prop.o | ${FAIL_IF_OLD}
+       ${OBJCIMAGEINFO} old-class__cat-with-class-prop.o | ${FAIL_IF_OLD}
+
+       # Two categories: OLD + NEW with class properties. Has diagnostics. Result should be marked OLD.
+       ${LD_R} old-class.o old-cat.o cat-with-class-prop.o -o old-class__old-cat__cat-with-class-prop.o 2>&1 | ${FAIL_IF_NO_DIAGNOSTICS}
+       ${OBJCIMAGEINFO} old-class__old-cat__cat-with-class-prop.o | ${FAIL_IF_NEW}
+
+       # Two categories: OLD + NEW without class properties. No diagnostics. Result should be marked OLD.
+       ${LD_R} old-class.o old-cat.o cat-without-class-prop.o -o old-class__old-cat__cat-without-class-prop.o 2>&1 | ${FAIL_IF_DIAGNOSTICS}
+       ${OBJCIMAGEINFO} old-class__old-cat__cat-without-class-prop.o | ${FAIL_IF_NEW}
+
+       ${PASS_IFF} true
+
+clean:
+       rm -rf *.o
diff --git a/unit-tests/test-cases/objc-category-class-property-mismatch/cat.m b/unit-tests/test-cases/objc-category-class-property-mismatch/cat.m
new file mode 100644 (file)
index 0000000..54aa451
--- /dev/null
@@ -0,0 +1,24 @@
+#if CATEGORY
+
+#include <Foundation/Foundation.h>
+
+@interface Foo : NSObject
+@end
+
+@interface Foo(mycat)
+@property(readonly) int instanceProperty;
+#if CLASS_PROPERTY
+@property(class, readonly) int classProperty;
+#endif
+@end
+
+@implementation Foo(mycat)
+-(int) instanceProperty { return 0; }
++(int) classProperty { return 0; }
+@end
+
+#else
+
+int x = 0; 
+
+#endif
diff --git a/unit-tests/test-cases/objc-category-class-property-mismatch/class.m b/unit-tests/test-cases/objc-category-class-property-mismatch/class.m
new file mode 100644 (file)
index 0000000..065450c
--- /dev/null
@@ -0,0 +1,9 @@
+#include <Foundation/Foundation.h>
+
+@interface Foo : NSObject
+@end
+
+
+@implementation Foo
+@end
+
index be39fa715b90df489d7af1b40dbaed54625887d3..cfd2cc31e74a4edb6e87dad3129728ded63962b9 100644 (file)
@@ -26,28 +26,24 @@ include ${TESTROOT}/include/common.makefile
 #
 # Verify optimization where categories are merged into classes
 #
-OPTIONS = 
-
 ifeq ($(ARCH),i386)
-       OPTIONS = -mios-simulator-version-min=6.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/
+       ALL = all-noopt
+else
+       ALL = all-opt
 endif
 
-all:   all-${ARCH}
+all: ${ALL}
 
-all-ppc:
+# For platforms that do not perform category optimization
+all-noopt:
        ${PASS_IFF} true
 
-all-i386: all-rest
-all-x86_64: all-rest
-all-armv6: all-rest
-all-armv7: all-rest
-
-all-rest:
+# For platforms that optimize categories
+all-opt:
        # check optimization of category methods
        ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m  -framework Foundation -o libfoo.dylib
-       size -l libfoo.dylib | grep "__objc_catlist:" | ${FAIL_IF_EMPTY}
+       size -m -l libfoo.dylib | grep "__objc_catlist:" | ${FAIL_IF_EMPTY}
        ${PASS_IFF_GOOD_MACHO} libfoo.dylib
-       
-       
+
 clean:
        rm -rf libfoo.dylib
index 37c69f137070c671dc7d6f3ecb85c1850c58d779..4783f04046722ea56e747795079e51c17409dea2 100644 (file)
@@ -26,51 +26,43 @@ include ${TESTROOT}/include/common.makefile
 #
 # Verify optimization where categories are merged into classes
 #
-OPTIONS = 
 
 ifeq ($(ARCH),i386)
-       OPTIONS = -mios-simulator-version-min=6.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/
+       ALL = all-noopt
+else
+       ALL = all-opt
 endif
 
-ifeq ($(ARCH),arm)
-       OPTIONS = -isysroot=${IOS_SDK}
-endif
-
-all:   all-${ARCH}
+all: ${ALL}
 
-all-ppc:
+# For platforms that do not perform category optimization
+all-noopt:
        ${PASS_IFF} true
 
-all-i386: all-rest
-all-x86_64: all-rest
-all-armv6: all-rest
-all-armv7: all-rest
-
-all-rest:
+# For platforms that optimize categories
+all-opt:
        # check optimization can be turned off
        ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -framework Foundation -Wl,-no_objc_category_merging -o libno.dylib
-       size -l libno.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_STDIN}
+       size -m -l libno.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_STDIN}
        otool -ov libno.dylib | grep -A17 __objc_classlist | grep -A16 '_OBJC_CLASS_$$_Foo' | grep "count 1" | ${FAIL_IF_EMPTY}
        # check optimization of category methods
        ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -framework Foundation -o libfoo.dylib
-       size -l libfoo.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY}
+       size -m -l libfoo.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY}
        otool -ov libfoo.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 4" | ${FAIL_IF_EMPTY}
        # check optimization of protocol and category methods
        ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DPROTOCOLS -framework Foundation -o libfoo2.dylib
-       size -l libfoo2.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY}
+       size -m -l libfoo2.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY}
        otool -ov libfoo2.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 6" | ${FAIL_IF_EMPTY}
        # check optimization of properties and category methods
        ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DPROPERTIES -framework Foundation -o libfoo3.dylib
-       size -l libfoo3.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY}
+       size -m -l libfoo3.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY}
        otool -ov libfoo3.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 6" | ${FAIL_IF_EMPTY}
        # check optimization of category methods and no base methods
        ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DNO_BASE_METHODS -framework Foundation -o libfoo4.dylib
-       size -l libfoo4.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY}
+       size -m -l libfoo4.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY}
        otool -ov libfoo4.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 3" | ${FAIL_IF_EMPTY}
        otool -ov libfoo4.dylib | grep -A20 "Meta Class" | grep "count 2" | ${FAIL_IF_EMPTY}
        ${PASS_IFF_GOOD_MACHO} libfoo3.dylib
-       
-       
-       
+
 clean:
        rm -rf  lib*.dylib
index 11c170b656d1a58e0cdf1632d7f2eb72ab2c0751..3652fca8372140c5db6ffde1b05959ea5f635bf0 100644 (file)
@@ -14,6 +14,8 @@
 #if PROPERTIES
        @property(readonly) int property1;
        @property(readonly) int property2;
+       @property(class,readonly) int property3;
+       @property(class,readonly) int property4;
 #endif
 @end
 
@@ -24,6 +26,8 @@
 #if PROPERTIES
        -(int) property1 { return 0; }
        -(int) property2 { return 0; }
+       +(int) property3 { return 0; }
+       +(int) property4 { return 0; }
 #endif
 @end
 
index 31d841b40fb0d0eb471d606ffe68a6400e3e9580..e1835a0db7a855c4b2d23ca465525d9842c287a6 100644 (file)
@@ -27,22 +27,20 @@ SHELL = bash # use bash shell so we can redirect just stderr
 #
 # Verify optimization in which categories are merged into classes
 #
-OPTIONS = 
-
 ifeq ($(ARCH),i386)
-       OPTIONS = -mios-simulator-version-min=6.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/
+       ALL = all-noopt
+else
+       ALL = all-opt
 endif
 
-all:   all-${FILEARCH}
+all: ${ALL}
 
-all-ppc:
+# For platforms that do not perform category optimization
+all-noopt:
        ${PASS_IFF} true
 
-all-i386: all-rest
-all-x86_64: all-rest
-all-arm: all-rest
-
-all-rest:
+# For platforms that optimize categories
+all-opt:
        ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat.m -DOVERRIDE_CLASS=1 -framework Foundation -o libfoo.dylib 2> warnings1.txt
        grep warning warnings1.txt | grep instance_method | ${FAIL_IF_EMPTY}
        grep warning warnings1.txt | grep class_method | ${FAIL_IF_EMPTY}
@@ -50,6 +48,6 @@ all-rest:
        grep warning warnings2.txt | grep instance_method_fromcat | ${FAIL_IF_EMPTY}
        grep warning warnings2.txt | grep class_method_fromcat | ${FAIL_IF_EMPTY}
        ${PASS_IFF_GOOD_MACHO} libfoo2.dylib
-       
+
 clean:
        rm -rf  lib*.dylib warnings*.txt
index 52840c9303087365619fe9bde771bfea3f69b0c3..0dda0e2b273590be2d20e22c7b8ddced0a79eb95 100644 (file)
@@ -26,12 +26,9 @@ include ${TESTROOT}/include/common.makefile
 #
 # Verify -alias works with ObjC classes
 #
-CLASS_NAME_FOO = .objc_class_name_Foo
-ifeq (${ARCH},x86_64)
-       CLASS_NAME_FOO = '_OBJC_CLASS_$$_Foo'
-endif
-ifeq (${FILEARCH},arm)
-       CLASS_NAME_FOO = '_OBJC_CLASS_$$_Foo'
+CLASS_NAME_FOO = '_OBJC_CLASS_$$_Foo'
+ifeq (${ARCH},i386)
+       CLASS_NAME_FOO = .objc_class_name_Foo
 endif
 
 
@@ -42,6 +39,6 @@ all:
        ${DYLDINFO} -export test | grep ${CLASS_NAME_FOO} | awk '{ print $$1}' > foo.addr
        ${DYLDINFO} -export test | grep _MagicName | awk '{ print $$1}' > magic.addr
        ${PASS_IFF} diff foo.addr magic.addr 
-       
+
 clean:
        rm -rf test foo.addr  magic.addr
index 13d17432bfb0e2c395624c5393be59d13916010d..426d9b212ef074d9a5cc4c773d81263886eb521d 100644 (file)
@@ -25,42 +25,53 @@ include ${TESTROOT}/include/common.makefile
 
 SHELL = bash # use bash shell so we can redirect just stderr
 
+#
+# Validate that the linker catches illegal combinations of .o files 
+# compiled with different GC settings. 
+#
 
-IMAGE_INFO = "__image_info"
+IMAGE_INFO = "__objc_imageinfo"
+ifeq ($(ARCH),i386)
+       IMAGE_INFO = "__image_info"
+endif
 
+ifeq ($(ARCH),i386)
+       ALL = all-gc
+else
 ifeq ($(ARCH),x86_64)
-       IMAGE_INFO = "__objc_imageinfo"
+       ALL = all-gc
+else
+       ALL = all-nogc
+endif
 endif
 
-test: test-${FILEARCH}
-
-test-i386: test-macosx
-test-x86_64: test-macosx
-test-arm:   test-good
+all: ${ALL}
 
-#
-# Validate that the linker catches illegal combinations of .o files 
-# compiled with different GC settings. 
-#
+# For platforms that do not support GC.
+all-nogc:
+       ${PASS_IFF} true
 
-test-macosx:
+# For platforms that support GC.
+all-gc:
        ${CC} ${CCFLAGS} foo.m -c -o foo.o
        ${FAIL_IF_BAD_OBJ} foo.o
 
-       ${CC} ${CCFLAGS} foo.m -c -o foo-gc.o -fobjc-gc
-       ${FAIL_IF_BAD_OBJ} foo-gc.o
-
-       ${CC} ${CCFLAGS} foo.m -c -o foo-gc-only.o -fobjc-gc-only
-       ${FAIL_IF_BAD_OBJ} foo-gc-only.o
-
        ${CC} ${CCFLAGS} bar.m -c -o bar.o
        ${FAIL_IF_BAD_OBJ} bar.o
 
-       ${CC} ${CCFLAGS} bar.m -c -o bar-gc.o -fobjc-gc
-       ${FAIL_IF_BAD_OBJ} bar-gc.o
+       # clang no longer builds GC so we create fake GC object files instead.
+
+       cp -f foo.o foo-gc.o
+       ${OBJCIMAGEINFO} +supports-gc foo-gc.o
 
-       ${CC} ${CCFLAGS} bar.m -c -o bar-gc-only.o -fobjc-gc-only
-       ${FAIL_IF_BAD_OBJ} bar-gc-only.o
+       cp -f foo.o foo-gc-only.o
+       ${OBJCIMAGEINFO} +supports-gc +requires-gc foo-gc-only.o
+
+       cp -f bar.o bar-gc.o
+       ${OBJCIMAGEINFO} +supports-gc bar-gc.o
+
+       cp -f bar.o bar-gc-only.o
+       ${OBJCIMAGEINFO} +supports-gc +requires-gc bar-gc-only.o
 
        # check RR + RR -> RR
        ${CC} ${CCFLAGS} foo.o bar.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation
@@ -69,56 +80,56 @@ test-macosx:
        # check GC/RR + GC/RR -> GC/RR
        ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib  -framework Foundation
        ${FAIL_IF_BAD_MACHO} libfoobar.dylib
-       otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x2  | ${FAIL_IF_EMPTY}
+       ${OBJCIMAGEINFO} libfoobar.dylib | grep supports-gc | ${FAIL_IF_EMPTY}
 
        # check GC + GC -> GC
        ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib  -framework Foundation
        ${FAIL_IF_BAD_MACHO} libfoobar.dylib
-       otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x6  | ${FAIL_IF_EMPTY}
+       ${OBJCIMAGEINFO} libfoobar.dylib | grep 'supports-gc requires-gc' | ${FAIL_IF_EMPTY}
 
        # check RR + GC/RR -> RR
        ${CC} ${CCFLAGS} foo.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib  -framework Foundation
        ${FAIL_IF_BAD_MACHO} libfoobar.dylib
-       otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x[26]  | ${FAIL_IF_STDIN}
+       ${OBJCIMAGEINFO} libfoobar.dylib | grep gc | ${FAIL_IF_STDIN}
 
        # check GC/RR + RR -> RR
        ${CC} ${CCFLAGS} bar-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib  -framework Foundation
        ${FAIL_IF_BAD_MACHO} libfoobar.dylib
-       otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x[26]  | ${FAIL_IF_STDIN}
+       ${OBJCIMAGEINFO} libfoobar.dylib | grep gc | ${FAIL_IF_STDIN}
 
        # check GC + GC/RR -> GC
        ${CC} ${CCFLAGS} foo-gc-only.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib   -framework Foundation
        ${FAIL_IF_BAD_MACHO} libfoobar.dylib
-       otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x6  | ${FAIL_IF_EMPTY}
+       ${OBJCIMAGEINFO} libfoobar.dylib | grep 'supports-gc requires-gc' | ${FAIL_IF_EMPTY}
 
        # check RR + GC -> error
        ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib  -framework Foundation 2> fail.log
 
        # check cmd line GC/RR, GC/RR + RR -> error
        ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib -Wl,-objc_gc  -framework Foundation 2> fail.log
-       
+
        # check GC/RR + compaction
        ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib  -framework Foundation
-       otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x12  | ${FAIL_IF_EMPTY}
+       ${OBJCIMAGEINFO} libfoobar.dylib | grep 0x12 | ${FAIL_IF_EMPTY}
 
        # check GC + compaction
        ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib  -framework Foundation
-       otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x16  | ${FAIL_IF_EMPTY}
+       ${OBJCIMAGEINFO} libfoobar.dylib | grep 0x16 | ${FAIL_IF_EMPTY}
 
        # none + GC/RR-dylib -> none
        ${CC} ${CCFLAGS} foo-gc.o runtime.c -dynamiclib -o libfoo.dylib  -framework Foundation
        ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation
-       size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN}
+       size -m -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN}
 
        # none + GC-dylib -> none
        ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib  -framework Foundation
        ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation
-       size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN}
+       size -m -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN}
 
        # none + RR-dylib -> none
        ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib  -framework Foundation
        ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation
-       size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN}
+       size -m -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN}
 
        # check RR + GC-dylib -> error
        ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib  -framework Foundation
@@ -130,8 +141,5 @@ test-macosx:
 
        ${PASS_IFF} true
 
-test-good:
-       ${PASS_IFF} true
-
 clean:
        rm -rf foo*.o bar*.o libfoobar.dylib fail.log libfoo.dylib libnone.dylib
index 76a7347a631ab4554eabcf359c7df4a2210ac6d1..e51a63a85776d2976aced0987f2b1e99245581b2 100644 (file)
@@ -31,13 +31,10 @@ include ${TESTROOT}/include/common.makefile
 #
 #
 
-SELECTOR_REFS = "__OBJC,__message_refs"
+SELECTOR_REFS = "__DATA,__objc_selrefs"
 
-ifeq ($(ARCH),x86_64)
-       SELECTOR_REFS = "__DATA,__objc_selrefs"
-endif
-ifeq ($(ARCH),armv6)
-       SELECTOR_REFS = "__DATA,__objc_selrefs"
+ifeq ($(ARCH),i386)
+       SELECTOR_REFS = "__OBJC,__message_refs"
 endif
 
 
@@ -52,6 +49,6 @@ all:
        ${OBJECTDUMP}  -no_content test-r.o | grep -B3 -A6 ${SELECTOR_REFS}  > test-r.dump
 
        diff test.dump test-r.dump | ${PASS_IFF_EMPTY}
-       
+
 clean:
        rm -rf test.o test.dump test-r.o test-r.dump
index 2560a869e40b01051e97ff5fa3043e1d819fd05a..0e4d720af0ec1c6ad8b2430591a754ff6e24690a 100644 (file)
@@ -31,12 +31,14 @@ include ${TESTROOT}/include/common.makefile
 run: all
 
 all:
-       mkdir -p hide
+       mkdir -p hide rhide
        ${CC} ${CCFLAGS} -dynamiclib foo.c -install_name '@loader_path/libfoo.dylib' -o hide/libfoo.dylib 
        ${FAIL_IF_BAD_MACHO} hide/libfoo.dylib
        ${CC} ${CCFLAGS} -dynamiclib bar.c -o hide/libbar.dylib -install_name '@executable_path/hide/libbar.dylib'
        ${FAIL_IF_BAD_MACHO} hide/libbar.dylib
-       ${CC} ${CCFLAGS} -dynamiclib wrap.c -o hide/libwrap.dylib -Wl,-reexport-lfoo -Wl,-reexport-lbar -Lhide
+       ${CC} ${CCFLAGS} -dynamiclib baz.c -o rhide/libbaz.dylib -install_name '@rpath/libbaz.dylib'
+       ${FAIL_IF_BAD_MACHO} hide/libbar.dylib
+       ${CC} ${CCFLAGS} -dynamiclib wrap.c -o hide/libwrap.dylib -Wl,-reexport-lfoo -Wl,-reexport-lbar -Lhide -Wl,-rpath,@loader_path/../rhide -Wl,-reexport-lbaz -Lrhide
        ${FAIL_IF_BAD_MACHO} hide/libwrap.dylib
        ${CC} ${CCFLAGS} main.c -o main hide/libwrap.dylib
        ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib hide/libwrap.dylib -Wl,-executable_path,`pwd`/main
@@ -46,4 +48,4 @@ all:
 
 clean:
 
-       rm -rf hide libbar.dylib libfoo.dylib libwrap.dylib main libmain.dylib
+       rm -rf hide rhide main libmain.dylib
diff --git a/unit-tests/test-cases/re-export-relative-paths/baz.c b/unit-tests/test-cases/re-export-relative-paths/baz.c
new file mode 100644 (file)
index 0000000..5b832e8
--- /dev/null
@@ -0,0 +1,5 @@
+
+int baz(void)
+{
+       return 1;
+}
index 5b1f246bd7463be8dcc21c6980e5b50d5b99bc59..08613eda8be8cf6e7555500d4584c5165fa2870c 100644 (file)
@@ -32,8 +32,8 @@ run: all
 
 all:
        ${CC} ${CCFLAGS} main.c -o main -sectcreate __MYSEG __mysect sect_content -dead_strip
-       size -l main | grep __MYSEG | ${FAIL_IF_EMPTY}
-       size -l main | grep __mysect | ${FAIL_IF_EMPTY}
+       size -m -l main | grep __MYSEG | ${FAIL_IF_EMPTY}
+       size -m -l main | grep __mysect | ${FAIL_IF_EMPTY}
        ${PASS_IFF_GOOD_MACHO} main
 
 clean:
index e9c09c437b274b44402739a7419bbc772344453f..9bd440539d434ea4adcd950fd10404e9fee24479 100644 (file)
@@ -33,9 +33,9 @@ run: all
 
 all:
        ${CC} ${CCFLAGS} hello.c -o hello
-       size -l hello | grep __IMPORT | ${FAIL_IF_STDIN} 
+       size -m -l hello | grep __IMPORT | ${FAIL_IF_STDIN} 
        ${CC} ${CCFLAGS} hello.c -dynamiclib -o libhello.dylib 
-       size -l libhello.dylib | grep __IMPORT | ${FAIL_IF_STDIN}
+       size -m -l libhello.dylib | grep __IMPORT | ${FAIL_IF_STDIN}
        ${PASS_IFF_GOOD_MACHO} hello
 
 clean:
index fcb7e7d0f87d8caa12c68f7acf5a6b75f241b35b..2288cf9826b9b96b5f8fa304271c0b448fba4f39 100644 (file)
@@ -47,8 +47,8 @@ all:
        ${CC} ${CCFLAGS} all.o  -dynamiclib -o dylib2   
        otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib1 >dylib1.pointers
        otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib2 >dylib2.pointers
-       size -l dylib1 | grep __IMPORT | ${FAIL_IF_STDIN}
-       size -l dylib2 | grep __IMPORT | ${FAIL_IF_STDIN}
+       size -m -l dylib1 | grep __IMPORT | ${FAIL_IF_STDIN}
+       size -m -l dylib2 | grep __IMPORT | ${FAIL_IF_STDIN}
        ${PASS_IFF} diff dylib1.pointers dylib2.pointers
 
 clean:
diff --git a/unit-tests/test-cases/symbol-move-and-section-rename/Makefile b/unit-tests/test-cases/symbol-move-and-section-rename/Makefile
new file mode 100644 (file)
index 0000000..6a5acbf
--- /dev/null
@@ -0,0 +1,57 @@
+##
+# Copyright (c) 2016 Apple 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
+
+#
+# Check interaction of -section_rename and -move_to_r[ow]_segment
+#
+
+run: all
+
+all:
+       ${CC} ${CCFLAGS} main.c -c -o main.o
+       ${CC} ${CCFLAGS} foo1.c -c -o foo1.o
+       ${CC} ${CCFLAGS} foo2.c -c -o foo2.o
+       libtool -static foo1.o foo2.o -o libfoo.a
+       ${LD} -arch ${ARCH} main.o -force_load libfoo.a \
+        -preload -o main.preload \
+               -e _main -trace_symbol_layout \
+               -move_to_ro_segment __TMP spec.list \
+               -rename_section __TMP __text __TEXT spec \
+               -move_to_ro_segment __TMP2 foo.list \
+               -rename_section __TMP2 __text __TEXT libfoo \
+               -move_to_rw_segment __TMPD hot.list \
+               -rename_section __TMPD __data __DATA hot \
+               -rename_section __TMPD __common __DATA hot \
+        > placement.log
+       nm -m main.preload | grep _s1   | grep __TEXT | grep spec   | ${FAIL_IF_EMPTY}
+       nm -m main.preload | grep _s2   | grep __TEXT | grep spec   | ${FAIL_IF_EMPTY}
+       nm -m main.preload | grep _def  | grep __DATA | grep hot    | ${FAIL_IF_EMPTY}
+       nm -m main.preload | grep _com3 | grep __DATA | grep hot    | ${FAIL_IF_EMPTY}
+       nm -m main.preload | grep _foo1 | grep __TEXT | grep libfoo | ${FAIL_IF_EMPTY}
+       nm -m main.preload | grep _foo2 | grep __TEXT | grep libfoo | ${FAIL_IF_EMPTY}
+       ${PASS_IFF} true
+
+clean:
+       rm  -f main.preload main.o foo1.o foo2.o libfoo.a placement.log
diff --git a/unit-tests/test-cases/symbol-move-and-section-rename/foo.list b/unit-tests/test-cases/symbol-move-and-section-rename/foo.list
new file mode 100644 (file)
index 0000000..28656a5
--- /dev/null
@@ -0,0 +1,2 @@
+libfoo.a(*.o):*
+
diff --git a/unit-tests/test-cases/symbol-move-and-section-rename/foo1.c b/unit-tests/test-cases/symbol-move-and-section-rename/foo1.c
new file mode 100644 (file)
index 0000000..c6d4367
--- /dev/null
@@ -0,0 +1 @@
+void foo1() {}
diff --git a/unit-tests/test-cases/symbol-move-and-section-rename/foo2.c b/unit-tests/test-cases/symbol-move-and-section-rename/foo2.c
new file mode 100644 (file)
index 0000000..a5b7316
--- /dev/null
@@ -0,0 +1 @@
+void foo2() {}
diff --git a/unit-tests/test-cases/symbol-move-and-section-rename/hot.list b/unit-tests/test-cases/symbol-move-and-section-rename/hot.list
new file mode 100644 (file)
index 0000000..704da52
--- /dev/null
@@ -0,0 +1,4 @@
+_def
+_com3
+
+
diff --git a/unit-tests/test-cases/symbol-move-and-section-rename/main.c b/unit-tests/test-cases/symbol-move-and-section-rename/main.c
new file mode 100644 (file)
index 0000000..9b080ab
--- /dev/null
@@ -0,0 +1,30 @@
+
+void mm() 
+{
+}
+
+void s1() {
+  mm();
+}
+
+void s2() {
+  mm();
+}
+
+int main()
+{
+    s1();
+    s2();
+       return 0;
+}
+
+
+int abc = 10;
+int def = 20;
+int ghi = 30;
+
+int com1;
+int com2;
+int com3;
+int com4;
+
diff --git a/unit-tests/test-cases/symbol-move-and-section-rename/spec.list b/unit-tests/test-cases/symbol-move-and-section-rename/spec.list
new file mode 100644 (file)
index 0000000..b07733b
--- /dev/null
@@ -0,0 +1,4 @@
+_s1
+_s2
+
+
index 32a24b412e2067cc9f08637105e98f2f8bc3c97b..1bc26cccc78a9af82bedf2908b07c54299edb405 100644 (file)
@@ -44,14 +44,14 @@ all:
        nm -m main.preload | grep _foo | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY}
        nm -m main.preload | grep _s1  | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY}
        nm -m main.preload | grep _mylocal | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY}
-       size -l main.preload | grep __cstring |  ${FAIL_IF_STDIN}
-       size -l main.preload | grep mycstrings |  ${FAIL_IF_EMPTY}
-       size -l main.preload | grep __TEXT |  ${FAIL_IF_STDIN}
+       size -m -l main.preload | grep __cstring |  ${FAIL_IF_STDIN}
+       size -m -l main.preload | grep mycstrings |  ${FAIL_IF_EMPTY}
+       size -m -l main.preload | grep __TEXT |  ${FAIL_IF_STDIN}
        nm -m main.preload | grep _mm | grep __ROM3 | grep __text | ${FAIL_IF_EMPTY}
        nm -m main.preload | grep _main | grep __ROM3 | grep __text | ${FAIL_IF_EMPTY}
        nm -m main.preload | grep _abc | grep __RAM1  | ${FAIL_IF_EMPTY}
        nm -m main.preload | grep _com | grep __RAM1  | ${FAIL_IF_EMPTY}
-       size -l main.preload | grep __DATA |  ${FAIL_IF_STDIN}
+       size -m -l main.preload | grep __DATA |  ${FAIL_IF_STDIN}
        nm -m main.preload | grep _def | grep __RAM2 | grep mydata | ${FAIL_IF_EMPTY}
        nm -m main.preload | grep _ghi | grep __RAM2 | grep mydata | ${FAIL_IF_EMPTY}
        ${PASS_IFF} true
index a82eafb40c7d1077a4cc5d177902c0825592fb2b..034180fa061e17bc24f0b342eac417a8231aca2c 100644 (file)
@@ -1,6 +1,6 @@
 
 // foo is an exported thread local variable
-__thread int foo = 6;
+__thread int foo[1024];
 
 // _bar is an exported regular variable
 int bar = 5;
index 5d0eb88cc5f8d40fb943869499bc4f276d27f302..800356dc1e98dd66edffc42115f667d1e9de0b6f 100644 (file)
  */
 
 #if USE_FOO_WRONG
-       extern int foo;
+       extern int foo[];
 #elif USE_BAR_WRONG
        extern __thread int bar;
 #else
-  extern __thread int foo;
+  extern __thread int foo[];
 #endif
 
 
@@ -38,7 +38,7 @@ int main()
 #elif USE_BAR_WRONG
        bar = 1;
 #else
-       foo = 1;
+       foo[0] = 1;
 #endif
        return 0;
 }
index 3d845e5731814d1148432262de6af4228f951fdf..b032fae698fbbec42f2826af1fc6df9713c35ea2 100644 (file)
@@ -34,7 +34,7 @@ all:
        ${CC} ${CCFLAGS} withnul.s -c -o withnul.o
        ${CC} ${CCFLAGS} other.s -c -o other.o
        ${LD} -r -arch ${ARCH} withnul.o other.o -o all.o
-       size -l all.o | grep "__ustring): 44" | ${PASS_IFF_STDIN}
+       size -m -l all.o | grep "__ustring): 44" | ${PASS_IFF_STDIN}
        
 
 clean:
diff --git a/unit-tests/test-cases/weak_import-disable/Makefile b/unit-tests/test-cases/weak_import-disable/Makefile
new file mode 100644 (file)
index 0000000..3e68e08
--- /dev/null
@@ -0,0 +1,29 @@
+
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Tests -no_weak_imports
+#
+
+
+run: all
+
+all:
+    # check weak_import works and triggers error with -no_weak_imports
+       ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib 
+       ${FAIL_IF_BAD_MACHO} libfoo.dylib
+       ${CC} ${CCFLAGS} main.c libfoo.dylib -o main1a
+       nm -m main1a | grep _foo | grep  weak | ${FAIL_IF_STDIN}
+       ${CC} ${CCFLAGS} main.c libfoo.dylib -o main1b -DMAKE_FOO_WEAK_IMPORT=1
+       nm -m main1b | grep _foo | grep  weak | ${FAIL_IF_EMPTY}
+       ${CC} ${CCFLAGS} main.c libfoo.dylib -o main1c -DMAKE_FOO_WEAK_IMPORT=1 -Wl,-no_weak_imports 2>main1c.errors || true
+       grep 'ld:' main1c.errors | grep _foo | ${FAIL_IF_EMPTY}
+       # check $ld$sxx works with -no_weak_imports
+       ${CC} ${CCFLAGS} main2.c libfoo.dylib -o main2a -Wl,-no_weak_imports
+       ${CC} ${CCFLAGS} main2.c libfoo.dylib -o main2b -Wl,-no_weak_imports -mmacosx-version-min=10.7 2>main2b.errors || true
+       grep _bar main2b.errors | ${FAIL_IF_EMPTY}
+       ${PASS_IFF_GOOD_MACHO} main1a
+       
+clean:
+       rm -rf libfoo.dylib main1a main1b main1c main1c.errors main2a main2b main2b.errors
diff --git a/unit-tests/test-cases/weak_import-disable/foo.c b/unit-tests/test-cases/weak_import-disable/foo.c
new file mode 100644 (file)
index 0000000..260977e
--- /dev/null
@@ -0,0 +1,12 @@
+
+
+
+void foo() {}
+
+
+#define WEAK_IMPORT_FOR_10_7(sym) \
+                 extern const char sym##_tmp __asm("$ld$weak$os10.7$_" #sym ); const char sym##_tmp = 0;
+
+void bar() {}
+WEAK_IMPORT_FOR_10_7(bar)
+
diff --git a/unit-tests/test-cases/weak_import-disable/main.c b/unit-tests/test-cases/weak_import-disable/main.c
new file mode 100644 (file)
index 0000000..c276f1f
--- /dev/null
@@ -0,0 +1,15 @@
+
+extern void foo()
+#if MAKE_FOO_WEAK_IMPORT
+__attribute__((weak_import))
+#endif
+;
+
+
+int main()
+{
+       foo();
+
+    return 0;
+}
+
diff --git a/unit-tests/test-cases/weak_import-disable/main2.c b/unit-tests/test-cases/weak_import-disable/main2.c
new file mode 100644 (file)
index 0000000..d908e87
--- /dev/null
@@ -0,0 +1,12 @@
+
+extern void bar();
+
+
+
+
+int main()
+{
+    bar();
+    return 0;
+}
+
index d16b9d03f185c5e5a521f6e796d77b1f7cdd0194..b38061b1760b64b1616eca46701a4b3f1fdc4f67 100644 (file)
@@ -45,4 +45,4 @@ test-arm:
        ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -DSHRINK=4 test.c --save-temps -o test-${ARCH} 2>/dev/null
 
 clean:
-       rm -rf test-* *.o *.s *.i
+       rm -rf test-* *.o *.s *.i *.bc