From c26469061cdd6e2ef3ada29b1f198253c71e6a29 Mon Sep 17 00:00:00 2001 From: Apple Date: Thu, 14 Apr 2005 21:30:21 +0000 Subject: [PATCH] ld64-21.tar.gz --- APPLE_LICENSE | 367 ++++ doc/man/man1/ld64.1 | 1 + ld64.xcode/project.pbxproj | 423 ++++ src/ExecutableFile.h | 72 + src/MachOAbstraction.h | 1978 ++++++++++++++++++ src/ObjDump.cpp | 331 +++ src/ObjectFile.h | 232 ++ src/Options.cpp | 1174 +++++++++++ src/Options.h | 226 ++ src/Readers/ObjectFileArchiveMachO.cpp | 315 +++ src/Readers/ObjectFileDylibMachO.cpp | 484 +++++ src/Readers/ObjectFileMachO-all.cpp | 119 ++ src/Readers/ObjectFileMachO-all.h | 75 + src/Readers/ObjectFileMachO.cpp | 2273 ++++++++++++++++++++ src/SectCreate.cpp | 156 ++ src/SectCreate.h | 42 + src/Writers/ExecutableFileMachO-all.cpp | 104 + src/Writers/ExecutableFileMachO-all.h | 54 + src/Writers/ExecutableFileMachO.cpp | 2554 +++++++++++++++++++++++ src/ld.cpp | 1304 ++++++++++++ 20 files changed, 12284 insertions(+) create mode 100644 APPLE_LICENSE create mode 100644 doc/man/man1/ld64.1 create mode 100644 ld64.xcode/project.pbxproj create mode 100644 src/ExecutableFile.h create mode 100644 src/MachOAbstraction.h create mode 100644 src/ObjDump.cpp create mode 100644 src/ObjectFile.h create mode 100644 src/Options.cpp create mode 100644 src/Options.h create mode 100644 src/Readers/ObjectFileArchiveMachO.cpp create mode 100644 src/Readers/ObjectFileDylibMachO.cpp create mode 100644 src/Readers/ObjectFileMachO-all.cpp create mode 100644 src/Readers/ObjectFileMachO-all.h create mode 100644 src/Readers/ObjectFileMachO.cpp create mode 100644 src/SectCreate.cpp create mode 100644 src/SectCreate.h create mode 100644 src/Writers/ExecutableFileMachO-all.cpp create mode 100644 src/Writers/ExecutableFileMachO-all.h create mode 100644 src/Writers/ExecutableFileMachO.cpp create mode 100644 src/ld.cpp diff --git a/APPLE_LICENSE b/APPLE_LICENSE new file mode 100644 index 0000000..fe81a60 --- /dev/null +++ b/APPLE_LICENSE @@ -0,0 +1,367 @@ +APPLE PUBLIC SOURCE LICENSE +Version 2.0 - August 6, 2003 + +Please read this License carefully before downloading this software. +By downloading or using this software, you are agreeing to be bound by +the terms of this License. If you do not or cannot agree to the terms +of this License, please do not download or use the software. + +1. General; Definitions. This License applies to any program or other +work which Apple Computer, Inc. ("Apple") makes publicly available and +which contains a notice placed by Apple identifying such program or +work as "Original Code" and stating that it is subject to the terms of +this Apple Public Source License version 2.0 ("License"). As used in +this License: + +1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is +the grantor of rights, (i) claims of patents that are now or hereafter +acquired, owned by or assigned to Apple and (ii) that cover subject +matter contained in the Original Code, but only to the extent +necessary to use, reproduce and/or distribute the Original Code +without infringement; and (b) in the case where You are the grantor of +rights, (i) claims of patents that are now or hereafter acquired, +owned by or assigned to You and (ii) that cover subject matter in Your +Modifications, taken alone or in combination with Original Code. + +1.2 "Contributor" means any person or entity that creates or +contributes to the creation of Modifications. + +1.3 "Covered Code" means the Original Code, Modifications, the +combination of Original Code and any Modifications, and/or any +respective portions thereof. + +1.4 "Externally Deploy" means: (a) to sublicense, distribute or +otherwise make Covered Code available, directly or indirectly, to +anyone other than You; and/or (b) to use Covered Code, alone or as +part of a Larger Work, in any way to provide a service, including but +not limited to delivery of content, through electronic communication +with a client other than You. + +1.5 "Larger Work" means a work which combines Covered Code or portions +thereof with code not governed by the terms of this License. + +1.6 "Modifications" mean any addition to, deletion from, and/or change +to, the substance and/or structure of the Original Code, any previous +Modifications, the combination of Original Code and any previous +Modifications, and/or any respective portions thereof. When code is +released as a series of files, a Modification is: (a) any addition to +or deletion from the contents of a file containing Covered Code; +and/or (b) any new file or other representation of computer program +statements that contains any part of Covered Code. + +1.7 "Original Code" means (a) the Source Code of a program or other +work as originally made available by Apple under this License, +including the Source Code of any updates or upgrades to such programs +or works made available by Apple under this License, and that has been +expressly identified by Apple as such in the header file(s) of such +work; and (b) the object code compiled from such Source Code and +originally made available by Apple under this License. + +1.8 "Source Code" means the human readable form of a program or other +work that is suitable for making modifications to it, including all +modules it contains, plus any associated interface definition files, +scripts used to control compilation and installation of an executable +(object code). + +1.9 "You" or "Your" means an individual or a legal entity exercising +rights under this License. For legal entities, "You" or "Your" +includes any entity which controls, is controlled by, or is under +common control with, You, where "control" means (a) the power, direct +or indirect, to cause the direction or management of such entity, +whether by contract or otherwise, or (b) ownership of fifty percent +(50%) or more of the outstanding shares or beneficial ownership of +such entity. + +2. Permitted Uses; Conditions & Restrictions. Subject to the terms +and conditions of this License, Apple hereby grants You, effective on +the date You accept this License and download the Original Code, a +world-wide, royalty-free, non-exclusive license, to the extent of +Apple's Applicable Patent Rights and copyrights covering the Original +Code, to do the following: + +2.1 Unmodified Code. You may use, reproduce, display, perform, +internally distribute within Your organization, and Externally Deploy +verbatim, unmodified copies of the Original Code, for commercial or +non-commercial purposes, provided that in each instance: + +(a) You must retain and reproduce in all copies of Original Code the +copyright and other proprietary notices and disclaimers of Apple as +they appear in the Original Code, and keep intact all notices in the +Original Code that refer to this License; and + +(b) You must include a copy of this License with every copy of Source +Code of Covered Code and documentation You distribute or Externally +Deploy, and You may not offer or impose any terms on such Source Code +that alter or restrict this License or the recipients' rights +hereunder, except as permitted under Section 6. + +2.2 Modified Code. You may modify Covered Code and use, reproduce, +display, perform, internally distribute within Your organization, and +Externally Deploy Your Modifications and Covered Code, for commercial +or non-commercial purposes, provided that in each instance You also +meet all of these conditions: + +(a) You must satisfy all the conditions of Section 2.1 with respect to +the Source Code of the Covered Code; + +(b) You must duplicate, to the extent it does not already exist, the +notice in Exhibit A in each file of the Source Code of all Your +Modifications, and cause the modified files to carry prominent notices +stating that You changed the files and the date of any change; and + +(c) If You Externally Deploy Your Modifications, You must make +Source Code of all Your Externally Deployed Modifications either +available to those to whom You have Externally Deployed Your +Modifications, or publicly available. Source Code of Your Externally +Deployed Modifications must be released under the terms set forth in +this License, including the license grants set forth in Section 3 +below, for as long as you Externally Deploy the Covered Code or twelve +(12) months from the date of initial External Deployment, whichever is +longer. You should preferably distribute the Source Code of Your +Externally Deployed Modifications electronically (e.g. download from a +web site). + +2.3 Distribution of Executable Versions. In addition, if You +Externally Deploy Covered Code (Original Code and/or Modifications) in +object code, executable form only, You must include a prominent +notice, in the code itself as well as in related documentation, +stating that Source Code of the Covered Code is available under the +terms of this License with information on how and where to obtain such +Source Code. + +2.4 Third Party Rights. You expressly acknowledge and agree that +although Apple and each Contributor grants the licenses to their +respective portions of the Covered Code set forth herein, no +assurances are provided by Apple or any Contributor that the Covered +Code does not infringe the patent or other intellectual property +rights of any other entity. Apple and each Contributor disclaim any +liability to You for claims brought by any other entity based on +infringement of intellectual property rights or otherwise. As a +condition to exercising the rights and licenses granted hereunder, You +hereby assume sole responsibility to secure any other intellectual +property rights needed, if any. For example, if a third party patent +license is required to allow You to distribute the Covered Code, it is +Your responsibility to acquire that license before distributing the +Covered Code. + +3. Your Grants. In consideration of, and as a condition to, the +licenses granted to You under this License, You hereby grant to any +person or entity receiving or distributing Covered Code under this +License a non-exclusive, royalty-free, perpetual, irrevocable license, +under Your Applicable Patent Rights and other intellectual property +rights (other than patent) owned or controlled by You, to use, +reproduce, display, perform, modify, sublicense, distribute and +Externally Deploy Your Modifications of the same scope and extent as +Apple's licenses under Sections 2.1 and 2.2 above. + +4. Larger Works. You may create a Larger Work by combining Covered +Code with other code not governed by the terms of this License and +distribute the Larger Work as a single product. In each such instance, +You must make sure the requirements of this License are fulfilled for +the Covered Code or any portion thereof. + +5. Limitations on Patent License. Except as expressly stated in +Section 2, no other patent rights, express or implied, are granted by +Apple herein. Modifications and/or Larger Works may require additional +patent licenses from Apple which Apple may grant in its sole +discretion. + +6. Additional Terms. You may choose to offer, and to charge a fee for, +warranty, support, indemnity or liability obligations and/or other +rights consistent with the scope of the license granted herein +("Additional Terms") to one or more recipients of Covered Code. +However, You may do so only on Your own behalf and as Your sole +responsibility, and not on behalf of Apple or any Contributor. You +must obtain the recipient's agreement that any such Additional Terms +are offered by You alone, and You hereby agree to indemnify, defend +and hold Apple and every Contributor harmless for any liability +incurred by or claims asserted against Apple or such Contributor by +reason of any such Additional Terms. + +7. Versions of the License. Apple may publish revised and/or new +versions of this License from time to time. Each version will be given +a distinguishing version number. Once Original Code has been published +under a particular version of this License, You may continue to use it +under the terms of that version. You may also choose to use such +Original Code under the terms of any subsequent version of this +License published by Apple. No one other than Apple has the right to +modify the terms applicable to Covered Code created under this +License. + +8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in +part pre-release, untested, or not fully tested works. The Covered +Code may contain errors that could cause failures or loss of data, and +may be incomplete or contain inaccuracies. You expressly acknowledge +and agree that use of the Covered Code, or any portion thereof, is at +Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND +WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND +APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE +PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM +ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF +MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR +PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD +PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST +INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE +FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, +THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR +ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO +ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE +AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. +You acknowledge that the Covered Code is not intended for use in the +operation of nuclear facilities, aircraft navigation, communication +systems, or air traffic control machines in which case the failure of +the Covered Code could lead to death, personal injury, or severe +physical or environmental damage. + +9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO +EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING +TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR +ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, +TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF +APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY +REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF +INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY +TO YOU. In no event shall Apple's total liability to You for all +damages (other than as may be required by applicable law) under this +License exceed the amount of fifty dollars ($50.00). + +10. Trademarks. This License does not grant any rights to use the +trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS", +"QuickTime", "QuickTime Streaming Server" or any other trademarks, +service marks, logos or trade names belonging to Apple (collectively +"Apple Marks") or to any trademark, service mark, logo or trade name +belonging to any Contributor. You agree not to use any Apple Marks in +or as part of the name of products derived from the Original Code or +to endorse or promote products derived from the Original Code other +than as expressly permitted by and in strict compliance at all times +with Apple's third party trademark usage guidelines which are posted +at http://www.apple.com/legal/guidelinesfor3rdparties.html. + +11. Ownership. Subject to the licenses granted under this License, +each Contributor retains all rights, title and interest in and to any +Modifications made by such Contributor. Apple retains all rights, +title and interest in and to the Original Code and any Modifications +made by or on behalf of Apple ("Apple Modifications"), and such Apple +Modifications will not be automatically subject to this License. Apple +may, at its sole discretion, choose to license such Apple +Modifications under this License, or on different terms from those +contained in this License or may choose not to license them at all. + +12. Termination. + +12.1 Termination. This License and the rights granted hereunder will +terminate: + +(a) automatically without notice from Apple if You fail to comply with +any term(s) of this License and fail to cure such breach within 30 +days of becoming aware of such breach; + +(b) immediately in the event of the circumstances described in Section +13.5(b); or + +(c) automatically without notice from Apple if You, at any time during +the term of this License, commence an action for patent infringement +against Apple; provided that Apple did not first commence +an action for patent infringement against You in that instance. + +12.2 Effect of Termination. Upon termination, You agree to immediately +stop any further use, reproduction, modification, sublicensing and +distribution of the Covered Code. All sublicenses to the Covered Code +which have been properly granted prior to termination shall survive +any termination of this License. Provisions which, by their nature, +should remain in effect beyond the termination of this License shall +survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, +12.2 and 13. No party will be liable to any other for compensation, +indemnity or damages of any sort solely as a result of terminating +this License in accordance with its terms, and termination of this +License will be without prejudice to any other right or remedy of +any party. + +13. Miscellaneous. + +13.1 Government End Users. The Covered Code is a "commercial item" as +defined in FAR 2.101. Government software and technical data rights in +the Covered Code include only those rights customarily provided to the +public as defined in this License. This customary commercial license +in technical data and software is provided in accordance with FAR +12.211 (Technical Data) and 12.212 (Computer Software) and, for +Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- +Commercial Items) and 227.7202-3 (Rights in Commercial Computer +Software or Computer Software Documentation). Accordingly, all U.S. +Government End Users acquire Covered Code with only those rights set +forth herein. + +13.2 Relationship of Parties. This License will not be construed as +creating an agency, partnership, joint venture or any other form of +legal association between or among You, Apple or any Contributor, and +You will not represent to the contrary, whether expressly, by +implication, appearance or otherwise. + +13.3 Independent Development. Nothing in this License will impair +Apple's right to acquire, license, develop, have others develop for +it, market and/or distribute technology or products that perform the +same or similar functions as, or otherwise compete with, +Modifications, Larger Works, technology or products that You may +develop, produce, market or distribute. + +13.4 Waiver; Construction. Failure by Apple or any Contributor to +enforce any provision of this License will not be deemed a waiver of +future enforcement of that or any other provision. Any law or +regulation which provides that the language of a contract shall be +construed against the drafter will not apply to this License. + +13.5 Severability. (a) If for any reason a court of competent +jurisdiction finds any provision of this License, or portion thereof, +to be unenforceable, that provision of the License will be enforced to +the maximum extent permissible so as to effect the economic benefits +and intent of the parties, and the remainder of this License will +continue in full force and effect. (b) Notwithstanding the foregoing, +if applicable law prohibits or restricts You from fully and/or +specifically complying with Sections 2 and/or 3 or prevents the +enforceability of either of those Sections, this License will +immediately terminate and You must immediately discontinue any use of +the Covered Code and destroy all copies of it that are in your +possession or control. + +13.6 Dispute Resolution. Any litigation or other dispute resolution +between You and Apple relating to this License shall take place in the +Northern District of California, and You and Apple hereby consent to +the personal jurisdiction of, and venue in, the state and federal +courts within that District with respect to this License. The +application of the United Nations Convention on Contracts for the +International Sale of Goods is expressly excluded. + +13.7 Entire Agreement; Governing Law. This License constitutes the +entire agreement between the parties with respect to the subject +matter hereof. This License shall be governed by the laws of the +United States and the State of California, except that body of +California law concerning conflicts of law. + +Where You are located in the province of Quebec, Canada, the following +clause applies: The parties hereby confirm that they have requested +that this License and all related documents be drafted in English. Les +parties ont exige que le present contrat et tous les documents +connexes soient rediges en anglais. + +EXHIBIT A. + +"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights +Reserved. + +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." diff --git a/doc/man/man1/ld64.1 b/doc/man/man1/ld64.1 new file mode 100644 index 0000000..615b0e5 --- /dev/null +++ b/doc/man/man1/ld64.1 @@ -0,0 +1 @@ +.so man1/ld.1 diff --git a/ld64.xcode/project.pbxproj b/ld64.xcode/project.pbxproj new file mode 100644 index 0000000..ef5a16d --- /dev/null +++ b/ld64.xcode/project.pbxproj @@ -0,0 +1,423 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 39; + objects = { + F9023C2C06D5A227001BBF46 = { + children = ( + F971EED706D5AD240041D381, + F9023C3F06D5A254001BBF46, + F9C0D48A06DD1E1B001C7193, + F9C0D48B06DD1E1B001C7193, + F9023C3E06D5A254001BBF46, + F9023C4006D5A254001BBF46, + F9023C4106D5A254001BBF46, + F97288E607D277570031794D, + F972890007D27FD00031794D, + F9023C4206D5A254001BBF46, + F9023C4806D5A254001BBF46, + F97F5028070D0BB200B9FCD7, + F9023C3A06D5A23E001BBF46, + ); + isa = PBXGroup; + refType = 4; + sourceTree = ""; + }; + F9023C2E06D5A227001BBF46 = { + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_OPTIMIZATION_LEVEL = 0; + }; + isa = PBXBuildStyle; + name = Development; + }; + F9023C2F06D5A227001BBF46 = { + buildSettings = { + COPY_PHASE_STRIP = YES; + }; + isa = PBXBuildStyle; + name = Deployment; + }; + F9023C3006D5A227001BBF46 = { + buildSettings = { + }; + buildStyles = ( + F9023C2E06D5A227001BBF46, + F9023C2F06D5A227001BBF46, + ); + hasScannedForEncodings = 0; + isa = PBXProject; + mainGroup = F9023C2C06D5A227001BBF46; + productRefGroup = F9023C3A06D5A23E001BBF46; + projectDirPath = ""; + targets = ( + F9023C3806D5A23E001BBF46, + F971EED206D5ACF60041D381, + ); + }; + F9023C3606D5A23E001BBF46 = { + buildActionMask = 2147483647; + files = ( + F9023C4E06D5A272001BBF46, + F9C0D4BD06DD28D2001C7193, + F9023C4F06D5A272001BBF46, + F9023C5006D5A272001BBF46, + F97288E707D277570031794D, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + F9023C3706D5A23E001BBF46 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + F9023C3806D5A23E001BBF46 = { + buildPhases = ( + F9023C3606D5A23E001BBF46, + F9023C3706D5A23E001BBF46, + F97F5025070D0B6300B9FCD7, + ); + buildRules = ( + ); + buildSettings = { + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 3; + INSTALL_PATH = /usr/bin; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = ld64; + SECTORDER_FLAGS = ""; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + dependencies = ( + ); + isa = PBXNativeTarget; + name = ld64; + productName = ld64; + productReference = F9023C3906D5A23E001BBF46; + productType = "com.apple.product-type.tool"; + }; + F9023C3906D5A23E001BBF46 = { + explicitFileType = "compiled.mach-o.executable"; + includeInIndex = 0; + isa = PBXFileReference; + path = ld64; + refType = 3; + sourceTree = BUILT_PRODUCTS_DIR; + }; + F9023C3A06D5A23E001BBF46 = { + children = ( + F9023C3906D5A23E001BBF46, + F971EED306D5ACF60041D381, + ); + isa = PBXGroup; + name = Products; + refType = 4; + sourceTree = ""; + }; + F9023C3E06D5A254001BBF46 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = ExecutableFile.h; + path = src/ExecutableFile.h; + refType = 4; + sourceTree = ""; + }; + F9023C3F06D5A254001BBF46 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = ld.cpp; + path = src/ld.cpp; + refType = 4; + sourceTree = ""; + }; + F9023C4006D5A254001BBF46 = { + fileEncoding = 30; + indentWidth = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = MachOAbstraction.h; + path = src/MachOAbstraction.h; + refType = 4; + sourceTree = ""; + tabWidth = 4; + usesTabs = 1; + }; + F9023C4106D5A254001BBF46 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = ObjectFile.h; + path = src/ObjectFile.h; + refType = 4; + sourceTree = ""; + }; + F9023C4206D5A254001BBF46 = { + children = ( + F9023C4706D5A254001BBF46, + F9023C4406D5A254001BBF46, + F95DD3B106EE395A007CAFEB, + F9023C4506D5A254001BBF46, + F9023C4606D5A254001BBF46, + ); + isa = PBXGroup; + name = Readers; + path = src/Readers; + refType = 4; + sourceTree = ""; + }; + F9023C4406D5A254001BBF46 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + path = ObjectFileDylibMachO.cpp; + refType = 4; + sourceTree = ""; + }; + F9023C4506D5A254001BBF46 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + path = "ObjectFileMachO-all.cpp"; + refType = 4; + sourceTree = ""; + }; + F9023C4606D5A254001BBF46 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + path = "ObjectFileMachO-all.h"; + refType = 4; + sourceTree = ""; + }; + F9023C4706D5A254001BBF46 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + path = ObjectFileMachO.cpp; + refType = 4; + sourceTree = ""; + }; + F9023C4806D5A254001BBF46 = { + children = ( + F9023C4906D5A254001BBF46, + F9023C4A06D5A254001BBF46, + F9023C4B06D5A254001BBF46, + ); + isa = PBXGroup; + name = Writers; + path = src/Writers; + refType = 4; + sourceTree = ""; + }; + F9023C4906D5A254001BBF46 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + path = "ExecutableFileMachO-all.cpp"; + refType = 4; + sourceTree = ""; + }; + F9023C4A06D5A254001BBF46 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + path = "ExecutableFileMachO-all.h"; + refType = 4; + sourceTree = ""; + }; + F9023C4B06D5A254001BBF46 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + path = ExecutableFileMachO.cpp; + refType = 4; + sourceTree = ""; + }; + F9023C4E06D5A272001BBF46 = { + fileRef = F9023C3F06D5A254001BBF46; + isa = PBXBuildFile; + settings = { + }; + }; + F9023C4F06D5A272001BBF46 = { + fileRef = F9023C4506D5A254001BBF46; + isa = PBXBuildFile; + settings = { + }; + }; + F9023C5006D5A272001BBF46 = { + fileRef = F9023C4906D5A254001BBF46; + isa = PBXBuildFile; + settings = { + }; + }; + F95DD3B106EE395A007CAFEB = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + path = ObjectFileArchiveMachO.cpp; + refType = 4; + sourceTree = ""; + }; + F971EED006D5ACF60041D381 = { + buildActionMask = 2147483647; + files = ( + F971EED806D5AD240041D381, + F971EEDA06D5AD450041D381, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + F971EED106D5ACF60041D381 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + F971EED206D5ACF60041D381 = { + buildPhases = ( + F971EED006D5ACF60041D381, + F971EED106D5ACF60041D381, + ); + buildRules = ( + ); + buildSettings = { + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = "$(HOME)/bin"; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = ObjectDump; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + dependencies = ( + ); + isa = PBXNativeTarget; + name = ObjectDump; + productName = ObjectDump; + productReference = F971EED306D5ACF60041D381; + productType = "com.apple.product-type.tool"; + }; + F971EED306D5ACF60041D381 = { + explicitFileType = "compiled.mach-o.executable"; + includeInIndex = 0; + isa = PBXFileReference; + path = ObjectDump; + refType = 3; + sourceTree = BUILT_PRODUCTS_DIR; + }; + F971EED706D5AD240041D381 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = ObjDump.cpp; + path = src/ObjDump.cpp; + refType = 4; + sourceTree = ""; + }; + F971EED806D5AD240041D381 = { + fileRef = F971EED706D5AD240041D381; + isa = PBXBuildFile; + settings = { + }; + }; + F971EEDA06D5AD450041D381 = { + fileRef = F9023C4506D5A254001BBF46; + isa = PBXBuildFile; + settings = { + }; + }; + F97288E607D277570031794D = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = SectCreate.cpp; + path = src/SectCreate.cpp; + refType = 4; + sourceTree = ""; + }; + F97288E707D277570031794D = { + fileRef = F97288E607D277570031794D; + isa = PBXBuildFile; + settings = { + }; + }; + F972890007D27FD00031794D = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = SectCreate.h; + path = src/SectCreate.h; + refType = 4; + sourceTree = ""; + }; + F97F5025070D0B6300B9FCD7 = { + buildActionMask = 8; + dstPath = /usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + F97F5029070D0BB200B9FCD7, + ); + isa = PBXCopyFilesBuildPhase; + runOnlyForDeploymentPostprocessing = 1; + }; + F97F5028070D0BB200B9FCD7 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = text.man; + name = ld64.1; + path = doc/man/man1/ld64.1; + refType = 4; + sourceTree = ""; + }; + F97F5029070D0BB200B9FCD7 = { + fileRef = F97F5028070D0BB200B9FCD7; + isa = PBXBuildFile; + settings = { + }; + }; + F9C0D48A06DD1E1B001C7193 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = Options.cpp; + path = src/Options.cpp; + refType = 4; + sourceTree = ""; + }; + F9C0D48B06DD1E1B001C7193 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = Options.h; + path = src/Options.h; + refType = 4; + sourceTree = ""; + }; + F9C0D4BD06DD28D2001C7193 = { + fileRef = F9C0D48A06DD1E1B001C7193; + isa = PBXBuildFile; + settings = { + }; + }; + }; + rootObject = F9023C3006D5A227001BBF46; +} diff --git a/src/ExecutableFile.h b/src/ExecutableFile.h new file mode 100644 index 0000000..e2e1e8c --- /dev/null +++ b/src/ExecutableFile.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __EXECUTABLEFILE__ +#define __EXECUTABLEFILE__ + +#include +#include + +#include "ObjectFile.h" +#include "Options.h" + + +namespace ExecutableFile { + + struct DyLibUsed + { + ObjectFile::Reader* reader; + DynamicLibraryOptions options; + bool indirect; // library found indirect. Do not make load command + ObjectFile::Reader* directReader; // direct library which re-exports this library + }; + + class Writer : public ObjectFile::Reader + { + public: + virtual ~Writer() {}; + + virtual const char* getPath() = 0; + virtual std::vector& getAtoms() = 0; + virtual std::vector* getJustInTimeAtomsFor(const char* name) = 0; + virtual std::vector* getStabsDebugInfo() = 0; + + virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name) = 0; + virtual void write(std::vector& atoms, class ObjectFile::Atom* entryPointAtom) = 0; + + + + protected: + Writer(std::vector&) {}; + }; + +}; + + + + +#endif // __EXECUTABLEFILE__ + + + diff --git a/src/MachOAbstraction.h b/src/MachOAbstraction.h new file mode 100644 index 0000000..211a189 --- /dev/null +++ b/src/MachOAbstraction.h @@ -0,0 +1,1978 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include + + +#undef ENDIAN_READ16 +#undef ENDIAN_WRITE16 +#undef ENDIAN_READ32 +#undef ENDIAN_WRITE32 +#undef ENDIAN_SWAP64 +#undef ENDIAN_SWAP_POINTER + +#if defined(MACHO_32_SAME_ENDIAN) || defined(MACHO_64_SAME_ENDIAN) + #define ENDIAN_READ16(x) (x) + #define ENDIAN_WRITE16(into, value) into = (value); + + #define ENDIAN_READ32(x) (x) + #define ENDIAN_WRITE32(into, value) into = (value); + + #define ENDIAN_SWAP64(x) (x) + +#elif defined(MACHO_32_OPPOSITE_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) + #define ENDIAN_READ16(x) OSReadSwapInt16((uint16_t*)&(x), 0) + #define ENDIAN_WRITE16(into, value) OSWriteSwapInt16(&(into), 0, value); + + #define ENDIAN_READ32(x) OSReadSwapInt32((uint32_t*)&(x), 0) + #define ENDIAN_WRITE32(into, value) OSWriteSwapInt32(&(into), 0, value); + + #define ENDIAN_SWAP64(x) OSSwapInt64(x) + +#else + #error file format undefined +#endif + + +#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) + typedef uint64_t macho_uintptr_t; + typedef int64_t macho_intptr_t; +#else + typedef uint32_t macho_uintptr_t; + typedef int32_t macho_intptr_t; +#endif + +#if defined(MACHO_32_SAME_ENDIAN) + #define ENDIAN_SWAP_POINTER(x) (x) +#elif defined(MACHO_64_SAME_ENDIAN) + #define ENDIAN_SWAP_POINTER(x) (x) +#elif defined(MACHO_32_OPPOSITE_ENDIAN) + #define ENDIAN_SWAP_POINTER(x) OSSwapInt32(x) +#elif defined(MACHO_64_OPPOSITE_ENDIAN) + #define ENDIAN_SWAP_POINTER(x) OSSwapInt64(x) +#else + #error file format undefined +#endif + + +#undef mach_header +#undef mach_header_64 +class macho_header { +public: + uint32_t magic() const; + void set_magic(uint32_t); + + cpu_type_t cputype() const; + void set_cputype(cpu_type_t); + + cpu_subtype_t cpusubtype() const; + void set_cpusubtype(cpu_subtype_t); + + uint32_t filetype() const; + void set_filetype(uint32_t); + + uint32_t ncmds() const; + void set_ncmds(uint32_t); + + uint32_t sizeofcmds() const; + void set_sizeofcmds(uint32_t); + + uint32_t flags() const; + void set_flags(uint32_t); + + void set_reserved(); + +#if defined(MACHO_64_SAME_ENDIAN) + enum { size = sizeof(mach_header_64) }; + enum { magic_value = MH_MAGIC_64 }; +#elif defined(MACHO_64_OPPOSITE_ENDIAN) + enum { size = sizeof(mach_header_64) }; + enum { magic_value = MH_MAGIC_64 }; +#elif defined(MACHO_32_SAME_ENDIAN) + enum { size = sizeof(mach_header) }; + enum { magic_value = MH_MAGIC }; +#elif defined(MACHO_32_OPPOSITE_ENDIAN) + enum { size = sizeof(mach_header) }; + enum { magic_value = MH_MAGIC }; +#endif + +private: +#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) + struct mach_header_64 content; +#else + struct mach_header content; +#endif +}; +#define mach_header __my_bad +#define mach_header_64 __my_bad + +inline __attribute__((always_inline)) +uint32_t macho_header::magic() const { + return ENDIAN_READ32(content.magic); +} + +inline __attribute__((always_inline)) +void macho_header::set_magic(uint32_t _value) { + ENDIAN_WRITE32(content.magic, _value); +} + +inline __attribute__((always_inline)) +cpu_type_t macho_header::cputype() const { + return ENDIAN_READ32(content.cputype); +} + +inline __attribute__((always_inline)) +void macho_header::set_cputype(cpu_type_t _value) { + ENDIAN_WRITE32(content.cputype, _value); +} + +inline __attribute__((always_inline)) +cpu_subtype_t macho_header::cpusubtype() const { + return ENDIAN_READ32(content.cpusubtype); +} + +inline __attribute__((always_inline)) +void macho_header::set_cpusubtype(cpu_subtype_t _value) { + ENDIAN_WRITE32(content.cpusubtype, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_header::filetype() const { + return ENDIAN_READ32(content.filetype); +} + +inline __attribute__((always_inline)) +void macho_header::set_filetype(uint32_t _value) { + ENDIAN_WRITE32(content.filetype, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_header::ncmds() const { + return ENDIAN_READ32(content.ncmds); +} + +inline __attribute__((always_inline)) +void macho_header::set_ncmds(uint32_t _value) { + ENDIAN_WRITE32(content.ncmds, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_header::sizeofcmds() const { + return ENDIAN_READ32(content.sizeofcmds); +} + +inline __attribute__((always_inline)) +void macho_header::set_sizeofcmds(uint32_t _value) { + ENDIAN_WRITE32(content.sizeofcmds, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_header::flags() const { + return ENDIAN_READ32(content.flags); +} + +inline __attribute__((always_inline)) +void macho_header::set_flags(uint32_t _value) { + ENDIAN_WRITE32(content.flags, _value); +} + +inline __attribute__((always_inline)) +void macho_header::set_reserved() { +#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) + content.reserved = 0; +#endif +} + + +#undef load_command +class macho_load_command { +public: + uint32_t cmd() const; + void set_cmd(uint32_t); + + uint32_t cmdsize() const; + void set_cmdsize(uint32_t); + +private: + struct load_command content; +}; + +inline __attribute__((always_inline)) +uint32_t macho_load_command::cmd() const { + return ENDIAN_READ32(content.cmd); +} + +inline __attribute__((always_inline)) +void macho_load_command::set_cmd(uint32_t _value) { + ENDIAN_WRITE32(content.cmd, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_load_command::cmdsize() const { + return ENDIAN_READ32(content.cmdsize); +} + +inline __attribute__((always_inline)) +void macho_load_command::set_cmdsize(uint32_t _value) { + ENDIAN_WRITE32(content.cmdsize, _value); +} +#define load_command __my_bad + + +#undef segment_command +#undef segment_command_64 +class macho_segment_command { +public: + uint32_t cmd() const; + void set_cmd(uint32_t); + + uint32_t cmdsize() const; + void set_cmdsize(uint32_t); + + const char* segname() const; + void set_segname(const char*); + + uint64_t vmaddr() const; + void set_vmaddr(uint64_t); + + uint64_t vmsize() const; + void set_vmsize(uint64_t); + + uint64_t fileoff() const; + void set_fileoff(uint64_t); + + uint64_t filesize() const; + void set_filesize(uint64_t); + + vm_prot_t maxprot() const; + void set_maxprot(vm_prot_t); + + vm_prot_t initprot() const; + void set_initprot(vm_prot_t); + + uint32_t nsects() const; + void set_nsects(uint32_t); + + uint32_t flags() const; + void set_flags(uint32_t); + + enum { size = +#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) + sizeof(segment_command_64) }; +#else + sizeof(segment_command) }; +#endif + + enum { command = +#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) + LC_SEGMENT_64 +#else + LC_SEGMENT +#endif + }; + +private: +#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) + struct segment_command_64 content; +#else + struct segment_command content; +#endif +}; +#define segment_command __my_bad +#define segment_command_64 __my_bad + +inline __attribute__((always_inline)) +uint32_t macho_segment_command::cmd() const { + return ENDIAN_READ32(content.cmd); +} + +inline __attribute__((always_inline)) +void macho_segment_command::set_cmd(uint32_t _value) { + ENDIAN_WRITE32(content.cmd, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_segment_command::cmdsize() const { + return ENDIAN_READ32(content.cmdsize); +} + +inline __attribute__((always_inline)) +void macho_segment_command::set_cmdsize(uint32_t _value) { + ENDIAN_WRITE32(content.cmdsize, _value); +} + +inline __attribute__((always_inline)) +const char* macho_segment_command::segname() const { + return content.segname; +} + +inline __attribute__((always_inline)) +void macho_segment_command::set_segname(const char* _value) { + strncpy(content.segname, _value, 16); +} + +inline __attribute__((always_inline)) +uint64_t macho_segment_command::vmaddr() const { +#if defined(ARCH_PPC64) + return ENDIAN_SWAP64(content.vmaddr); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + return ENDIAN_READ32(content.vmaddr); +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +void macho_segment_command::set_vmaddr(uint64_t _value) { +#if defined(ARCH_PPC64) + content.vmaddr = ENDIAN_SWAP64(_value); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + ENDIAN_WRITE32(content.vmaddr, _value); +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +uint64_t macho_segment_command::vmsize() const { +#if defined(ARCH_PPC64) + return ENDIAN_SWAP64(content.vmsize); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + return ENDIAN_READ32(content.vmsize); +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +void macho_segment_command::set_vmsize(uint64_t _value) { +#if defined(ARCH_PPC64) + content.vmsize = ENDIAN_SWAP64(_value); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + ENDIAN_WRITE32(content.vmsize, _value); +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +uint64_t macho_segment_command::fileoff() const { +#if defined(ARCH_PPC64) + return ENDIAN_SWAP64(content.fileoff); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + return ENDIAN_READ32(content.fileoff); +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +void macho_segment_command::set_fileoff(uint64_t _value) { +#if defined(ARCH_PPC64) + content.fileoff = ENDIAN_SWAP64(_value); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + ENDIAN_WRITE32(content.fileoff, _value); +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +uint64_t macho_segment_command::filesize() const { +#if defined(ARCH_PPC64) + return ENDIAN_SWAP64(content.filesize); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + return ENDIAN_READ32(content.filesize); +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +void macho_segment_command::set_filesize(uint64_t _value) { +#if defined(ARCH_PPC64) + content.filesize = ENDIAN_SWAP64(_value); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + ENDIAN_WRITE32(content.filesize, _value); +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +vm_prot_t macho_segment_command::maxprot() const { + return ENDIAN_READ32(content.maxprot); +} + +inline __attribute__((always_inline)) +void macho_segment_command::set_maxprot(vm_prot_t _value) { + ENDIAN_WRITE32(content.maxprot, _value); +} + +inline __attribute__((always_inline)) +vm_prot_t macho_segment_command::initprot() const { + return ENDIAN_READ32(content.initprot); +} + +inline __attribute__((always_inline)) +void macho_segment_command::set_initprot(vm_prot_t _value) { + ENDIAN_WRITE32(content.initprot, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_segment_command::nsects() const { + return ENDIAN_READ32(content.nsects); +} + +inline __attribute__((always_inline)) +void macho_segment_command::set_nsects(uint32_t _value) { + ENDIAN_WRITE32(content.nsects, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_segment_command::flags() const { + return ENDIAN_READ32(content.flags); +} + +inline __attribute__((always_inline)) +void macho_segment_command::set_flags(uint32_t _value) { + ENDIAN_WRITE32(content.flags, _value); +} + +#undef section +#undef section_64 +class macho_section { +public: + const char* sectname() const; + void set_sectname(const char*); + + const char* segname() const; + void set_segname(const char*); + + uint64_t addr() const; + void set_addr(uint64_t); + + uint64_t size() const; + void set_size(uint64_t); + + uint32_t offset() const; + void set_offset(uint32_t); + + uint32_t align() const; + void set_align(uint32_t); + + uint32_t reloff() const; + void set_reloff(uint32_t); + + uint32_t nreloc() const; + void set_nreloc(uint32_t); + + uint32_t flags() const; + void set_flags(uint32_t); + + uint32_t reserved1() const; + void set_reserved1(uint32_t); + + uint32_t reserved2() const; + void set_reserved2(uint32_t); + + enum { content_size = +#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) + sizeof(section_64) }; +#else + sizeof(section) }; +#endif + +private: +#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) + struct section_64 content; +#else + struct section content; +#endif +}; +#define section __my_bad +#define section_64 __my_bad + +inline __attribute__((always_inline)) +const char* macho_section::sectname() const { + return content.sectname; +} + +inline __attribute__((always_inline)) +void macho_section::set_sectname(const char* _value) { + strncpy(content.sectname, _value, 16); +} + +inline __attribute__((always_inline)) +const char* macho_section::segname() const { + return content.segname; +} + +inline __attribute__((always_inline)) +void macho_section::set_segname(const char* _value) { + strncpy(content.segname, _value, 16); +} + +inline __attribute__((always_inline)) +uint64_t macho_section::addr() const { +#if defined(ARCH_PPC64) + return ENDIAN_SWAP64(content.addr); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + return ENDIAN_READ32(content.addr); +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +void macho_section::set_addr(uint64_t _value) { +#if defined(ARCH_PPC64) + content.addr = ENDIAN_SWAP64(_value); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + ENDIAN_WRITE32(content.addr, _value); +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +uint64_t macho_section::size() const { +#if defined(ARCH_PPC64) + return ENDIAN_SWAP64(content.size); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + return ENDIAN_READ32(content.size); +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +void macho_section::set_size(uint64_t _value) { +#if defined(ARCH_PPC64) + content.size = ENDIAN_SWAP64(_value); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + ENDIAN_WRITE32(content.size, _value); +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +uint32_t macho_section::offset() const { + return ENDIAN_READ32(content.offset); +} + +inline __attribute__((always_inline)) +void macho_section::set_offset(uint32_t _value) { + ENDIAN_WRITE32(content.offset, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_section::align() const { + return ENDIAN_READ32(content.align); +} + +inline __attribute__((always_inline)) +void macho_section::set_align(uint32_t _value) { + ENDIAN_WRITE32(content.align, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_section::reloff() const { + return ENDIAN_READ32(content.reloff); +} + +inline __attribute__((always_inline)) +void macho_section::set_reloff(uint32_t _value) { + ENDIAN_WRITE32(content.reloff, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_section::nreloc() const { + return ENDIAN_READ32(content.nreloc); +} + +inline __attribute__((always_inline)) +void macho_section::set_nreloc(uint32_t _value) { + ENDIAN_WRITE32(content.nreloc, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_section::flags() const { + return ENDIAN_READ32(content.flags); +} + +inline __attribute__((always_inline)) +void macho_section::set_flags(uint32_t _value) { + ENDIAN_WRITE32(content.flags, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_section::reserved1() const { + return ENDIAN_READ32(content.reserved1); +} + +inline __attribute__((always_inline)) +void macho_section::set_reserved1(uint32_t _value) { + ENDIAN_WRITE32(content.reserved1, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_section::reserved2() const { + return ENDIAN_READ32(content.reserved2); +} + +inline __attribute__((always_inline)) +void macho_section::set_reserved2(uint32_t _value) { + ENDIAN_WRITE32(content.reserved2, _value); +} + +#undef dylib_command +class macho_dylib_command { +public: + uint32_t cmd() const; + void set_cmd(uint32_t); + + uint32_t cmdsize() const; + void set_cmdsize(uint32_t); + + const char* name() const; + void set_name_offset(); + + uint32_t timestamp() const; + void set_timestamp(uint32_t); + + uint32_t current_version() const; + void set_current_version(uint32_t); + + uint32_t compatibility_version() const; + void set_compatibility_version(uint32_t); + + enum { name_offset = sizeof(struct dylib_command) }; + +private: + struct dylib_command content; +}; +#define dylib_command __my_bad + +inline __attribute__((always_inline)) +uint32_t macho_dylib_command::cmd() const { + return ENDIAN_READ32(content.cmd); +} + +inline __attribute__((always_inline)) +void macho_dylib_command::set_cmd(uint32_t _value) { + ENDIAN_WRITE32(content.cmd, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dylib_command::cmdsize() const { + return ENDIAN_READ32(content.cmdsize); +} + +inline __attribute__((always_inline)) +void macho_dylib_command::set_cmdsize(uint32_t _value) { + ENDIAN_WRITE32(content.cmdsize, _value); +} + +inline __attribute__((always_inline)) +const char* macho_dylib_command::name() const { + return (char*)(&content) + ENDIAN_READ32(content.dylib.name.offset); +} + +inline __attribute__((always_inline)) +void macho_dylib_command::set_name_offset() { + ENDIAN_WRITE32(content.dylib.name.offset, name_offset); +} + +inline __attribute__((always_inline)) +uint32_t macho_dylib_command::timestamp() const { + return ENDIAN_READ32(content.dylib.timestamp); +} + +inline __attribute__((always_inline)) +void macho_dylib_command::set_timestamp(uint32_t _value) { + ENDIAN_WRITE32(content.dylib.timestamp, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dylib_command::current_version() const { + return ENDIAN_READ32(content.dylib.current_version); +} + +inline __attribute__((always_inline)) +void macho_dylib_command::set_current_version(uint32_t _value) { + ENDIAN_WRITE32(content.dylib.current_version, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dylib_command::compatibility_version() const { + return ENDIAN_READ32(content.dylib.compatibility_version); +} + +inline __attribute__((always_inline)) +void macho_dylib_command::set_compatibility_version(uint32_t _value) { + ENDIAN_WRITE32(content.dylib.compatibility_version, _value); +} + + + +#undef dylinker_command +class macho_dylinker_command { +public: + uint32_t cmd() const; + void set_cmd(uint32_t); + + uint32_t cmdsize() const; + void set_cmdsize(uint32_t); + + void set_name_offset(); + + enum { name_offset = sizeof(struct dylinker_command) }; + +private: + struct dylinker_command content; +}; +#define dylinker_command __my_bad + +inline __attribute__((always_inline)) +uint32_t macho_dylinker_command::cmd() const { + return ENDIAN_READ32(content.cmd); +} + +inline __attribute__((always_inline)) +void macho_dylinker_command::set_cmd(uint32_t _value) { + ENDIAN_WRITE32(content.cmd, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dylinker_command::cmdsize() const { + return ENDIAN_READ32(content.cmdsize); +} + +inline __attribute__((always_inline)) +void macho_dylinker_command::set_cmdsize(uint32_t _value) { + ENDIAN_WRITE32(content.cmdsize, _value); +} + +inline __attribute__((always_inline)) +void macho_dylinker_command::set_name_offset() { + ENDIAN_WRITE32(content.name.offset, name_offset); +} + + + +#undef sub_framework_command +class macho_sub_framework_command { +public: + uint32_t cmd() const; + void set_cmd(uint32_t); + + uint32_t cmdsize() const; + void set_cmdsize(uint32_t); + + const char* name() const; + void set_name_offset(); + + enum { name_offset = sizeof(struct sub_framework_command) }; + +private: + struct sub_framework_command content; +}; +#define sub_framework_command __my_bad + +inline __attribute__((always_inline)) +uint32_t macho_sub_framework_command::cmd() const { + return ENDIAN_READ32(content.cmd); +} + +inline __attribute__((always_inline)) +void macho_sub_framework_command::set_cmd(uint32_t _value) { + ENDIAN_WRITE32(content.cmd, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_sub_framework_command::cmdsize() const { + return ENDIAN_READ32(content.cmdsize); +} + +inline __attribute__((always_inline)) +void macho_sub_framework_command::set_cmdsize(uint32_t _value) { + ENDIAN_WRITE32(content.cmdsize, _value); +} + +inline __attribute__((always_inline)) +const char* macho_sub_framework_command::name() const { + return (char*)(&content) + ENDIAN_READ32(content.umbrella.offset); +} + +inline __attribute__((always_inline)) +void macho_sub_framework_command::set_name_offset() { + ENDIAN_WRITE32(content.umbrella.offset, name_offset); +} + +#undef sub_client_command +class macho_sub_client_command { +public: + uint32_t cmd() const; + void set_cmd(uint32_t); + + uint32_t cmdsize() const; + void set_cmdsize(uint32_t); + + const char* name() const; + void set_name_offset(); + + enum { name_offset = sizeof(struct sub_client_command) }; +private: + struct sub_client_command content; +}; +#define sub_client_command __my_bad + +inline __attribute__((always_inline)) +uint32_t macho_sub_client_command::cmd() const { + return ENDIAN_READ32(content.cmd); +} + +inline __attribute__((always_inline)) +void macho_sub_client_command::set_cmd(uint32_t _value) { + ENDIAN_WRITE32(content.cmd, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_sub_client_command::cmdsize() const { + return ENDIAN_READ32(content.cmdsize); +} + +inline __attribute__((always_inline)) +void macho_sub_client_command::set_cmdsize(uint32_t _value) { + ENDIAN_WRITE32(content.cmdsize, _value); +} + +inline __attribute__((always_inline)) +const char* macho_sub_client_command::name() const { + return (char*)(&content) + ENDIAN_READ32(content.client.offset); +} + +inline __attribute__((always_inline)) +void macho_sub_client_command::set_name_offset() { + ENDIAN_WRITE32(content.client.offset, name_offset); +} + + + +#undef sub_umbrella_command +class macho_sub_umbrella_command { +public: + uint32_t cmd() const; + void set_cmd(uint32_t); + + uint32_t cmdsize() const; + void set_cmdsize(uint32_t); + + const char* name() const; + void set_name_offset(); + + enum { name_offset = sizeof(struct sub_umbrella_command) }; +private: + struct sub_umbrella_command content; +}; +#define sub_umbrella_command __my_bad + +inline __attribute__((always_inline)) +uint32_t macho_sub_umbrella_command::cmd() const { + return ENDIAN_READ32(content.cmd); +} + +inline __attribute__((always_inline)) +void macho_sub_umbrella_command::set_cmd(uint32_t _value) { + ENDIAN_WRITE32(content.cmd, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_sub_umbrella_command::cmdsize() const { + return ENDIAN_READ32(content.cmdsize); +} + +inline __attribute__((always_inline)) +void macho_sub_umbrella_command::set_cmdsize(uint32_t _value) { + ENDIAN_WRITE32(content.cmdsize, _value); +} + +inline __attribute__((always_inline)) +const char* macho_sub_umbrella_command::name() const { + return (char*)(&content) + ENDIAN_READ32(content.sub_umbrella.offset); +} + +inline __attribute__((always_inline)) +void macho_sub_umbrella_command::set_name_offset() { + ENDIAN_WRITE32(content.sub_umbrella.offset, name_offset); +} + + + + +#undef sub_library_command +class macho_sub_library_command { +public: + uint32_t cmd() const; + void set_cmd(uint32_t); + + uint32_t cmdsize() const; + void set_cmdsize(uint32_t); + + const char* name() const; + void set_name_offset(); + + enum { name_offset = sizeof(struct sub_library_command) }; +private: + struct sub_library_command content; +}; +#define sub_library_command __my_bad + +inline __attribute__((always_inline)) +uint32_t macho_sub_library_command::cmd() const { + return ENDIAN_READ32(content.cmd); +} + +inline __attribute__((always_inline)) +void macho_sub_library_command::set_cmd(uint32_t _value) { + ENDIAN_WRITE32(content.cmd, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_sub_library_command::cmdsize() const { + return ENDIAN_READ32(content.cmdsize); +} + +inline __attribute__((always_inline)) +void macho_sub_library_command::set_cmdsize(uint32_t _value) { + ENDIAN_WRITE32(content.cmdsize, _value); +} + +inline __attribute__((always_inline)) +const char* macho_sub_library_command::name() const { + return (char*)(&content) + ENDIAN_READ32(content.sub_library.offset); +} + +inline __attribute__((always_inline)) +void macho_sub_library_command::set_name_offset() { + ENDIAN_WRITE32(content.sub_library.offset, name_offset); +} + + + +#undef routines_command +#undef routines_command_64 +class macho_routines_command { +public: + uint32_t cmd() const; + void set_cmd(uint32_t); + + uint32_t cmdsize() const; + void set_cmdsize(uint32_t); + + uint64_t init_address() const; + void set_init_address(uint64_t); + + uint64_t init_module() const; + void set_init_module(uint64_t); + +#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) + enum { size = sizeof(struct routines_command_64) }; + enum { command = LC_ROUTINES_64 }; +#else + enum { size = sizeof(struct routines_command) }; + enum { command = LC_ROUTINES }; +#endif + +private: +#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) + struct routines_command_64 content; +#else + struct routines_command content; +#endif +}; +#define routines_command __my_bad +#define routines_command_64 __my_bad + +inline __attribute__((always_inline)) +uint32_t macho_routines_command::cmd() const { + return ENDIAN_READ32(content.cmd); +} + +inline __attribute__((always_inline)) +void macho_routines_command::set_cmd(uint32_t _value) { + ENDIAN_WRITE32(content.cmd, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_routines_command::cmdsize() const { + return ENDIAN_READ32(content.cmdsize); +} + +inline __attribute__((always_inline)) +void macho_routines_command::set_cmdsize(uint32_t _value) { + ENDIAN_WRITE32(content.cmdsize, _value); +} + +inline __attribute__((always_inline)) +uint64_t macho_routines_command::init_address() const { + return ENDIAN_SWAP64(content.init_address); +} + +inline __attribute__((always_inline)) +void macho_routines_command::set_init_address(uint64_t _value) { + content.init_address = ENDIAN_SWAP64(_value); +} + +inline __attribute__((always_inline)) +uint64_t macho_routines_command::init_module() const { + return ENDIAN_SWAP64(content.init_module); +} + +inline __attribute__((always_inline)) +void macho_routines_command::set_init_module(uint64_t _value) { + content.init_module = ENDIAN_SWAP64(_value); +} + + + +#undef symtab_command +class macho_symtab_command { +public: + uint32_t cmd() const; + void set_cmd(uint32_t); + + uint32_t cmdsize() const; + void set_cmdsize(uint32_t); + + uint32_t symoff() const; + void set_symoff(uint32_t); + + uint32_t nsyms() const; + void set_nsyms(uint32_t); + + uint32_t stroff() const; + void set_stroff(uint32_t); + + uint32_t strsize() const; + void set_strsize(uint32_t); + + enum { size = sizeof(struct symtab_command ) }; + +private: + struct symtab_command content; +}; +#define symtab_command __my_bad + + +inline __attribute__((always_inline)) +uint32_t macho_symtab_command::cmd() const { + return ENDIAN_READ32(content.cmd); +} + +inline __attribute__((always_inline)) +void macho_symtab_command::set_cmd(uint32_t _value) { + ENDIAN_WRITE32(content.cmd, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_symtab_command::cmdsize() const { + return ENDIAN_READ32(content.cmdsize); +} + +inline __attribute__((always_inline)) +void macho_symtab_command::set_cmdsize(uint32_t _value) { + ENDIAN_WRITE32(content.cmdsize, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_symtab_command::symoff() const { + return ENDIAN_READ32(content.symoff); +} + +inline __attribute__((always_inline)) +void macho_symtab_command::set_symoff(uint32_t _value) { + ENDIAN_WRITE32(content.symoff, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_symtab_command::nsyms() const { + return ENDIAN_READ32(content.nsyms); +} + +inline __attribute__((always_inline)) +void macho_symtab_command::set_nsyms(uint32_t _value) { + ENDIAN_WRITE32(content.nsyms, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_symtab_command::stroff() const { + return ENDIAN_READ32(content.stroff); +} + +inline __attribute__((always_inline)) +void macho_symtab_command::set_stroff(uint32_t _value) { + ENDIAN_WRITE32(content.stroff, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_symtab_command::strsize() const { + return ENDIAN_READ32(content.strsize); +} + +inline __attribute__((always_inline)) +void macho_symtab_command::set_strsize(uint32_t _value) { + ENDIAN_WRITE32(content.strsize, _value); +} + + +#undef dysymtab_command +class macho_dysymtab_command { +public: + uint32_t cmd() const; + void set_cmd(uint32_t); + + uint32_t cmdsize() const; + void set_cmdsize(uint32_t); + + uint32_t ilocalsym() const; + void set_ilocalsym(uint32_t); + + uint32_t nlocalsym() const; + void set_nlocalsym(uint32_t); + + uint32_t iextdefsym() const; + void set_iextdefsym(uint32_t); + + uint32_t nextdefsym() const; + void set_nextdefsym(uint32_t); + + uint32_t iundefsym() const; + void set_iundefsym(uint32_t); + + uint32_t nundefsym() const; + void set_nundefsym(uint32_t); + + uint32_t tocoff() const; + void set_tocoff(uint32_t); + + uint32_t ntoc() const; + void set_ntoc(uint32_t); + + uint32_t modtaboff() const; + void set_modtaboff(uint32_t); + + uint32_t nmodtab() const; + void set_nmodtab(uint32_t); + + uint32_t extrefsymoff() const; + void set_extrefsymoff(uint32_t); + + uint32_t nextrefsyms() const; + void set_nextrefsyms(uint32_t); + + uint32_t indirectsymoff() const; + void set_indirectsymoff(uint32_t); + + uint32_t nindirectsyms() const; + void set_nindirectsyms(uint32_t); + + uint32_t extreloff() const; + void set_extreloff(uint32_t); + + uint32_t nextrel() const; + void set_nextrel(uint32_t); + + uint32_t locreloff() const; + void set_locreloff(uint32_t); + + uint32_t nlocrel() const; + void set_nlocrel(uint32_t); + + enum { size = sizeof(struct dysymtab_command ) }; +private: + struct dysymtab_command content; +}; +#define dysymtab_command __my_bad + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::cmd() const { + return ENDIAN_READ32(content.cmd); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_cmd(uint32_t _value) { + ENDIAN_WRITE32(content.cmd, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::cmdsize() const { + return ENDIAN_READ32(content.cmdsize); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_cmdsize(uint32_t _value) { + ENDIAN_WRITE32(content.cmdsize, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::ilocalsym() const { + return ENDIAN_READ32(content.ilocalsym); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_ilocalsym(uint32_t _value) { + ENDIAN_WRITE32(content.ilocalsym, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::nlocalsym() const { + return ENDIAN_READ32(content.nlocalsym); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_nlocalsym(uint32_t _value) { + ENDIAN_WRITE32(content.nlocalsym, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::iextdefsym() const { + return ENDIAN_READ32(content.iextdefsym); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_iextdefsym(uint32_t _value) { + ENDIAN_WRITE32(content.iextdefsym, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::nextdefsym() const { + return ENDIAN_READ32(content.nextdefsym); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_nextdefsym(uint32_t _value) { + ENDIAN_WRITE32(content.nextdefsym, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::iundefsym() const { + return ENDIAN_READ32(content.iundefsym); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_iundefsym(uint32_t _value) { + ENDIAN_WRITE32(content.iundefsym, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::nundefsym() const { + return ENDIAN_READ32(content.nundefsym); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_nundefsym(uint32_t _value) { + ENDIAN_WRITE32(content.nundefsym, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::tocoff() const { + return ENDIAN_READ32(content.tocoff); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_tocoff(uint32_t _value) { + ENDIAN_WRITE32(content.tocoff, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::ntoc() const { + return ENDIAN_READ32(content.ntoc); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_ntoc(uint32_t _value) { + ENDIAN_WRITE32(content.ntoc, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::modtaboff() const { + return ENDIAN_READ32(content.modtaboff); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_modtaboff(uint32_t _value) { + ENDIAN_WRITE32(content.modtaboff, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::nmodtab() const { + return ENDIAN_READ32(content.nmodtab); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_nmodtab(uint32_t _value) { + ENDIAN_WRITE32(content.nmodtab, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::extrefsymoff() const { + return ENDIAN_READ32(content.extrefsymoff); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_extrefsymoff(uint32_t _value) { + ENDIAN_WRITE32(content.extrefsymoff, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::nextrefsyms() const { + return ENDIAN_READ32(content.nextrefsyms); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_nextrefsyms(uint32_t _value) { + ENDIAN_WRITE32(content.nextrefsyms, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::indirectsymoff() const { + return ENDIAN_READ32(content.indirectsymoff); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_indirectsymoff(uint32_t _value) { + ENDIAN_WRITE32(content.indirectsymoff, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::nindirectsyms() const { + return ENDIAN_READ32(content.nindirectsyms); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_nindirectsyms(uint32_t _value) { + ENDIAN_WRITE32(content.nindirectsyms, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::extreloff() const { + return ENDIAN_READ32(content.extreloff); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_extreloff(uint32_t _value) { + ENDIAN_WRITE32(content.extreloff, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::nextrel() const { + return ENDIAN_READ32(content.nextrel); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_nextrel(uint32_t _value) { + ENDIAN_WRITE32(content.nextrel, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::locreloff() const { + return ENDIAN_READ32(content.locreloff); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_locreloff(uint32_t _value) { + ENDIAN_WRITE32(content.locreloff, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_dysymtab_command::nlocrel() const { + return ENDIAN_READ32(content.nlocrel); +} + +inline __attribute__((always_inline)) +void macho_dysymtab_command::set_nlocrel(uint32_t _value) { + ENDIAN_WRITE32(content.nlocrel, _value); +} + + + +#undef twolevel_hints_command +class macho_twolevel_hints_command { +public: + uint32_t cmd() const; + void set_cmd(uint32_t); + + uint32_t cmdsize() const; + void set_cmdsize(uint32_t); + + uint32_t offset() const; + void set_offset(uint32_t); + + uint32_t nhints() const; + void set_nhints(uint32_t); + +private: + struct twolevel_hints_command content; +}; +#define twolevel_hints_command __my_bad + +inline __attribute__((always_inline)) +uint32_t macho_twolevel_hints_command::cmd() const { + return ENDIAN_READ32(content.cmd); +} + +inline __attribute__((always_inline)) +void macho_twolevel_hints_command::set_cmd(uint32_t _value) { + ENDIAN_WRITE32(content.cmd, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_twolevel_hints_command::cmdsize() const { + return ENDIAN_READ32(content.cmdsize); +} + +inline __attribute__((always_inline)) +void macho_twolevel_hints_command::set_cmdsize(uint32_t _value) { + ENDIAN_WRITE32(content.cmdsize, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_twolevel_hints_command::offset() const { + return ENDIAN_READ32(content.offset); +} + +inline __attribute__((always_inline)) +void macho_twolevel_hints_command::set_offset(uint32_t _value) { + ENDIAN_WRITE32(content.offset, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_twolevel_hints_command::nhints() const { + return ENDIAN_READ32(content.nhints); +} + +inline __attribute__((always_inline)) +void macho_twolevel_hints_command::set_nhints(uint32_t _value) { + ENDIAN_WRITE32(content.nhints, _value); +} + + +#undef thread_command +class macho_thread_command { +public: + uint32_t cmd() const; + void set_cmd(uint32_t); + + uint32_t cmdsize() const; + void set_cmdsize(uint32_t); + + uint32_t flavor() const; + void set_flavor(uint32_t); + + uint32_t count() const; + void set_count(uint32_t); + + uint32_t threadState32(uint32_t index) const; + void set_threadState32(uint32_t index, uint32_t value); + + uint64_t threadState64(uint32_t offset) const; + void set_threadState64(uint32_t index, uint64_t value); + + enum { size = sizeof(struct thread_command) + 8 }; + +private: + struct thread_command content; + uint32_t content_flavor; + uint32_t content_count; + uint32_t threadState[1]; +}; +#define thread_command __my_bad + +inline __attribute__((always_inline)) +uint32_t macho_thread_command::cmd() const { + return ENDIAN_READ32(content.cmd); +} + +inline __attribute__((always_inline)) +void macho_thread_command::set_cmd(uint32_t _value) { + ENDIAN_WRITE32(content.cmd, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_thread_command::cmdsize() const { + return ENDIAN_READ32(content.cmdsize); +} + +inline __attribute__((always_inline)) +void macho_thread_command::set_cmdsize(uint32_t _value) { + ENDIAN_WRITE32(content.cmdsize, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_thread_command::flavor() const { + return ENDIAN_READ32(content_flavor); +} + +inline __attribute__((always_inline)) +void macho_thread_command::set_flavor(uint32_t _value) { + ENDIAN_WRITE32(content_flavor, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_thread_command::count() const { + return ENDIAN_READ32(content_count); +} + +inline __attribute__((always_inline)) +void macho_thread_command::set_count(uint32_t _value) { + ENDIAN_WRITE32(content_count, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_thread_command::threadState32(uint32_t index) const +{ + return ENDIAN_READ32(threadState[index]); +} + +inline __attribute__((always_inline)) +void macho_thread_command::set_threadState32(uint32_t index, uint32_t _value) +{ + ENDIAN_WRITE32(threadState[index], _value); +} + +inline __attribute__((always_inline)) +uint64_t macho_thread_command::threadState64(uint32_t index) const +{ + uint64_t temp = *((uint64_t*)(&threadState[index])); + return ENDIAN_SWAP64(temp); +} + +inline __attribute__((always_inline)) +void macho_thread_command::set_threadState64(uint32_t index, uint64_t _value) +{ + *((uint64_t*)(&threadState[index])) = ENDIAN_SWAP64(_value); +} + + + +#undef nlist +#undef nlist_64 +class macho_nlist { +public: + uint32_t n_strx() const; + void set_n_strx(uint32_t); + + uint8_t n_type() const; + void set_n_type(uint8_t); + + uint8_t n_sect() const; + void set_n_sect(uint8_t); + + uint16_t n_desc() const; + void set_n_desc(uint16_t); + + uint64_t n_value() const; + void set_n_value(uint64_t); + + +#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) + enum { size = sizeof(struct nlist_64) }; +#else + enum { size = sizeof(struct nlist) }; +#endif + +private: +#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) + struct nlist_64 content; +#else + struct nlist content; +#endif +}; +#define nlist __my_bad +#define nlist_64 __my_bad + +inline __attribute__((always_inline)) +uint32_t macho_nlist::n_strx() const { + return ENDIAN_READ32(content.n_un.n_strx); +} + +inline __attribute__((always_inline)) +void macho_nlist::set_n_strx(uint32_t _value) { + ENDIAN_WRITE32(content.n_un.n_strx, _value); +} + +inline __attribute__((always_inline)) +uint8_t macho_nlist::n_type() const { + return content.n_type; +} + +inline __attribute__((always_inline)) +void macho_nlist::set_n_type(uint8_t _value) { + content.n_type = _value; +} + +inline __attribute__((always_inline)) +uint8_t macho_nlist::n_sect() const { + return content.n_sect; +} + +inline __attribute__((always_inline)) +void macho_nlist::set_n_sect(uint8_t _value) { + content.n_sect = _value; +} + +inline __attribute__((always_inline)) +uint16_t macho_nlist::n_desc() const { + return ENDIAN_READ16(content.n_desc); +} + +inline __attribute__((always_inline)) +void macho_nlist::set_n_desc(uint16_t _value) { + ENDIAN_WRITE16(content.n_desc, _value); +} + +inline __attribute__((always_inline)) +uint64_t macho_nlist::n_value() const { +#if defined(ARCH_PPC64) + return ENDIAN_SWAP64(content.n_value); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + return ENDIAN_READ32(content.n_value); +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +void macho_nlist::set_n_value(uint64_t _value) { + content.n_value = ENDIAN_SWAP64(_value); +} + + + +#undef relocation_info +class macho_relocation_info { +public: + int32_t r_address() const; + void set_r_address(int32_t); + + uint32_t r_symbolnum() const; + void set_r_symbolnum(uint32_t); + + bool r_pcrel() const; + void set_r_pcrel(bool); + + uint8_t r_length() const; + void set_r_length(uint8_t); + + bool r_extern() const; + void set_r_extern(bool); + + uint8_t r_type() const; + void set_r_type(uint8_t); + + enum { size = sizeof(struct relocation_info) }; +#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) + enum { pointer_length = 3 }; +#else + enum { pointer_length = 2 }; +#endif + +private: + struct relocation_info content; +}; +#define relocation_info __my_bad + + +inline __attribute__((always_inline)) +int32_t macho_relocation_info::r_address() const { + return ENDIAN_READ32(content.r_address); +} + +inline __attribute__((always_inline)) +void macho_relocation_info::set_r_address(int32_t _value) { + ENDIAN_WRITE32(content.r_address, _value); +} + +inline __attribute__((always_inline)) +uint32_t macho_relocation_info::r_symbolnum() const { + uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + return (temp >> 8); +#elif defined(ARCH_I386) + return temp & 0x00FFFFFF; +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +void macho_relocation_info::set_r_symbolnum(uint32_t _value) { + uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + temp &= 0x000000FF; + temp |= ((_value & 0x00FFFFFF) << 8); +#elif defined(ARCH_I386) + temp &= 0xFF000000; + temp |= (_value & 0x00FFFFFF); +#else + #error unknown architecture +#endif + ENDIAN_WRITE32(((uint32_t*)&content)[1], temp); +} + +inline __attribute__((always_inline)) +bool macho_relocation_info::r_pcrel() const { + uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + return ((temp & 0x00000080) != 0); +#elif defined(ARCH_I386) + return ((temp & 0x01000000) != 0); +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +void macho_relocation_info::set_r_pcrel(bool _value) { + uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + temp &= 0xFFFFFF7F; + if ( _value ) + temp |= 0x00000080; +#elif defined(ARCH_I386) + temp &= 0x7FFFFFFF; + if ( _value ) + temp |= 0x01000000; +#else + #error unknown architecture +#endif + ENDIAN_WRITE32(((uint32_t*)&content)[1], temp); +} + +inline __attribute__((always_inline)) +uint8_t macho_relocation_info::r_length() const { + uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + return ((temp & 0x00000060) >> 5); +#elif defined(ARCH_I386) + return ((temp & 0x06000000) >> 25); +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +void macho_relocation_info::set_r_length(uint8_t _value) { + uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + temp &= 0xFFFFFF9F; + temp |= ((_value & 0x03) << 5); +#elif defined(ARCH_I386) + temp &= 0xF9FFFFFF; + temp |= ((_value & 0x03) << 25); +#else + #error unknown architecture +#endif + ENDIAN_WRITE32(((uint32_t*)&content)[1], temp); +} + +inline __attribute__((always_inline)) +bool macho_relocation_info::r_extern() const { + uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + return ((temp & 0x00000010) != 0); +#elif defined(ARCH_I386) + return ((temp & 0x08000000) != 0); +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +void macho_relocation_info::set_r_extern(bool _value) { + uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + temp &= 0xFFFFFFEF; + if ( _value ) + temp |= 0x00000010; +#elif defined(ARCH_I386) + temp &= 0xEFFFFFFF; + if ( _value ) + temp |= 0x08000000; +#else + #error unknown architecture +#endif + ENDIAN_WRITE32(((uint32_t*)&content)[1], temp); +} + +inline __attribute__((always_inline)) +uint8_t macho_relocation_info::r_type() const { + uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + return (temp & 0x0000000F); +#elif defined(ARCH_I386) + return ((temp & 0xF0000000) >> 28); +#else + #error unknown architecture +#endif +} + +inline __attribute__((always_inline)) +void macho_relocation_info::set_r_type(uint8_t _value) { + uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + temp &= 0xFFFFFFF0; + temp |= (_value & 0x0F); +#elif defined(ARCH_I386) + temp &= 0x0FFFFFFF; + temp |= ((_value & 0x0F) << 28); +#else + #error unknown architecture +#endif + ENDIAN_WRITE32(((uint32_t*)&content)[1], temp); +} + + + +#undef scattered_relocation_info +class macho_scattered_relocation_info { +public: + bool r_scattered() const; + void set_r_scattered(bool); + + bool r_pcrel() const; + void set_r_pcrel(bool); + + uint8_t r_length() const; + void set_r_length(uint8_t); + + uint8_t r_type() const; + void set_r_type(uint8_t); + + uint32_t r_address() const; + void set_r_address(uint32_t); + + int32_t r_value() const; + void set_r_value(int32_t); + +private: + struct scattered_relocation_info content; +}; +#define scattered_relocation_info __my_bad + +inline __attribute__((always_inline)) +bool macho_scattered_relocation_info::r_scattered() const { + uint32_t temp = *((const uint32_t*)&content); + temp = ENDIAN_READ32(temp); + return ((temp & 0x80000000) != 0); +} + +inline __attribute__((always_inline)) +void macho_scattered_relocation_info::set_r_scattered(bool _value) { + uint32_t temp = ENDIAN_READ32(*((const uint32_t*)&content)); + if ( _value ) + temp |= 0x80000000; + else + temp &= ~0x80000000; + ENDIAN_WRITE32(*((uint32_t*)&content), temp); +} + +inline __attribute__((always_inline)) +bool macho_scattered_relocation_info::r_pcrel() const { + uint32_t temp = *((const uint32_t*)&content); + temp = ENDIAN_READ32(temp); + return ((temp & 0x40000000) != 0); +} + +inline __attribute__((always_inline)) +void macho_scattered_relocation_info::set_r_pcrel(bool _value) { + uint32_t temp = ENDIAN_READ32(*((const uint32_t*)&content)); + if ( _value ) + temp |= 0x40000000; + else + temp &= ~0x40000000; + ENDIAN_WRITE32(*((uint32_t*)&content), temp); +} + +inline __attribute__((always_inline)) +uint8_t macho_scattered_relocation_info::r_length() const { + uint32_t temp = ENDIAN_READ32(*((const uint32_t*)&content)); + return (temp >> 28) & 0x03; +} + +inline __attribute__((always_inline)) +void macho_scattered_relocation_info::set_r_length(uint8_t _value) { + uint32_t temp = ENDIAN_READ32(*((const uint32_t*)&content)); + temp &= 0xCFFFFFFF; + temp |= ((_value & 0x03) << 28); + ENDIAN_WRITE32(*((uint32_t*)&content), temp); +} + +inline __attribute__((always_inline)) +uint8_t macho_scattered_relocation_info::r_type() const { + uint32_t temp = ENDIAN_READ32(*((const uint32_t*)&content)); + return (temp >> 24) & 0x0F; +} + +inline __attribute__((always_inline)) +void macho_scattered_relocation_info::set_r_type(uint8_t _value) { + uint32_t temp = ENDIAN_READ32(*((const uint32_t*)&content)); + temp &= 0xF0FFFFFF; + temp |= ((_value &0x0F) << 24); + ENDIAN_WRITE32(*((uint32_t*)&content), temp); +} + +inline __attribute__((always_inline)) +uint32_t macho_scattered_relocation_info::r_address() const { + uint32_t temp = ENDIAN_READ32(*((const uint32_t*)&content)); + return temp & 0x00FFFFFF; +} + +inline __attribute__((always_inline)) +void macho_scattered_relocation_info::set_r_address(uint32_t _value) { + uint32_t temp = ENDIAN_READ32(*((const uint32_t*)&content)); + _value &= 0x00FFFFFF; + temp &= 0xFF000000; + temp |= _value; + ENDIAN_WRITE32(*((uint32_t*)&content), temp); +} + +inline __attribute__((always_inline)) +int32_t macho_scattered_relocation_info::r_value() const { + return ENDIAN_READ32(content.r_value); +} + +inline __attribute__((always_inline)) +void macho_scattered_relocation_info::set_r_value(int32_t _value) { + ENDIAN_WRITE32(content.r_value, _value); +} + + + + diff --git a/src/ObjDump.cpp b/src/ObjDump.cpp new file mode 100644 index 0000000..e3d6082 --- /dev/null +++ b/src/ObjDump.cpp @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "ObjectFile.h" +#include "ObjectFileMachO-all.h" + + __attribute__((noreturn)) +void throwf(const char* format, ...) +{ + va_list list; + char* p; + va_start(list, format); + vasprintf(&p, format, list); + va_end(list); + + const char* t = p; + throw t; +} + +static void dumpStabs(std::vector* stabs) +{ + // debug info + const int stabCount = stabs->size(); + printf("stabs: (%u)\n", stabCount); + for (int i=0; i < stabCount; ++i) { + ObjectFile::StabsInfo& stab = (*stabs)[i]; + const char* code = "?????"; + switch (stab.type) { + case N_GSYM: + code = " GSYM"; + break; + case N_FNAME: + code = "FNAME"; + break; + case N_FUN: + code = " FUN"; + break; + case N_STSYM: + code = "STSYM"; + break; + case N_LCSYM: + code = "LCSYM"; + break; + case N_BNSYM: + code = "BNSYM"; + break; + case N_OPT: + code = " OPT"; + break; + case N_RSYM: + code = " RSYM"; + break; + case N_SLINE: + code = "SLINE"; + break; + case N_ENSYM: + code = "ENSYM"; + break; + case N_SSYM: + code = " SSYM"; + break; + case N_SO: + code = " SO"; + break; + case N_LSYM: + code = " LSYM"; + break; + case N_BINCL: + code = "BINCL"; + break; + case N_SOL: + code = " SOL"; + break; + case N_PARAMS: + code = "PARMS"; + break; + case N_VERSION: + code = " VERS"; + break; + case N_OLEVEL: + code = "OLEVL"; + break; + case N_PSYM: + code = " PSYM"; + break; + case N_EINCL: + code = "EINCL"; + break; + case N_ENTRY: + code = "ENTRY"; + break; + case N_LBRAC: + code = "LBRAC"; + break; + case N_EXCL: + code = " EXCL"; + break; + case N_RBRAC: + code = "RBRAC"; + break; + case N_BCOMM: + code = "BCOMM"; + break; + case N_ECOMM: + code = "ECOMM"; + break; + case N_LENG: + code = "LENG"; + break; + } + printf(" %08X %02X %04X %s %s\n", (uint32_t)stab.atomOffset, stab.other, stab.desc, code, stab.string); + } +} + + +static void dumpAtom(ObjectFile::Atom* atom) +{ + //printf("atom: %p\n", atom); + + // name + printf("name: %s\n", atom->getDisplayName()); + + // scope + switch ( atom->getScope() ) { + case ObjectFile::Atom::scopeTranslationUnit: + printf("scope: translation unit\n"); + break; + case ObjectFile::Atom::scopeLinkageUnit: + printf("scope: linkage unit\n"); + break; + case ObjectFile::Atom::scopeGlobal: + printf("scope: global\n"); + break; + default: + printf("scope: unknown\n"); + } + + // segment and section + printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName()); + + // attributes + printf("attrs: "); + if ( atom->isWeakDefinition() ) + printf("weak "); + if ( atom->isCoalesableByName() ) + printf("coalesce-by-name "); + if ( atom->isCoalesableByValue() ) + printf("coalesce-by-value "); + if ( atom->dontDeadStrip() ) + printf("dont-dead-strip "); + if ( atom->isZeroFill() ) + printf("zero-fill "); + printf("\n"); + + // size + printf("size: 0x%012llX\n", atom->getSize()); + + // alignment + printf("align: %d\n", atom->getAlignment()); + + // content + uint64_t size = atom->getSize(); + if ( size < 4096 ) { + uint8_t content[size]; + atom->copyRawContent(content); + printf("content: "); + if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) { + printf("\"%s\"", content); + } + else { + for (unsigned int i=0; i < size; ++i) + printf("%02X ", content[i]); + } + } + printf("\n"); + + // references + std::vector& references = atom->getReferences(); + const int refCount = references.size(); + printf("references: (%u)\n", refCount); + for (int i=0; i < refCount; ++i) { + ObjectFile::Reference* ref = references[i]; + printf(" %s\n", ref->getDescription()); + } + + // debug info + std::vector* stabs = atom->getStabsDebugInfo(); + if ( stabs != NULL ) + dumpStabs(stabs); +} + + +static void dumpFile(ObjectFile::Reader* reader) +{ +#if 0 + // debug info + std::vector* stabs = reader->getStabsDebugInfo(); + if ( stabs != NULL ) + dumpStabs(stabs); +#endif + // atom content + std::vector atoms = reader->getAtoms(); + const int atomCount = atoms.size(); + for(int i=0; i < atomCount; ++i) { + dumpAtom(atoms[i]); + printf("\n"); + } +} + + +static ObjectFile::Reader* createReader(const char* path, const ObjectFile::ReaderOptions& options) +{ + struct stat stat_buf; + + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throw "cannot open file"; + ::fstat(fd, &stat_buf); + char* p = (char*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE, fd, 0); + ::close(fd); + const mach_header* mh = (mach_header*)p; + if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + const struct fat_header* fh = (struct fat_header*)p; + const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); + for (unsigned long i=0; i < fh->nfat_arch; ++i) { + if ( archs[i].cputype == CPU_TYPE_POWERPC64 ) { + p = p + archs[i].offset; + mh = (struct mach_header*)p; + } + } + } + if ( mh->magic == MH_MAGIC ) { + if ( mh->filetype == MH_OBJECT ) { + switch ( mh->cputype ) { + case CPU_TYPE_I386: + return i386::ObjectFileMachO::MakeReader((class i386::macho_header*)mh, path, options); + case CPU_TYPE_POWERPC: + return ppc::ObjectFileMachO::MakeReader((class ppc::macho_header*)mh, path, options); + case CPU_TYPE_POWERPC64: + return ppc64::ObjectFileMachO::MakeReader((class ppc64::macho_header*)mh, path, options); + default: + throw "unknown mach-o cpu type"; + } + } + if ( mh->filetype == MH_DYLIB ) + return ppc::ObjectFileDylibMachO::MakeReader((class ppc::macho_header*)mh, path, options); + throw "unknown mach-o file type"; + } + else if ( mh->magic == MH_MAGIC_64 ) { + if ( mh->filetype == MH_OBJECT ) + return ppc64::ObjectFileMachO::MakeReader((class ppc64::macho_header*)mh, path, options); + if ( mh->filetype == MH_DYLIB ) + return ppc64::ObjectFileDylibMachO::MakeReader((class ppc64::macho_header*)mh, path, options); + throw "unknown mach-o file type"; + } + else if ( mh->magic == OSSwapInt32(MH_MAGIC) ) { + if ( mh->filetype == OSSwapInt32(MH_OBJECT) ) { + switch ( OSSwapInt32(mh->cputype) ) { + case CPU_TYPE_I386: + return i386::ObjectFileMachO::MakeReader((class i386::macho_header*)mh, path, options); + case CPU_TYPE_POWERPC: + return ppc::ObjectFileMachO::MakeReader((class ppc::macho_header*)mh, path, options); + case CPU_TYPE_POWERPC64: + return ppc64::ObjectFileMachO::MakeReader((class ppc64::macho_header*)mh, path, options); + default: + throw "unknown mach-o cpu type"; + } + } + if ( mh->filetype == OSSwapInt32(MH_DYLIB) ) + return ppc::ObjectFileDylibMachO::MakeReader((class ppc::macho_header*)mh, path, options); + throw "unknown mach-o file type"; + } + else if ( mh->magic == OSSwapInt32(MH_MAGIC_64) ) { + if ( mh->filetype == OSSwapInt32(MH_OBJECT) ) + return ppc64::ObjectFileMachO::MakeReader((class ppc64::macho_header*)mh, path, options); + if ( mh->filetype == OSSwapInt32(MH_DYLIB) ) + return ppc64::ObjectFileDylibMachO::MakeReader((class ppc64::macho_header*)mh, path, options); + throw "unknown mach-o file type"; + } + throw "unknown file type"; +} + + +int main(int argc, const char* argv[]) +{ + ObjectFile::ReaderOptions options; + //const char* path = argv[1]; + //ObjectFile::Reader* reader = ObjectFile::Reader::createReader(path); + try { + ObjectFile::Reader* reader = createReader("/tmp/gcov-1.o", options); + + dumpFile(reader); + } + catch (const char* msg) { + fprintf(stderr, "ObjDump failed: %s\n", msg); + } + + return 0; +} + + + diff --git a/src/ObjectFile.h b/src/ObjectFile.h new file mode 100644 index 0000000..5a80c26 --- /dev/null +++ b/src/ObjectFile.h @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __OBJECTFILE__ +#define __OBJECTFILE__ + +#include +#include +#include + + + +namespace ObjectFile { + +struct StabsInfo +{ + uint64_t atomOffset; + const char* string; + uint8_t type; + uint8_t other; + uint16_t desc; +}; + +class ReaderOptions +{ +public: + ReaderOptions() : fFullyLoadArchives(false), fLoadObjcClassesInArchives(false), fFlatNamespace(false), + fStripDebugInfo(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false) {} + + bool fFullyLoadArchives; + bool fLoadObjcClassesInArchives; + bool fFlatNamespace; + bool fStripDebugInfo; + bool fTraceDylibs; + bool fTraceIndirectDylibs; + bool fTraceArchives; +}; + + +class Reader +{ +public: + static Reader* createReader(const char* path, const ReaderOptions& options); + + virtual const char* getPath() = 0; + virtual std::vector& getAtoms() = 0; + virtual std::vector* getJustInTimeAtomsFor(const char* name) = 0; + virtual std::vector* getStabsDebugInfo() = 0; + + // For Dynamic Libraries only + virtual const char* getInstallPath() { return NULL; } + virtual uint32_t getTimestamp() { return 0; } + virtual uint32_t getCurrentVersion() { return 0; } + virtual uint32_t getCompatibilityVersion() { return 0; } + virtual std::vector* getDependentLibraryPaths() { return NULL; } + virtual bool reExports(Reader*) { return false; } + virtual bool isDefinitionWeak(const Atom&){ return false; } + + + +protected: + Reader() {} +}; + +class Segment +{ +public: + virtual const char* getName() const = 0; + virtual bool isContentReadable() const = 0; + virtual bool isContentWritable() const = 0; + virtual bool isContentExecutable() const = 0; + + uint64_t getBaseAddress() const { return fBaseAddress; } + void setBaseAddress(uint64_t addr) { fBaseAddress = addr; } + +protected: + Segment() : fBaseAddress(0) {} + uint64_t fBaseAddress; +}; + +class Reference; + +class Section +{ +public: + unsigned int getIndex() { return fIndex; } + uint64_t getBaseAddress() { return fBaseAddress; } + void setBaseAddress(uint64_t addr) { fBaseAddress = addr; } + void* fOther; + +protected: + Section() : fOther(NULL), fBaseAddress(0), fIndex(0) {} + uint64_t fBaseAddress; + unsigned int fIndex; +}; + + +class ContentWriter +{ +public: + virtual void write(uint64_t atomOffset, const void* buffer, uint64_t size) = 0; +}; + +class Atom +{ +public: + enum Scope { scopeTranslationUnit, scopeLinkageUnit, scopeGlobal }; + enum WeakImportSetting { kWeakUnset, kWeakImport, kNonWeakImport }; + + virtual Reader* getFile() const = 0; + virtual const char* getName() const = 0; + virtual const char* getDisplayName() const = 0; + virtual Scope getScope() const = 0; + virtual bool isTentativeDefinition() const = 0; + virtual bool isWeakDefinition() const = 0; + virtual bool isCoalesableByName() const = 0; + virtual bool isCoalesableByValue() const = 0; + virtual bool isZeroFill() const = 0; + virtual bool dontDeadStrip() const = 0; + virtual bool dontStripName() const = 0; // referenced dynamically + virtual bool isImportProxy() const = 0; + virtual uint64_t getSize() const = 0; + virtual std::vector& getReferences() const = 0; + virtual bool mustRemainInSection() const = 0; + virtual const char* getSectionName() const = 0; + virtual Segment& getSegment() const = 0; + virtual bool requiresFollowOnAtom() const = 0; + virtual Atom& getFollowOnAtom() const = 0; + virtual std::vector* getStabsDebugInfo() const = 0; + virtual uint8_t getAlignment() const = 0; + virtual WeakImportSetting getImportWeakness() const = 0; + virtual void copyRawContent(uint8_t buffer[]) const = 0; + virtual void writeContent(bool finalLinkedImage, ContentWriter&) const = 0; + virtual void setScope(Scope) = 0; + virtual void setImportWeakness(bool weakImport) = 0; + + + uint64_t getSectionOffset() const { return fSectionOffset; } + uint64_t getSegmentOffset() const { return fSegmentOffset; } + uint64_t getAddress() const { return fSection->getBaseAddress() + fSectionOffset; } + unsigned int getSortOrder() const { return fSortOrder; } + class Section* getSection() const { return fSection; } + + void setSegmentOffset(uint64_t offset) { fSegmentOffset = offset; } + void setSectionOffset(uint64_t offset) { fSectionOffset = offset; } + void setSection(class Section* sect) { fSection = sect; } + unsigned int setSortOrder(unsigned int order); // recursively sets follow-on atoms + +protected: + Atom() : fSegmentOffset(0), fSectionOffset(0), fSortOrder(0), fSection(NULL) {} + + uint64_t fSegmentOffset; + uint64_t fSectionOffset; + unsigned int fSortOrder; + class Section* fSection; +}; + + + +// recursively sets follow-on atoms +inline unsigned int Atom::setSortOrder(unsigned int order) +{ + if ( this->requiresFollowOnAtom() ) { + fSortOrder = order; + return this->getFollowOnAtom().setSortOrder(order+1); + } + else { + fSortOrder = order; + return (order + 1); + } +} + + + +class Reference +{ +public: + enum Kind { noFixUp, pointer, ppcFixupBranch24, ppcFixupBranch14, + ppcFixupPicBaseLow16, ppcFixupPicBaseLow14, ppcFixupPicBaseHigh16, + ppcFixupAbsLow16, ppcFixupAbsLow14, ppcFixupAbsHigh16, ppcFixupAbsHigh16AddLow, + pointer32Difference, pointer64Difference, x86FixupBranch32 }; + + virtual bool isUnbound() const = 0; + virtual bool isWeakReference() const = 0; + virtual bool requiresRuntimeFixUp() const = 0; + virtual bool isLazyReference() const = 0; + virtual Kind getKind() const = 0; + virtual uint64_t getFixUpOffset() const = 0; + virtual const char* getTargetName() const = 0; + virtual Atom& getTarget() const = 0; + virtual uint64_t getTargetOffset() const = 0; + virtual Atom& getFromTarget() const = 0; + virtual const char* getFromTargetName() const = 0; + virtual uint64_t getFromTargetOffset() const = 0; + + virtual void setTarget(Atom&) = 0; + virtual void setFromTarget(Atom&) = 0; + virtual const char* getDescription() const = 0; +}; + + +}; // namespace ObjectFile + + +#endif // __OBJECTFILE__ + + + + + + diff --git a/src/Options.cpp b/src/Options.cpp new file mode 100644 index 0000000..6b913af --- /dev/null +++ b/src/Options.cpp @@ -0,0 +1,1174 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include + + +#include "Options.h" + + __attribute__((noreturn)) +void throwf(const char* format, ...) +{ + va_list list; + char* p; + va_start(list, format); + vasprintf(&p, format, list); + va_end(list); + + const char* t = p; + throw t; +} + + +Options::Options(int argc, const char* argv[]) + : fOutputFile("a.out"), fArchitecture(CPU_TYPE_POWERPC64), fOutputKind(kDynamicExecutable), fBindAtLoad(false), + fStripLocalSymbols(false), fKeepPrivateExterns(false), + fInterposable(false), fIgnoreOtherArchFiles(false), fForceSubtypeAll(false), fNameSpace(kTwoLevelNameSpace), + fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fEntryName("start"), fBaseAddress(0), + fExportMode(kExportDefault), fLibrarySearchMode(kSearchAllDirsForDylibsThenAllDirsForArchives), + fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(false), fPICTreatment(kPICError), + fWeakReferenceMismatchTreatment(kWeakReferenceMismatchError), + fUmbrellaName(NULL), fInitFunctionName(NULL), fZeroPageSize(0x1000), fStackSize(0), fStackAddr(0), fMinimumHeaderPad(0), + fCommonsMode(kCommonsIgnoreDylibs), fWarnCommons(false) +{ + this->parsePreCommandLineEnvironmentSettings(); + this->parse(argc, argv); + this->parsePostCommandLineEnvironmentSettings(); + this->checkIllegalOptionCombinations(); +} + +Options::~Options() +{ +} + + +ObjectFile::ReaderOptions& Options::readerOptions() +{ + return fReaderOptions; +} + +cpu_type_t Options::architecture() +{ + return fArchitecture; +} + + +const char* Options::getOutputFilePath() +{ + return fOutputFile; +} + + +std::vector& Options::getInputFiles() +{ + return fInputFiles; +} + +Options::OutputKind Options::outputKind() +{ + return fOutputKind; +} + +bool Options::stripLocalSymbols() +{ + return fStripLocalSymbols; +} + +bool Options::stripDebugInfo() +{ + return fReaderOptions.fStripDebugInfo; +} + +bool Options::bindAtLoad() +{ + return fBindAtLoad; +} + +bool Options::fullyLoadArchives() +{ + return fReaderOptions.fFullyLoadArchives; +} + +Options::NameSpace Options::nameSpace() +{ + return fNameSpace; +} + +const char* Options::installPath() +{ + if ( fDylibInstallName != NULL ) + return fDylibInstallName; + else + return fOutputFile; +} + +uint32_t Options::currentVersion() +{ + return fDylibCurrentVersion; +} + +uint32_t Options::compatibilityVersion() +{ + return fDylibCompatVersion; +} + +const char* Options::entryName() +{ + return fEntryName; +} + +uint64_t Options::baseAddress() +{ + return fBaseAddress; +} + +bool Options::keepPrivateExterns() +{ + return fKeepPrivateExterns; +} + +bool Options::interposable() +{ + return fInterposable; +} + +bool Options::ignoreOtherArchInputFiles() +{ + return fIgnoreOtherArchFiles; +} + +bool Options::forceCpuSubtypeAll() +{ + return fForceSubtypeAll; +} + +bool Options::traceDylibs() +{ + return fReaderOptions.fTraceDylibs; +} + +bool Options::traceArchives() +{ + return fReaderOptions.fTraceArchives; +} + +Options::UndefinedTreatment Options::undefinedTreatment() +{ + return fUndefinedTreatment; +} + +Options::WeakReferenceMismatchTreatment Options::weakReferenceMismatchTreatment() +{ + return fWeakReferenceMismatchTreatment; +} + +const char* Options::umbrellaName() +{ + return fUmbrellaName; +} + +uint64_t Options::zeroPageSize() +{ + return fZeroPageSize; +} + +bool Options::hasCustomStack() +{ + return (fStackSize != 0); +} + +uint64_t Options::customStackSize() +{ + return fStackSize; +} + +uint64_t Options::customStackAddr() +{ + return fStackAddr; +} + +std::vector& Options::initialUndefines() +{ + return fInitialUndefines; +} + +const char* Options::initFunctionName() +{ + return fInitFunctionName; +} + +bool Options::hasExportRestrictList() +{ + return (fExportMode != kExportDefault); +} + +uint32_t Options::minimumHeaderPad() +{ + return fMinimumHeaderPad; +} + +std::vector& Options::extraSections() +{ + return fExtraSections; +} + +std::vector& Options::sectionAlignments() +{ + return fSectionAlignments; +} + + +Options::CommonsMode Options::commonsMode() +{ + return fCommonsMode; +} + +bool Options::warnCommons() +{ + return fWarnCommons; +} + + +bool Options::shouldExport(const char* symbolName) +{ + switch (fExportMode) { + case kExportSome: + return ( fExportSymbols.find(symbolName) != fExportSymbols.end() ); + case kDontExportSome: + return ( fDontExportSymbols.find(symbolName) == fDontExportSymbols.end() ); + case kExportDefault: + return true; + } + throw "internal error"; +} + + +void Options::parseArch(const char* architecture) +{ + if ( architecture == NULL ) + throw "-arch must be followed by an architecture string"; + if ( strcmp(architecture, "ppc") == 0 ) + fArchitecture = CPU_TYPE_POWERPC; + else if ( strcmp(architecture, "ppc64") == 0 ) + fArchitecture = CPU_TYPE_POWERPC64; + else if ( strcmp(architecture, "i386") == 0 ) + fArchitecture = CPU_TYPE_I386; + else + throw "-arch followed by unknown architecture name"; +} + +bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) +{ + struct stat statBuffer; + char possiblePath[strlen(dir)+strlen(rootName)+20]; + sprintf(possiblePath, format, dir, rootName); + if ( stat(possiblePath, &statBuffer) == 0 ) { + result.path = strdup(possiblePath); + result.fileLen = statBuffer.st_size; + return true; + } + return false; +} + + +Options::FileInfo Options::findLibrary(const char* rootName) +{ + FileInfo result; + const int rootNameLen = strlen(rootName); + // if rootName ends in .o there is no .a vs .dylib choice + if ( (rootNameLen > 3) && (strcmp(&rootName[rootNameLen-2], ".o") == 0) ) { + for (std::vector::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { + const char* dir = *it; + if ( checkForFile("%s/%s", dir, rootName, result) ) + return result; + } + } + else { + bool lookForDylibs = ( fOutputKind != Options::kDyld); + switch ( fLibrarySearchMode ) { + case kSearchAllDirsForDylibsThenAllDirsForArchives: + // first look in all directories for just for dylibs + if ( lookForDylibs ) { + for (std::vector::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { + const char* dir = *it; + if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) ) + return result; + } + } + // next look in all directories for just for archives + for (std::vector::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { + const char* dir = *it; + if ( checkForFile("%s/lib%s.a", dir, rootName, result) ) + return result; + } + break; + + case kSearchDylibAndArchiveInEachDir: + // look in each directory for just for a dylib then for an archive + for (std::vector::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { + const char* dir = *it; + if ( lookForDylibs && checkForFile("%s/lib%s.dylib", dir, rootName, result) ) + return result; + if ( checkForFile("%s/lib%s.a", dir, rootName, result) ) + return result; + } + break; + } + } + throwf("library not found for -l%s", rootName); +} + + +Options::FileInfo Options::findFramework(const char* rootName) +{ + struct stat statBuffer; + const int rootNameLen = strlen(rootName); + for (std::vector::iterator it = fFrameworkSearchPaths.begin(); it != fFrameworkSearchPaths.end(); it++) { + const char* dir = *it; + char possiblePath[strlen(dir)+2*rootNameLen+20]; + strcpy(possiblePath, dir); + strcat(possiblePath, "/"); + strcat(possiblePath, rootName); + strcat(possiblePath, ".framework/"); + strcat(possiblePath, rootName); + if ( stat(possiblePath, &statBuffer) == 0 ) { + FileInfo result; + result.path = strdup(possiblePath); + result.fileLen = statBuffer.st_size; + return result; + } + } + throwf("framework not found %s", rootName); +} + + +Options::FileInfo Options::makeFileInfo(const char* path) +{ + struct stat statBuffer; + if ( stat(path, &statBuffer) == 0 ) { + FileInfo result; + result.path = strdup(path); + result.fileLen = statBuffer.st_size; + return result; + } + else { + throwf("file not found: %s", path); + } +} + +void Options::loadFileList(const char* fileOfPaths) +{ + FILE* file = fopen(fileOfPaths, "r"); + if ( file == NULL ) + throwf("-filelist file not found: %s\n", fileOfPaths); + + char path[1024]; + while ( fgets(path, 1024, file) != NULL ) { + path[1023] = '\0'; + char* eol = strchr(path, '\n'); + if ( eol != NULL ) + *eol = '\0'; + + fInputFiles.push_back(makeFileInfo(path)); + } + fclose(file); +} + + +void Options::loadExportFile(const char* fileOfExports, const char* option, NameSet& set) +{ + // read in whole file + int fd = ::open(fileOfExports, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open %s file: %s", option, fileOfExports); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + char* p = (char*)malloc(stat_buf.st_size); + if ( p == NULL ) + throwf("can't process %s file: %s", option, fileOfExports); + + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) + throwf("can't read %s file: %s", option, fileOfExports); + + ::close(fd); + + // parse into symbols and add to hash_set + char * const end = &p[stat_buf.st_size]; + enum { lineStart, inSymbol, inComment } state = lineStart; + char* symbolStart = NULL; + for (char* s = p; s < end; ++s ) { + switch ( state ) { + case lineStart: + if ( *s =='#' ) { + state = inComment; + } + else if ( !isspace(*s) ) { + state = inSymbol; + symbolStart = s; + } + break; + case inSymbol: + if ( *s == '\n' ) { + *s = '\0'; + // removing any trailing spaces + char* last = s-1; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + set.insert(symbolStart); + symbolStart = NULL; + state = lineStart; + } + break; + case inComment: + if ( *s == '\n' ) + state = lineStart; + break; + } + } + // Note: we do not free() the malloc buffer, because the strings are used by the export-set hash table +} + +void Options::setUndefinedTreatment(const char* treatment) +{ + if ( treatment == NULL ) + throw "-undefined missing [ warning | error | suppress | dynamic_lookup ]"; + + if ( strcmp(treatment, "warning") == 0 ) + fUndefinedTreatment = kUndefinedWarning; + else if ( strcmp(treatment, "error") == 0 ) + fUndefinedTreatment = kUndefinedError; + else if ( strcmp(treatment, "suppress") == 0 ) + fUndefinedTreatment = kUndefinedSuppress; + else if ( strcmp(treatment, "dynamic_lookup") == 0 ) + fUndefinedTreatment = kUndefinedDynamicLookup; + else + throw "invalid option to -undefined [ warning | error | suppress | dynamic_lookup ]"; +} + +void Options::setReadOnlyRelocTreatment(const char* treatment) +{ + if ( treatment == NULL ) + throw "-read_only_relocs missing [ warning | error | suppress ]"; + + if ( strcmp(treatment, "warning") == 0 ) + throw "-read_only_relocs warning not supported"; + else if ( strcmp(treatment, "suppress") == 0 ) + throw "-read_only_relocs suppress not supported"; + else if ( strcmp(treatment, "error") != 0 ) + throw "invalid option to -read_only_relocs [ warning | error | suppress | dynamic_lookup ]"; +} + +void Options::setPICTreatment(const char* treatment) +{ + if ( treatment == NULL ) + throw "-sect_diff_relocs missing [ warning | error | suppress ]"; + + if ( strcmp(treatment, "warning") == 0 ) + fPICTreatment = kPICWarning; + else if ( strcmp(treatment, "error") == 0 ) + fPICTreatment = kPICError; + else if ( strcmp(treatment, "suppress") == 0 ) + fPICTreatment = kPICSuppress; + else + throw "invalid option to -sect_diff_relocs [ warning | error | suppress ]"; +} + +void Options::setWeakReferenceMismatchTreatment(const char* treatment) +{ + if ( treatment == NULL ) + throw "-weak_reference_mismatches missing [ error | weak | non-weak ]"; + + if ( strcmp(treatment, "error") == 0 ) + fWeakReferenceMismatchTreatment = kWeakReferenceMismatchError; + else if ( strcmp(treatment, "weak") == 0 ) + fWeakReferenceMismatchTreatment = kWeakReferenceMismatchWeak; + else if ( strcmp(treatment, "non-weak") == 0 ) + fWeakReferenceMismatchTreatment = kWeakReferenceMismatchNonWeak; + else + throw "invalid option to -weak_reference_mismatches [ error | weak | non-weak ]"; +} + +Options::CommonsMode Options::parseCommonsTreatment(const char* mode) +{ + if ( mode == NULL ) + throw "-commons missing [ ignore_dylibs | use_dylibs | error ]"; + + if ( strcmp(mode, "ignore_dylibs") == 0 ) + return kCommonsIgnoreDylibs; + else if ( strcmp(mode, "use_dylibs") == 0 ) + return kCommonsOverriddenByDylibs; + else if ( strcmp(mode, "error") == 0 ) + return kCommonsConflictsDylibsError; + else + throw "invalid option to -commons [ ignore_dylibs | use_dylibs | error ]"; +} + + +void Options::setDylibInstallNameOverride(const char* paths) +{ + + +} + +void Options::setExecutablePath(const char* path) +{ + + +} + + + + +uint64_t Options::parseAddress(const char* addr) +{ + char* endptr; + uint64_t result = strtoull(addr, &endptr, 16); + return result; +} + + + +// +// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz +// +// +uint32_t Options::parseVersionNumber(const char* versionString) +{ + unsigned long x = 0; + unsigned long y = 0; + unsigned long z = 0; + char* end; + x = strtoul(versionString, &end, 10); + if ( *end == '.' ) { + y = strtoul(&end[1], &end, 10); + if ( *end == '.' ) { + z = strtoul(&end[1], &end, 10); + } + } + if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) ) + throwf("malformed version number: %s", versionString); + + return (x << 16) | ( y << 8 ) | z; +} + +void Options::parseSectionOrderFile(const char* segment, const char* section, const char* path) +{ + fprintf(stderr, "ld64: warning -sectorder not yet supported for 64-bit code\n"); +} + +void Options::addSection(const char* segment, const char* section, const char* path) +{ + if ( strlen(segment) > 16 ) + throw "-seccreate segment name max 16 chars"; + if ( strlen(section) > 16 ) + throw "-seccreate section name max 16 chars"; + + // read in whole file + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open -sectcreate file: %s", path); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + char* p = (char*)malloc(stat_buf.st_size); + if ( p == NULL ) + throwf("can't process -sectcreate file: %s", path); + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) + throwf("can't read -sectcreate file: %s", path); + ::close(fd); + + // record section to create + ExtraSection info = { segment, section, path, (uint8_t*)p, stat_buf.st_size }; + fExtraSections.push_back(info); +} + +void Options::addSectionAlignment(const char* segment, const char* section, const char* alignmentStr) +{ + if ( strlen(segment) > 16 ) + throw "-sectalign segment name max 16 chars"; + if ( strlen(section) > 16 ) + throw "-sectalign section name max 16 chars"; + + char* endptr; + unsigned long value = strtoul(alignmentStr, &endptr, 16); + if ( *endptr != '\0') + throw "argument for -sectalign is not a hexadecimal number"; + if ( value > 0x8000 ) + throw "argument for -sectalign must be less than or equal to 0x8000"; + uint8_t alignment = 0; + for(unsigned long x=value; x != 1; x >>= 1) + ++alignment; + if ( (unsigned long)(1 << alignment) != value ) + throw "argument for -sectalign is not a power of two"; + + SectionAlignment info = { segment, section, alignment }; + fSectionAlignments.push_back(info); +} + + +void Options::parse(int argc, const char* argv[]) +{ + // pass one builds search list from -L and -F options + this->buildSearchPaths(argc, argv); + + // pass two parse all other options + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + + if ( arg[0] == '-' ) { + if ( (arg[1] == 'L') || (arg[1] == 'F') ) { + // previously handled + } + else if ( strcmp(arg, "-arch") == 0 ) { + parseArch(argv[++i]); + } + else if ( strcmp(arg, "-dynamic") == 0 ) { + // default + } + else if ( strcmp(arg, "-static") == 0 ) { + fOutputKind = kStaticExecutable; + } + else if ( strcmp(arg, "-dylib") == 0 ) { + fOutputKind = kDynamicLibrary; + } + else if ( strcmp(arg, "-bundle") == 0 ) { + fOutputKind = kDynamicBundle; + } + else if ( strcmp(arg, "-dylinker") == 0 ) { + fOutputKind = kDyld; + } + else if ( strcmp(arg, "-execute") == 0 ) { + if ( fOutputKind != kStaticExecutable ) + fOutputKind = kDynamicExecutable; + } + else if ( strcmp(arg, "-r") == 0 ) { + fOutputKind = kObjectFile; + } + else if ( strcmp(arg, "-o") == 0 ) { + fOutputFile = argv[++i]; + } + else if ( arg[1] == 'l' ) { + fInputFiles.push_back(findLibrary(&arg[2])); + } + else if ( strcmp(arg, "-weak-l") == 0 ) { + FileInfo info = findLibrary(&arg[2]); + info.options.fWeakImport = true; + fInputFiles.push_back(info); + } + else if ( strcmp(arg, "-bind_at_load") == 0 ) { + fBindAtLoad = true; + } + else if ( strcmp(arg, "-twolevel_namespace") == 0 ) { + fNameSpace = kTwoLevelNameSpace; + } + else if ( strcmp(arg, "-flat_namespace") == 0 ) { + fNameSpace = kFlatNameSpace; + } + else if ( strcmp(arg, "-force_flat_namespace") == 0 ) { + fNameSpace = kForceFlatNameSpace; + } + else if ( strcmp(arg, "-all_load") == 0 ) { + fReaderOptions.fFullyLoadArchives = true; + } + else if ( strcmp(arg, "-ObjC") == 0 ) { + fReaderOptions.fLoadObjcClassesInArchives = true; + } + else if ( strcmp(arg, "-dylib_compatibility_version") == 0 ) { + fDylibCompatVersion = parseVersionNumber(argv[++i]); + } + else if ( strcmp(arg, "-dylib_current_version") == 0 ) { + fDylibCurrentVersion = parseVersionNumber(argv[++i]); + } + else if ( strcmp(arg, "-sectorder") == 0 ) { + parseSectionOrderFile(argv[i+1], argv[i+2], argv[i+3]); + i += 3; + } + else if ( (strcmp(arg, "-sectcreate") == 0) || (strcmp(arg, "-segcreate") == 0) ) { + addSection(argv[i+1], argv[i+2], argv[i+3]); + i += 3; + } + else if ( (strcmp(arg, "-dylib_install_name") == 0) || (strcmp(arg, "-dylinker_install_name") == 0) ) { + fDylibInstallName = argv[++i]; + } + else if ( strcmp(arg, "-seg1addr") == 0 ) { + fBaseAddress = parseAddress(argv[++i]); + } + else if ( strcmp(arg, "-e") == 0 ) { + fEntryName = argv[++i]; + } + else if ( strcmp(arg, "-filelist") == 0 ) { + loadFileList(argv[++i]); + } + else if ( strcmp(arg, "-keep_private_externs") == 0 ) { + fKeepPrivateExterns = true; + } + else if ( strcmp(arg, "-final_output") == 0 ) { + ++i; + // ignore for now + } + else if ( (strcmp(arg, "-interposable") == 0) || (strcmp(arg, "-multi_module") == 0)) { + fInterposable = true; + } + else if ( strcmp(arg, "-single_module") == 0 ) { + fInterposable = false; + } + else if ( strcmp(arg, "-exported_symbols_list") == 0 ) { + if ( fExportMode == kDontExportSome ) + throw "can't use -exported_symbols_list and -unexported_symbols_list"; + fExportMode = kExportSome; + loadExportFile(argv[++i], "-exported_symbols_list", fExportSymbols); + } + else if ( strcmp(arg, "-unexported_symbols_list") == 0 ) { + if ( fExportMode == kExportSome ) + throw "can't use -exported_symbols_list and -unexported_symbols_list"; + fExportMode = kDontExportSome; + loadExportFile(argv[++i], "-unexported_symbols_list", fDontExportSymbols); + } + else if ( strcmp(arg, "-no_arch_warnings") == 0 ) { + fIgnoreOtherArchFiles = true; + } + else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) { + fForceSubtypeAll = true; + } + else if ( strcmp(arg, "-weak_library") == 0 ) { + FileInfo info = makeFileInfo(argv[++i]); + info.options.fWeakImport = true; + fInputFiles.push_back(info); + } + else if ( strcmp(arg, "-framework") == 0 ) { + fInputFiles.push_back(findFramework(argv[++i])); + } + else if ( strcmp(arg, "-weak_framework") == 0 ) { + FileInfo info = findFramework(argv[++i]); + info.options.fWeakImport = true; + fInputFiles.push_back(info); + } + else if ( strcmp(arg, "-search_paths_first") == 0 ) { + fLibrarySearchMode = kSearchDylibAndArchiveInEachDir; + } + else if ( strcmp(arg, "-undefined") == 0 ) { + setUndefinedTreatment(argv[++i]); + } + else if ( strcmp(arg, "-arch_multiple") == 0 ) { + fMessagesPrefixedWithArchitecture = true; + } + else if ( strcmp(arg, "-read_only_relocs") == 0 ) { + setReadOnlyRelocTreatment(argv[++i]); + } + else if ( strcmp(arg, "-sect_diff_relocs") == 0 ) { + setPICTreatment(argv[++i]); + } + else if ( strcmp(arg, "-weak_reference_mismatches") == 0 ) { + setWeakReferenceMismatchTreatment(argv[++i]); + } + else if ( strcmp(arg, "-prebind") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-noprebind") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-prebind_allow_overlap") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-prebind_all_twolevel_modules") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-noprebind_all_twolevel_modules") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-nofixprebinding") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-dylib_file") == 0 ) { + setDylibInstallNameOverride(argv[++i]); + } + else if ( strcmp(arg, "-executable_path") == 0 ) { + setExecutablePath(argv[++i]); + } + else if ( strcmp(arg, "-segalign") == 0 ) { + // FIX FIX + ++i; + } + else if ( strcmp(arg, "-segaddr") == 0 ) { + // FIX FIX + i += 2; + } + else if ( strcmp(arg, "-segs_read_only_addr") == 0 ) { + // FIX FIX + ++i; + } + else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) { + // FIX FIX + ++i; + } + else if ( strcmp(arg, "-seg_addr_table") == 0 ) { + // FIX FIX + ++i; + } + else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) { + // FIX FIX + ++i; + } + else if ( strcmp(arg, "-segprot") == 0 ) { + // FIX FIX + i += 3; + } + else if ( strcmp(arg, "-pagezero_size") == 0 ) { + fZeroPageSize = parseAddress(argv[++i]); + fZeroPageSize &= (-4096); // page align + } + else if ( strcmp(arg, "-stack_addr") == 0 ) { + fStackAddr = parseAddress(argv[++i]); + } + else if ( strcmp(arg, "-stack_size") == 0 ) { + fStackSize = parseAddress(argv[++i]); + } + else if ( strcmp(arg, "-sectalign") == 0 ) { + addSectionAlignment(argv[i+1], argv[i+2], argv[i+3]); + i += 3; + } + else if ( strcmp(arg, "-sectorder_detail") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-sectobjectsymbols") == 0 ) { + // FIX FIX + i += 2; + } + else if ( strcmp(arg, "-bundle_loader") == 0 ) { + // FIX FIX + ++i; + } + else if ( strcmp(arg, "-private_bundle") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-twolevel_namespace_hints") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-multiply_defined") == 0 ) { + // FIX FIX + ++i; + } + else if ( strcmp(arg, "-multiply_defined_unused") == 0 ) { + // FIX FIX + ++i; + } + else if ( strcmp(arg, "-nomultidefs") == 0 ) { + // FIX FIX + } + else if ( arg[1] == 'y' ) { + // FIX FIX + } + else if ( strcmp(arg, "-Y") == 0 ) { + ++i; + // FIX FIX + } + else if ( strcmp(arg, "-m") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-whyload") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-u") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-u missing argument"; + fInitialUndefines.push_back(name); + } + else if ( strcmp(arg, "-i") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-U") == 0 ) { + // FIX FIX + ++i; + } + else if ( strcmp(arg, "-s") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-x") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-S") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-X") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-Si") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-b") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-Sn") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-dead_strip") == 0 ) { + // FIX FIX + fprintf(stderr, "ld64: warning -dead_strip not yet supported for 64-bit code\n"); + } + else if ( strcmp(arg, "-v") == 0 ) { + extern const char ld64VersionString[]; + fprintf(stderr, "%s", ld64VersionString); + // if only -v specified, exit cleanly + if ( argc == 2 ) + exit(0); + } + else if ( strcmp(arg, "-w") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-arch_errors_fatal") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-M") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-whatsloaded") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-headerpad") == 0 ) { + const char* size = argv[++i]; + if ( size == NULL ) + throw "-headerpad missing argument"; + fMinimumHeaderPad = parseAddress(size); + } + else if ( strcmp(arg, "-headerpad_max_install_names") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-t") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-A") == 0 ) { + // FIX FIX + ++i; + } + else if ( strcmp(arg, "-umbrella") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-umbrella missing argument"; + fUmbrellaName = name; + } + else if ( strcmp(arg, "-allowable_client") == 0 ) { + // FIX FIX + ++i; + } + else if ( strcmp(arg, "-client_name") == 0 ) { + // FIX FIX + ++i; + } + else if ( strcmp(arg, "-sub_umbrella") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-sub_umbrella missing argument"; + fSubUmbellas.push_back(name); + } + else if ( strcmp(arg, "-sub_library") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-sub_library missing argument"; + fSubLibraries.push_back(name); + } + else if ( strcmp(arg, "-init") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-init missing argument"; + fInitFunctionName = name; + } + else if ( strcmp(arg, "-warn_commons") == 0 ) { + fWarnCommons = true; + } + else if ( strcmp(arg, "-commons") == 0 ) { + fCommonsMode = parseCommonsTreatment(argv[++i]); + } + + else { + fprintf(stderr, "unknown option: %s\n", arg); + } + } + else { + fInputFiles.push_back(makeFileInfo(arg)); + } + } +} + +void Options::buildSearchPaths(int argc, const char* argv[]) +{ + bool addStandardLibraryDirectories = true; + // scan through argv looking for -L and -F options + for(int i=0; i < argc; ++i) { + if ( (argv[i][0] == '-') && (argv[i][1] == 'L') ) + fLibrarySearchPaths.push_back(&argv[i][2]); + else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') ) + fFrameworkSearchPaths.push_back(&argv[i][2]); + else if ( strcmp(argv[i], "-Z") == 0 ) + addStandardLibraryDirectories = false; + } + if ( addStandardLibraryDirectories ) { + fLibrarySearchPaths.push_back("/usr/lib"); + fLibrarySearchPaths.push_back("/usr/local/lib"); + + fFrameworkSearchPaths.push_back("/Library/Frameworks/"); + fFrameworkSearchPaths.push_back("/Network/Library/Frameworks/"); + fFrameworkSearchPaths.push_back("/System/Library/Frameworks/"); + } +} + +// this is run before the command line is parsed +void Options::parsePreCommandLineEnvironmentSettings() +{ + if ( getenv("RC_TRACE_ARCHIVES") != NULL) + fReaderOptions.fTraceArchives = true; + + if ( getenv("RC_TRACE_DYLIBS") != NULL) { + fReaderOptions.fTraceDylibs = true; + fReaderOptions.fTraceIndirectDylibs = true; + } +} + +// this is run after the command line is parsed +void Options::parsePostCommandLineEnvironmentSettings() +{ + +} + +void Options::checkIllegalOptionCombinations() +{ + // check -undefined setting + switch ( fUndefinedTreatment ) { + case kUndefinedError: + case kUndefinedDynamicLookup: + // always legal + break; + case kUndefinedWarning: + case kUndefinedSuppress: + // requires flat namespace + if ( fNameSpace == kTwoLevelNameSpace ) + throw "can't use -undefined warning or suppress with -twolevel_namespace"; + break; + } + + // unify -sub_umbrella with dylibs + for (std::vector::iterator it = fSubUmbellas.begin(); it != fSubUmbellas.end(); it++) { + const char* subUmbrella = *it; + bool found = false; + for (std::vector::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) { + Options::FileInfo& info = *fit; + const char* lastSlash = strrchr(info.path, '/'); + if ( lastSlash == NULL ) + lastSlash = info.path - 1; + if ( strcmp(&lastSlash[1], subUmbrella) == 0 ) { + info.options.fReExport = true; + found = true; + break; + } + } + if ( ! found ) + fprintf(stderr, "ld64 warning: -sub_umbrella %s does not match a supplied dylib\n", subUmbrella); + } + + // unify -sub_library with dylibs + for (std::vector::iterator it = fSubLibraries.begin(); it != fSubLibraries.end(); it++) { + const char* subLibrary = *it; + bool found = false; + for (std::vector::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) { + Options::FileInfo& info = *fit; + const char* lastSlash = strrchr(info.path, '/'); + if ( lastSlash == NULL ) + lastSlash = info.path - 1; + const char* dot = strchr(lastSlash, '.'); + if ( dot == NULL ) + dot = &lastSlash[strlen(lastSlash)]; + if ( strncmp(&lastSlash[1], subLibrary, dot-lastSlash-1) == 0 ) { + info.options.fReExport = true; + found = true; + break; + } + } + if ( ! found ) + fprintf(stderr, "ld64 warning: -sub_library %s does not match a supplied dylib\n", subLibrary); + } + + // sync reader options + if ( fNameSpace != kTwoLevelNameSpace ) + fReaderOptions.fFlatNamespace = true; + + // check -stack_addr + if ( fStackAddr != 0 ) { + switch (fArchitecture) { + case CPU_TYPE_I386: + case CPU_TYPE_POWERPC: + if ( fStackAddr > 0xFFFFFFFF ) + throw "-stack_addr must be < 4G for 32-bit processes"; + break; + case CPU_TYPE_POWERPC64: + break; + } + if ( (fStackAddr & -4096) != fStackAddr ) + throw "-stack_addr must be multiples of 4K"; + if ( fStackSize == 0 ) + throw "-stack_addr must be used with -stack_size"; + } + + // check -stack_size + if ( fStackSize != 0 ) { + switch (fArchitecture) { + case CPU_TYPE_I386: + case CPU_TYPE_POWERPC: + if ( fStackSize > 0xFFFFFFFF ) + throw "-stack_size must be < 4G for 32-bit processes"; + if ( fStackAddr == 0 ) { + fprintf(stderr, "ld64 warning: -stack_addr not specified, using the default 0xC0000000\n"); + fStackAddr = 0xC0000000; + } + break; + case CPU_TYPE_POWERPC64: + if ( fStackAddr == 0 ) { + fprintf(stderr, "ld64 warning: -stack_addr not specified, using the default 0x0008000000000000\n"); + fStackAddr = 0x0008000000000000LL; + } + break; + } + if ( (fStackSize & -4096) != fStackSize ) + throw "-stack_size must be multiples of 4K"; + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + // custom stack size only legeal when building main executable + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + throw "-stack_size option can only be used when linking a main executable"; + } + } + + // check -init is only used when building a dylib + if ( (fInitFunctionName != NULL) && (fOutputKind != Options::kDynamicLibrary) ) + throw "-init can only be used with -dynamiclib"; +} + + diff --git a/src/Options.h b/src/Options.h new file mode 100644 index 0000000..33d1137 --- /dev/null +++ b/src/Options.h @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __OPTIONS__ +#define __OPTIONS__ + + +#include +#include + +#ifndef CPU_TYPE_POWERPC64 +#define CPU_TYPE_POWERPC64 0x1000012 +#endif + + +#include +#include + +#include "ObjectFile.h" + +extern __attribute__((noreturn)) void throwf(const char* format, ...); + + +class DynamicLibraryOptions +{ +public: + DynamicLibraryOptions() : fWeakImport(false), fReExport(false), fInstallPathOverride(NULL) {} + + bool fWeakImport; + bool fReExport; + const char* fInstallPathOverride; +}; + + +class Options +{ +public: + Options(int argc, const char* argv[]); + ~Options(); + + enum OutputKind { kDynamicExecutable, kStaticExecutable, kDynamicLibrary, kDynamicBundle, kObjectFile, kDyld }; + enum NameSpace { kTwoLevelNameSpace, kFlatNameSpace, kForceFlatNameSpace }; + enum UndefinedTreatment { kUndefinedError, kUndefinedWarning, kUndefinedSuppress, kUndefinedDynamicLookup }; + enum PICTreatment { kPICError, kPICWarning, kPICSuppress }; + enum WeakReferenceMismatchTreatment { kWeakReferenceMismatchError, kWeakReferenceMismatchWeak, kWeakReferenceMismatchNonWeak }; + enum CommonsMode { kCommonsIgnoreDylibs, kCommonsOverriddenByDylibs, kCommonsConflictsDylibsError }; + + struct FileInfo { + const char* path; + uint64_t fileLen; + DynamicLibraryOptions options; + }; + + struct ExtraSection { + const char* segmentName; + const char* sectionName; + const char* path; + const uint8_t* data; + uint64_t dataLen; + }; + + struct SectionAlignment { + const char* segmentName; + const char* sectionName; + uint8_t alignment; + }; + + ObjectFile::ReaderOptions& readerOptions(); + const char* getOutputFilePath(); + std::vector& getInputFiles(); + + cpu_type_t architecture(); + OutputKind outputKind(); + bool stripLocalSymbols(); + bool stripDebugInfo(); + bool bindAtLoad(); + bool fullyLoadArchives(); + NameSpace nameSpace(); + const char* installPath(); // only for kDynamicLibrary + uint32_t currentVersion(); // only for kDynamicLibrary + uint32_t compatibilityVersion(); // only for kDynamicLibrary + const char* entryName(); // only for kDynamicExecutable or kStaticExecutable + uint64_t baseAddress(); + bool keepPrivateExterns(); // only for kObjectFile + bool interposable(); // only for kDynamicLibrary + bool hasExportRestrictList(); + bool shouldExport(const char*); + bool ignoreOtherArchInputFiles(); + bool forceCpuSubtypeAll(); + bool traceDylibs(); + bool traceArchives(); + UndefinedTreatment undefinedTreatment(); + bool messagesPrefixedWithArchitecture(); + PICTreatment picTreatment(); + WeakReferenceMismatchTreatment weakReferenceMismatchTreatment(); + const char* umbrellaName(); + const char* initFunctionName(); // only for kDynamicLibrary + uint64_t zeroPageSize(); + bool hasCustomStack(); + uint64_t customStackSize(); + uint64_t customStackAddr(); + std::vector& initialUndefines(); + uint32_t minimumHeaderPad(); + std::vector& extraSections(); + std::vector& sectionAlignments(); + CommonsMode commonsMode(); + bool warnCommons(); + +private: + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + + typedef __gnu_cxx::hash_set, CStringEquals> NameSet; + enum ExportMode { kExportDefault, kExportSome, kDontExportSome }; + enum LibrarySearchMode { kSearchDylibAndArchiveInEachDir, kSearchAllDirsForDylibsThenAllDirsForArchives }; + + void parse(int argc, const char* argv[]); + void checkIllegalOptionCombinations(); + void buildSearchPaths(int argc, const char* argv[]); + void parseArch(const char* architecture); + FileInfo findLibrary(const char* rootName); + FileInfo findFramework(const char* rootName); + FileInfo makeFileInfo(const char* path); + bool checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result); + uint32_t parseVersionNumber(const char*); + void parseSectionOrderFile(const char* segment, const char* section, const char* path); + void addSection(const char* segment, const char* section, const char* path); + void addSubLibrary(const char* name); + void loadFileList(const char* fileOfPaths); + uint64_t parseAddress(const char* addr); + void loadExportFile(const char* fileOfExports, const char* option, NameSet& set); + void parsePreCommandLineEnvironmentSettings(); + void parsePostCommandLineEnvironmentSettings(); + void setUndefinedTreatment(const char* treatment); + void setPICTreatment(const char* treatment); + void setReadOnlyRelocTreatment(const char* treatment); + void setWeakReferenceMismatchTreatment(const char* treatment); + void setDylibInstallNameOverride(const char* paths); + void setExecutablePath(const char* path); + void addSectionAlignment(const char* segment, const char* section, const char* alignment); + CommonsMode parseCommonsTreatment(const char* mode); + + + + ObjectFile::ReaderOptions fReaderOptions; + const char* fOutputFile; + std::vector fInputFiles; + cpu_type_t fArchitecture; + OutputKind fOutputKind; + bool fBindAtLoad; + bool fStripLocalSymbols; + bool fKeepPrivateExterns; + bool fInterposable; + bool fIgnoreOtherArchFiles; + bool fForceSubtypeAll; + NameSpace fNameSpace; + uint32_t fDylibCompatVersion; + uint32_t fDylibCurrentVersion; + const char* fDylibInstallName; + const char* fEntryName; + uint64_t fBaseAddress; + NameSet fExportSymbols; + NameSet fDontExportSymbols; + ExportMode fExportMode; + LibrarySearchMode fLibrarySearchMode; + UndefinedTreatment fUndefinedTreatment; + bool fMessagesPrefixedWithArchitecture; + PICTreatment fPICTreatment; + WeakReferenceMismatchTreatment fWeakReferenceMismatchTreatment; + std::vector fSubUmbellas; + std::vector fSubLibraries; + const char* fUmbrellaName; + const char* fInitFunctionName; + uint64_t fZeroPageSize; + uint64_t fStackSize; + uint64_t fStackAddr; + uint32_t fMinimumHeaderPad; + CommonsMode fCommonsMode; + bool fWarnCommons; + std::vector fInitialUndefines; + std::vector fExtraSections; + std::vector fSectionAlignments; + + std::vector fLibrarySearchPaths; + std::vector fFrameworkSearchPaths; + +}; + + + + +#endif // __OPTIONS__ + + + + + + + + + + + diff --git a/src/Readers/ObjectFileArchiveMachO.cpp b/src/Readers/ObjectFileArchiveMachO.cpp new file mode 100644 index 0000000..a6851a1 --- /dev/null +++ b/src/Readers/ObjectFileArchiveMachO.cpp @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + +namespace ObjectFileArchiveMachO { + +class Reader : public ObjectFile::Reader +{ +public: + Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options); + virtual ~Reader(); + + virtual const char* getPath(); + virtual std::vector& getAtoms(); + virtual std::vector* getJustInTimeAtomsFor(const char* name); + virtual std::vector* getStabsDebugInfo(); + +private: + class Entry : ar_hdr + { + public: + const char* getName() const; + const uint8_t* getContent() const; + uint32_t getContentSize() const; + const Entry* getNext() const; + private: + bool hasLongName() const; + unsigned int getLongNameSpace() const; + + }; + + const struct ranlib* ranlibBinarySearch(const char* name); + const struct ranlib* ranlibLinearSearch(const char* name); + ObjectFile::Reader* makeObjectReaderForMember(const Entry* member); + void dumpTableOfContents(); + + const char* fPath; + const ObjectFile::ReaderOptions& fOptions; + const uint8_t* fFileContent; + uint64_t fFileLength; + const struct ranlib* fTableOfContents; + uint32_t fTableOfContentCount; + bool fSorted; + const char* fStringPool; + std::vector fAllAtoms; + std::set fInstantiatedEntries; + + static std::vector fgEmptyList; +}; + +std::vector Reader::fgEmptyList; + + +bool Reader::Entry::hasLongName() const +{ + return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 ); +} + +unsigned int Reader::Entry::getLongNameSpace() const +{ + char* endptr; + long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10); + return result; +} + +const char* Reader::Entry::getName() const +{ + if ( this->hasLongName() ) { + int len = this->getLongNameSpace(); + static char longName[256]; + strncpy(longName, ((char*)this)+sizeof(ar_hdr), len); + longName[len] = '\0'; + return longName; + } + else { + static char shortName[20]; + strncpy(shortName, this->ar_name, 16); + shortName[16] = '\0'; + char* space = strchr(shortName, ' '); + if ( space != NULL ) + *space = '\0'; + return shortName; + } +} + + +const uint8_t* Reader::Entry::getContent() const +{ + if ( this->hasLongName() ) + return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace(); + else + return ((uint8_t*)this) + sizeof(ar_hdr); +} + + +uint32_t Reader::Entry::getContentSize() const +{ + char temp[12]; + strncpy(temp, this->ar_size, 10); + temp[10] = '\0'; + char* endptr; + long size = strtol(temp, &endptr, 10); + // long name is included in ar_size + if ( this->hasLongName() ) + size -= this->getLongNameSpace(); + return size; +} + +const Reader::Entry* Reader::Entry::getNext() const +{ + const uint8_t* p = this->getContent() + getContentSize(); + p = (const uint8_t*)(((uint32_t)p+3) & (-4)); // 4-byte align + return (Reader::Entry*)p; +} + + + +Reader::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options) + : fPath(NULL), fOptions(options), fFileContent(NULL), fTableOfContents(NULL), fTableOfContentCount(0), + fSorted(false), fStringPool(NULL) +{ + fPath = strdup(path); + fFileContent = fileContent; + fFileLength = fileLength; + + if ( strncmp((const char*)fileContent, "!\n", 8) != 0 ) + throw "not an archive"; + + if ( !options.fFullyLoadArchives ) { + const Entry* const firstMember = (Entry*)&fFileContent[8]; + if ( strcmp(firstMember->getName(), SYMDEF_SORTED) == 0 ) + fSorted = true; + else if ( strcmp(firstMember->getName(), SYMDEF) == 0 ) + fSorted = false; + else + throw "archive has no table of contents"; + const uint8_t* contents = firstMember->getContent(); + uint32_t ranlibArrayLen = OSReadBigInt32((void *) contents, 0); + fTableOfContents = (const struct ranlib*)&contents[4]; + fTableOfContentCount = ranlibArrayLen / sizeof(struct ranlib); + fStringPool = (const char*)&contents[ranlibArrayLen+8]; + } + + if ( options.fTraceArchives ) + printf("[Logging for Build & Integration] Used static archive: %s\n", fPath); +} + +Reader::~Reader() +{ +} + + + +ObjectFile::Reader* Reader::makeObjectReaderForMember(const Entry* member) +{ + const char* memberName = member->getName(); + char memberPath[strlen(fPath) + strlen(memberName)+4]; + strcpy(memberPath, fPath); + strcat(memberPath, "("); + strcat(memberPath, memberName); + strcat(memberPath, ")"); + //fprintf(stderr, "using %s from %s\n", memberName, fPath); + try { + return ObjectFileMachO::MakeReader((class macho_header*)member->getContent(), memberPath, fOptions); + } + catch (const char* msg) { + throwf("in %s, %s", memberPath, msg); + } +} + +const char* Reader::getPath() +{ + return fPath; +} + +std::vector& Reader::getAtoms() +{ + if ( fOptions.fFullyLoadArchives ) { + // build vector of all atoms from all .o files in this archive + const Entry* const start = (Entry*)&fFileContent[8]; + const Entry* const end = (Entry*)&fFileContent[fFileLength]; + for (const Entry* p=start; p < end; p = p->getNext()) { + const char* memberName = p->getName(); + if ( (p==start) && (strcmp(memberName, SYMDEF_SORTED) == 0) ) + continue; + ObjectFile::Reader* r = this->makeObjectReaderForMember(p); + std::vector& atoms = r->getAtoms(); + fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end()); + } + return fAllAtoms; + } + else { + // return nonthing for now, getJustInTimeAtomsFor() will return atoms as needed + return fgEmptyList; + } +} + + +const struct ranlib* Reader::ranlibBinarySearch(const char* key) +{ + const struct ranlib* base = fTableOfContents; + for (uint32_t n = fTableOfContentCount; n > 0; n /= 2) { + const struct ranlib* pivot = &base[n/2]; + const char* pivotStr = &fStringPool[OSSwapBigToHostInt32(pivot->ran_un.ran_strx)]; + int cmp = strcmp(key, pivotStr); + if ( cmp == 0 ) + return pivot; + if ( cmp > 0 ) { + // key > pivot + // move base to symbol after pivot + base = &pivot[1]; + --n; + } + else { + // key < pivot + // keep same base + } + } + return NULL; +} + +const struct ranlib* Reader::ranlibLinearSearch(const char* key) +{ + for (uint32_t i = 0; i < fTableOfContentCount; ++i) { + const struct ranlib* entry = &fTableOfContents[i]; + const char* entryName = &fStringPool[OSSwapBigToHostInt32(entry->ran_un.ran_strx)]; + if ( strcmp(key, entryName) == 0 ) + return entry; + } + return NULL; +} + + +void Reader::dumpTableOfContents() +{ + for (unsigned int i=0; i < fTableOfContentCount; ++i) { + const struct ranlib* e = &fTableOfContents[i]; + printf("%s in %s\n", &fStringPool[OSSwapBigToHostInt32(e->ran_un.ran_strx)], ((Entry*)&fFileContent[OSSwapBigToHostInt32(e->ran_off)])->getName()); + } +} + +std::vector* Reader::getJustInTimeAtomsFor(const char* name) +{ + if ( fOptions.fFullyLoadArchives ) { + return NULL; + } + else { + const struct ranlib* result = NULL; + if ( fSorted ) { + // do a binary search of table of contents lookig for requested symbol + result = ranlibBinarySearch(name); + } + else { + // do a linear search of table of contents lookig for requested symbol + result = ranlibLinearSearch(name); + } + if ( result != NULL ) { + const Entry* member = (Entry*)&fFileContent[OSSwapBigToHostInt32(result->ran_off)]; + //fprintf(stderr, "%s found in %s\n", name, member->getName()); + if ( fInstantiatedEntries.count(member) == 0 ) { + // only return these atoms once + fInstantiatedEntries.insert(member); + ObjectFile::Reader* r = makeObjectReaderForMember(member); + return new std::vector(r->getAtoms()); + } + } + //fprintf(stderr, "%s NOT found in archive %s\n", name, fPath); + return NULL; + } +} + + +std::vector* Reader::getStabsDebugInfo() +{ + return NULL; +} + + + +Reader* MakeReader(const uint8_t fileContent[], uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options) +{ + return new Reader(fileContent, fileLength, path, options); +} + + + +}; + + + + + + + diff --git a/src/Readers/ObjectFileDylibMachO.cpp b/src/Readers/ObjectFileDylibMachO.cpp new file mode 100644 index 0000000..cadbef3 --- /dev/null +++ b/src/Readers/ObjectFileDylibMachO.cpp @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + +namespace ObjectFileDylibMachO { + +class Reader : public ObjectFile::Reader +{ +public: + Reader(const macho_header* header, const char* path, const ObjectFile::ReaderOptions& options); + virtual ~Reader(); + + virtual const char* getPath(); + virtual std::vector& getAtoms(); + virtual std::vector* getJustInTimeAtomsFor(const char* name); + virtual std::vector* getStabsDebugInfo(); + virtual const char* getInstallPath(); + virtual uint32_t getTimestamp(); + virtual uint32_t getCurrentVersion(); + virtual uint32_t getCompatibilityVersion(); + virtual std::vector* getDependentLibraryPaths(); + virtual bool reExports(ObjectFile::Reader*); + virtual bool isDefinitionWeak(const ObjectFile::Atom&); + +private: + struct CStringComparor + { + bool operator()(const char* left, const char* right) { return (strcmp(left, right) > 0); } + }; + typedef std::map Mapper; + + + void init(const macho_header* header,const char* path); + const macho_nlist* binarySearchWithToc(const char* key, const char stringPool[], const macho_nlist symbols[], const struct dylib_table_of_contents toc[], uint32_t symbolCount); + const macho_nlist* binarySearch(const char* key, const char stringPool[], const macho_nlist symbols[], uint32_t symbolCount); + bool hasExport(const char* name); + const macho_nlist* findExport(const char* name); + + const char* fPath; + const macho_header* fHeader; + const char* fStrings; + const macho_dysymtab_command* fDynamicInfo; + const macho_dylib_command* fDylibID; + const macho_nlist* fSymbols; + uint32_t fSymbolCount; + Mapper fAtoms; + std::vector fReExportedDylibs; + + static std::vector fEmptyAtomList; +}; + +std::vector Reader::fEmptyAtomList; + + +class Segment : public ObjectFile::Segment +{ +public: + Segment(const char* name) { fName = name; } + virtual const char* getName() const { return fName; } + virtual bool isContentReadable() const { return true; } + virtual bool isContentWritable() const { return false; } + virtual bool isContentExecutable() const { return false; } +private: + const char* fName; +}; + + +class ExportAtom : public ObjectFile::Atom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual const char* getName() const { return fName; } + virtual const char* getDisplayName() const; + virtual Scope getScope() const { return ObjectFile::Atom::scopeGlobal; } + virtual bool isTentativeDefinition() const { return false; } + virtual bool isWeakDefinition() const { return false; } + virtual bool isCoalesableByName() const { return false; } + virtual bool isCoalesableByValue() const { return false; } + virtual bool isZeroFill() const { return false; } + virtual bool dontDeadStrip() const { return false; } + virtual bool dontStripName() const { return false; } + virtual bool isImportProxy() const { return true; } + virtual uint64_t getSize() const { return 0; } + virtual std::vector& getReferences() const { return fgEmptyReferenceList; } + virtual bool mustRemainInSection() const { return false; } + virtual const char* getSectionName() const { return "._imports"; } + virtual Segment& getSegment() const { return fgImportSegment; } + virtual bool requiresFollowOnAtom() const{ return false; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } + virtual std::vector* getStabsDebugInfo() const { return NULL; } + virtual uint8_t getAlignment() const { return 0; } + virtual WeakImportSetting getImportWeakness() const { return fWeakImportSetting; } + virtual void copyRawContent(uint8_t buffer[]) const {} + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const {} + + virtual void setScope(Scope) { } + virtual void setImportWeakness(bool weakImport) { fWeakImportSetting = weakImport ? kWeakImport : kNonWeakImport; } + +protected: + friend class Reader; + + ExportAtom(Reader& owner, const char* name) : fOwner(owner), fName(name), fWeakImportSetting(kWeakUnset) {} + virtual ~ExportAtom() {} + + Reader& fOwner; + const char* fName; + WeakImportSetting fWeakImportSetting; + + static std::vector fgEmptyReferenceList; + static Segment fgImportSegment; +}; + +Segment ExportAtom::fgImportSegment("__LINKEDIT"); +std::vector ExportAtom::fgEmptyReferenceList; + +const char* ExportAtom::getDisplayName() const +{ + static char temp[300]; + strcpy(temp, fName); + strcat(temp, "$import"); + return temp; +} + + + +Reader::Reader(const macho_header* header, const char* path, const ObjectFile::ReaderOptions& options) + : fHeader(header), fStrings(NULL), fDylibID(NULL), fSymbols(NULL), fSymbolCount(0) +{ + typedef std::pair DylibAndReExportFlag; + std::vector dependentDylibs; + + fPath = strdup(path); + const uint32_t cmd_count = header->ncmds(); + const macho_load_command* const cmds = (macho_load_command*)((char*)header + macho_header::size); + // get all dylib load commands + const macho_load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + { + DylibAndReExportFlag info; + info.first = (struct macho_dylib_command*)cmd; + info.second = options.fFlatNamespace; + dependentDylibs.push_back(info); + } + break; + } + cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); + } + + // cache interesting pointers + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_SYMTAB: + { + const macho_symtab_command* symtab = (macho_symtab_command*)cmd; + fSymbolCount = symtab->nsyms(); + fSymbols = (const macho_nlist*)((char*)header + symtab->symoff()); + fStrings = (char*)header + symtab->stroff(); + } + break; + case LC_DYSYMTAB: + fDynamicInfo = (macho_dysymtab_command*)cmd; + break; + case LC_ID_DYLIB: + fDylibID = (macho_dylib_command*)cmd; + break; + case LC_SUB_UMBRELLA: + if ( !options.fFlatNamespace ) { + const char* frameworkLeafName = ((macho_sub_umbrella_command*)cmd)->name(); + for (std::vector::iterator it = dependentDylibs.begin(); it != dependentDylibs.end(); it++) { + const char* dylibName = it->first->name(); + const char* lastSlash = strrchr(dylibName, '/'); + if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) + it->second = true; + } + } + break; + case LC_SUB_LIBRARY: + if ( !options.fFlatNamespace ) { + const char* dylibBaseName = ((macho_sub_library_command*)cmd)->name(); + for (std::vector::iterator it = dependentDylibs.begin(); it != dependentDylibs.end(); it++) { + const char* dylibName = it->first->name(); + const char* lastSlash = strrchr(dylibName, '/'); + const char* leafStart = &lastSlash[1]; + if ( lastSlash == NULL ) + leafStart = dylibName; + const char* firstDot = strchr(leafStart, '.'); + int len = strlen(leafStart); + if ( firstDot != NULL ) + len = firstDot - leafStart; + if ( strncmp(leafStart, dylibBaseName, len) == 0 ) + it->second = true; + } + } + break; + } + cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); + } + + // load dylibs we need to re-export + for (std::vector::iterator it = dependentDylibs.begin(); it != dependentDylibs.end(); it++) { + if ( it->second ) { + // printf("%s need to re-export %s\n", path, it->first->name()); + //fReExportedDylibs.push_back( + } + } +} + + +Reader::~Reader() +{ +} + +const char* Reader::getPath() +{ + return fPath; +} + + +std::vector& Reader::getAtoms() +{ + return fEmptyAtomList; +} + + + +const macho_nlist* Reader::binarySearchWithToc(const char* key, const char stringPool[], const macho_nlist symbols[], + const struct dylib_table_of_contents toc[], uint32_t symbolCount) +{ + int32_t high = symbolCount-1; + int32_t mid = symbolCount/2; + + for (int32_t low = 0; low <= high; mid = (low+high)/2) { + const uint32_t index = ENDIAN_READ32(toc[mid].symbol_index); + const macho_nlist* pivot = &symbols[index]; + const char* pivotStr = &stringPool[pivot->n_strx()]; + int cmp = strcmp(key, pivotStr); + if ( cmp == 0 ) + return pivot; + if ( cmp > 0 ) { + // key > pivot + low = mid + 1; + } + else { + // key < pivot + high = mid - 1; + } + } + return NULL; +} + + +const macho_nlist* Reader::binarySearch(const char* key, const char stringPool[], const macho_nlist symbols[], uint32_t symbolCount) +{ + const macho_nlist* base = symbols; + for (uint32_t n = symbolCount; n > 0; n /= 2) { + const macho_nlist* pivot = &base[n/2]; + const char* pivotStr = &stringPool[pivot->n_strx()]; + int cmp = strcmp(key, pivotStr); + if ( cmp == 0 ) + return pivot; + if ( cmp > 0 ) { + // key > pivot + // move base to symbol after pivot + base = &pivot[1]; + --n; + } + else { + // key < pivot + // keep same base + } + } + return NULL; +} + +const macho_nlist* Reader::findExport(const char* name) +{ + if ( fDynamicInfo->tocoff() == 0 ) + return binarySearch(name, fStrings, &fSymbols[fDynamicInfo->iextdefsym()], fDynamicInfo->nextdefsym()); + else { + return binarySearchWithToc(name, fStrings, fSymbols, (dylib_table_of_contents*)((char*)fHeader + fDynamicInfo->tocoff()), + fDynamicInfo->nextdefsym()); + } +} + +bool Reader::hasExport(const char* name) +{ + return ( findExport(name) != NULL ); +} + +std::vector* Reader::getJustInTimeAtomsFor(const char* name) +{ + std::vector* atoms = NULL; + // search exports + if ( this->hasExport(name) ) { + // see if this atom already synthesized + ObjectFile::Atom* atom = NULL; + Mapper::iterator pos = fAtoms.find(name); + if ( pos != fAtoms.end() ) { + atom = pos->second; + } + else { + atom = new ExportAtom(*this, name); + fAtoms[name] = atom; + } + // return a vector of one atom + atoms = new std::vector; + atoms->push_back(atom); + return atoms; + } + + // check re-exports + for (std::vector::iterator it = fReExportedDylibs.begin(); it != fReExportedDylibs.end(); it++) { + Reader* reExportedReader = *it; + atoms = reExportedReader->getJustInTimeAtomsFor(name); + if ( atoms != NULL ) + return atoms; + } + + return NULL; +} + + +std::vector* Reader::getStabsDebugInfo() +{ + return NULL; +} + +const char* Reader::getInstallPath() +{ + return fDylibID->name(); +} + +uint32_t Reader::getTimestamp() +{ + return fDylibID->timestamp(); +} + +uint32_t Reader::getCurrentVersion() +{ + return fDylibID->current_version(); +} + +uint32_t Reader::getCompatibilityVersion() +{ + return fDylibID->compatibility_version(); +} + +std::vector* Reader::getDependentLibraryPaths() +{ + std::vector* result = new std::vector; + const uint32_t cmd_count = fHeader->ncmds(); + const macho_load_command* const cmds = (macho_load_command*)((char*)fHeader + macho_header::size); + const macho_load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + { + result->push_back(((struct macho_dylib_command*)cmd)->name()); + } + break; + } + cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); + } + return result; +} + +bool Reader::reExports(ObjectFile::Reader* child) +{ + // A dependent dylib is re-exported under two conditions: + // 1) parent contains LC_SUB_UMBRELLA or LC_SUB_LIBRARY with child name + { + const uint32_t cmd_count = fHeader->ncmds(); + const macho_load_command* const cmds = (macho_load_command*)((char*)fHeader + macho_header::size); + const macho_load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_SUB_UMBRELLA: + { + const char* frameworkLeafName = ((macho_sub_umbrella_command*)cmd)->name(); + const char* dylibName = child->getPath(); + const char* lastSlash = strrchr(dylibName, '/'); + if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) + return true; + } + break; + case LC_SUB_LIBRARY: + { + const char* dylibBaseName = ((macho_sub_library_command*)cmd)->name(); + const char* dylibName = child->getPath(); + const char* lastSlash = strrchr(dylibName, '/'); + const char* leafStart = &lastSlash[1]; + if ( lastSlash == NULL ) + leafStart = dylibName; + const char* firstDot = strchr(leafStart, '.'); + int len = strlen(leafStart); + if ( firstDot != NULL ) + len = firstDot - leafStart; + if ( strncmp(leafStart, dylibBaseName, len) == 0 ) + return true; + } + break; + } + cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); + } + } + + // 2) child contains LC_SUB_FRAMEWORK with parent name + { + const uint32_t cmd_count = ((Reader*)child)->fHeader->ncmds(); + const macho_load_command* const cmds = (macho_load_command*)((char*)(((Reader*)child)->fHeader) + macho_header::size); + const macho_load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_SUB_FRAMEWORK: + { + const char* frameworkLeafName = ((macho_sub_framework_command*)cmd)->name(); + const char* parentName = this->getPath(); + const char* lastSlash = strrchr(parentName, '/'); + if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) + return true; + } + break; + } + cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); + } + } + + + return false; +} + +bool Reader::isDefinitionWeak(const ObjectFile::Atom& atom) +{ + const macho_nlist* sym = findExport(atom.getName()); + if ( sym != NULL ) { + if ( (sym->n_desc() & N_WEAK_DEF) != 0 ) + return true; + } + return false; +} + + + +Reader* MakeReader(const macho_header* mh, const char* path, const ObjectFile::ReaderOptions& options) +{ + return new Reader(mh, path, options); +} + + + +}; + + + + + + + diff --git a/src/Readers/ObjectFileMachO-all.cpp b/src/Readers/ObjectFileMachO-all.cpp new file mode 100644 index 0000000..736b2a6 --- /dev/null +++ b/src/Readers/ObjectFileMachO-all.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ObjectFile.h" +#include "Options.h" + + + + +// buiild Writer for -arch ppc64 +#undef MACHO_32_SAME_ENDIAN +#undef MACHO_32_OPPOSITE_ENDIAN +#undef MACHO_64_SAME_ENDIAN +#undef MACHO_64_OPPOSITE_ENDIAN +#if __ppc__ || __ppc64__ + #define MACHO_64_SAME_ENDIAN +#elif __i386__ + #define MACHO_64_OPPOSITE_ENDIAN +#else + #error unknown architecture +#endif +namespace ppc64 { + #undef ARCH_PPC + #define ARCH_PPC64 + #undef ARCH_I386 + #include "MachOAbstraction.h" + #include "ObjectFileMachO.cpp" + #include "ObjectFileDylibMachO.cpp" + #include "ObjectFileArchiveMachO.cpp" +}; + +// buiild Writer for -arch ppc +#undef MACHO_32_SAME_ENDIAN +#undef MACHO_32_OPPOSITE_ENDIAN +#undef MACHO_64_SAME_ENDIAN +#undef MACHO_64_OPPOSITE_ENDIAN +#if __ppc__ || __ppc64__ + #define MACHO_32_SAME_ENDIAN +#elif __i386__ + #define MACHO_32_OPPOSITE_ENDIAN +#else + #error unknown architecture +#endif +namespace ppc { + #define ARCH_PPC + #undef ARCH_PPC64 + #undef ARCH_I386 + #include "MachOAbstraction.h" + #include "ObjectFileMachO.cpp" + #include "ObjectFileDylibMachO.cpp" + #include "ObjectFileArchiveMachO.cpp" +}; + + +// buiild Writer for -arch i386 +#undef MACHO_32_SAME_ENDIAN +#undef MACHO_32_OPPOSITE_ENDIAN +#undef MACHO_64_SAME_ENDIAN +#undef MACHO_64_OPPOSITE_ENDIAN +#if __ppc__ || __ppc64__ + #define MACHO_32_OPPOSITE_ENDIAN +#elif __i386__ + #define MACHO_32_SAME_ENDIAN +#else + #error unknown architecture +#endif +#undef i386 // compiler sometimes #defines this +namespace i386 { + #undef ARCH_PPC + #undef ARCH_PPC64 + #define ARCH_I386 + #include "MachOAbstraction.h" + #include "ObjectFileMachO.cpp" + #include "ObjectFileDylibMachO.cpp" + #include "ObjectFileArchiveMachO.cpp" +}; + + diff --git a/src/Readers/ObjectFileMachO-all.h b/src/Readers/ObjectFileMachO-all.h new file mode 100644 index 0000000..0f27244 --- /dev/null +++ b/src/Readers/ObjectFileMachO-all.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __OBJECTFILEMACHO__ +#define __OBJECTFILEMACHO__ + +class Options; + +namespace ppc { + class macho_header; + namespace ObjectFileMachO { + extern class ObjectFile::Reader* MakeReader(const macho_header*, const char* path, const ObjectFile::ReaderOptions& options); + }; + namespace ObjectFileDylibMachO { + extern class ObjectFile::Reader* MakeReader(const macho_header*, const char* path, const ObjectFile::ReaderOptions& options); + }; + namespace ObjectFileArchiveMachO { + extern class ObjectFile::Reader* MakeReader(const uint8_t fileContent[], uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options); + }; +}; + +namespace ppc64 { + class macho_header; + namespace ObjectFileMachO { + extern class ObjectFile::Reader* MakeReader(const macho_header*, const char* path, const ObjectFile::ReaderOptions& options); + }; + namespace ObjectFileDylibMachO { + extern class ObjectFile::Reader* MakeReader(const macho_header*, const char* path, const ObjectFile::ReaderOptions& options); + }; + namespace ObjectFileArchiveMachO { + extern class ObjectFile::Reader* MakeReader(const uint8_t fileContent[], uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options); + }; +}; + +#undef i386 // compiler sometimes #defines this +namespace i386 { + class macho_header; + namespace ObjectFileMachO { + extern class ObjectFile::Reader* MakeReader(const macho_header*, const char* path, const ObjectFile::ReaderOptions& options); + }; + namespace ObjectFileDylibMachO { + extern class ObjectFile::Reader* MakeReader(const macho_header*, const char* path, const ObjectFile::ReaderOptions& options); + }; + namespace ObjectFileArchiveMachO { + extern class ObjectFile::Reader* MakeReader(const uint8_t fileContent[], uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options); + }; +}; + + + +#endif // __OBJECTFILEMACHO__ + + + diff --git a/src/Readers/ObjectFileMachO.cpp b/src/Readers/ObjectFileMachO.cpp new file mode 100644 index 0000000..5736d00 --- /dev/null +++ b/src/Readers/ObjectFileMachO.cpp @@ -0,0 +1,2273 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- */ + + +namespace ObjectFileMachO { + +class Reference : public ObjectFile::Reference +{ +public: + Reference(macho_uintptr_t fixUpOffset, Kind kind, const char* targetName, uint64_t offsetInTarget, uint64_t offsetInFromTarget); + Reference(macho_uintptr_t fixUpOffset, Kind kind, class Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget); + Reference(macho_uintptr_t fixUpOffset, Kind kind, class Atom& target, uint64_t offsetInTarget, class Atom& fromTarget, uint64_t offsetInFromTarget); + virtual ~Reference(); + + + virtual bool isUnbound() const; + virtual bool isWeakReference() const; + virtual bool requiresRuntimeFixUp() const; + virtual bool isLazyReference() const; + virtual Kind getKind() const; + virtual uint64_t getFixUpOffset() const; + virtual const char* getTargetName() const; + virtual ObjectFile::Atom& getTarget() const; + virtual uint64_t getTargetOffset() const; + virtual ObjectFile::Atom& getFromTarget() const; + virtual const char* getFromTargetName() const; + virtual void setTarget(ObjectFile::Atom&); + virtual void setFromTarget(ObjectFile::Atom&); + virtual void setFromTargetName(const char*); + virtual const char* getDescription() const; + virtual uint64_t getFromTargetOffset() const; + + void setLazy(bool); + void setWeak(bool); +private: + ObjectFile::Atom* fTarget; + ObjectFile::Atom* fFromTarget; + const char* fTargetName; + const char* fFromTargetName; + macho_uintptr_t fTargetOffset; + macho_uintptr_t fFromTargetOffset; + macho_uintptr_t fFixUpOffsetInSrc; + Kind fKind; + bool fLazy; + bool fWeak; +}; + + + +class Reader : public ObjectFile::Reader +{ +public: + Reader(const char* path); + Reader(const macho_header* header, const char* path, const ObjectFile::ReaderOptions& options); + virtual ~Reader(); + + virtual const char* getPath(); + virtual std::vector& getAtoms(); + virtual std::vector* getJustInTimeAtomsFor(const char* name); + virtual std::vector* getStabsDebugInfo(); // stabs info not associated with an atom + + +private: + friend class Atom; + void init(const macho_header* header, const char* path); + void buildOffsetsSet(const macho_relocation_info* reloc, const macho_section* sect, std::set& offsets, std::set& dontUse); + void addRelocReference(const macho_section* sect, const macho_relocation_info* reloc); + Atom* findAtomCoveringOffset(uint32_t offset); + uint32_t findAtomIndex(const Atom& atom); + void addFixUp(uint32_t srcAddr, uint32_t dstAddr, Reference::Kind kind, uint32_t picBaseAddr); + class Segment* makeSegmentFromSection(const macho_section*); + macho_uintptr_t commonsOffset(); + void insertOffsetIfText(std::set& offsets, uint32_t value); + void insertOffsetIfNotText(std::set& offsets, uint32_t value); + const macho_section* findSectionCoveringOffset(uint32_t offset); + void addCallSiteReference(Atom& src, uint32_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint32_t picBaseOffset, uint32_t offsetInTargetAtom); + void deadStub(Atom& target); + + const char* fPath; + const ObjectFile::ReaderOptions& fOptions; + const macho_header* fHeader; + const char* fStrings; + const macho_nlist* fSymbols; + uint32_t fSymbolCount; + const macho_segment_command* fSegment; + const uint32_t* fIndirectTable; + std::vector fAtoms; + std::vector fSegments; + std::set fDeadAtoms; + uint32_t fNonAtomStabsStartIndex; + uint32_t fNonAtomStabsCount; + std::vector fNonAtomExtras; +}; + +class Segment : public ObjectFile::Segment +{ +public: + virtual const char* getName() const; + virtual bool isContentReadable() const; + virtual bool isContentWritable() const; + virtual bool isContentExecutable() const; +protected: + Segment(const macho_section*); + friend class Reader; +private: + const macho_section* fSection; +}; + +class Atom : public ObjectFile::Atom +{ +public: + virtual ObjectFile::Reader* getFile() const; + virtual const char* getName() const; + virtual const char* getDisplayName() const; + virtual Scope getScope() const; + virtual bool isTentativeDefinition() const; + virtual bool isWeakDefinition() const; + virtual bool isCoalesableByName() const; + virtual bool isCoalesableByValue() const; + virtual bool isZeroFill() const; + virtual bool dontDeadStrip() const; + virtual bool dontStripName() const; // referenced dynamically + virtual bool isImportProxy() const; + virtual uint64_t getSize() const; + virtual std::vector& getReferences() const; + virtual bool mustRemainInSection() const; + virtual const char* getSectionName() const; + virtual Segment& getSegment() const; + virtual bool requiresFollowOnAtom() const; + virtual ObjectFile::Atom& getFollowOnAtom() const; + virtual std::vector* getStabsDebugInfo() const; + virtual uint8_t getAlignment() const; + virtual WeakImportSetting getImportWeakness() const { return ObjectFile::Atom::kWeakUnset; } + virtual void copyRawContent(uint8_t buffer[]) const; + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; + virtual void setScope(Scope); + virtual void setImportWeakness(bool weakImport) { } + + bool isLazyStub(); + +protected: + friend class Reader; + Atom(Reader&, const macho_nlist*); + Atom(Reader&, uint32_t offset); + virtual ~Atom(); + + const macho_section* findSectionFromOffset(macho_uintptr_t offset); + const macho_section* getCommonsSection(); + void setSize(macho_uintptr_t); + void setFollowOnAtom(Atom&); + static bool atomCompare(const Atom* lhs, const Atom* rhs); + Reference* addDirectReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget); + Reference* addByNameReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, const char* targetName, uint64_t offsetInTarget, uint64_t offsetInFromTarget); + Reference* addDifferenceReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, Atom& fromTarget, uint64_t offsetInFromTarget); + Reference* addReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget); + + Reader& fOwner; + const macho_nlist* fSymbol; + macho_uintptr_t fOffset; + macho_uintptr_t fSize; + const macho_section* fSection; + Segment* fSegment; + const char* fSynthesizedName; + std::vector fReferences; + ObjectFile::Atom::Scope fScope; + uint32_t fStabsStartIndex; + uint32_t fStabsCount; + + static macho_section fgCommonsSection; // for us by tentative definitions +}; + + +Reader* MakeReader(const macho_header* mh, const char* path, const ObjectFile::ReaderOptions& options) +{ + return new Reader(mh, path, options); +} + + +Reader::Reader(const macho_header* header, const char* path, const ObjectFile::ReaderOptions& options) + : fPath(NULL), fOptions(options), fHeader(NULL), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fSegment(NULL) +{ + init(header, path); +} + +Reader::Reader(const char* path) + : fPath(NULL), fOptions(*(new ObjectFile::ReaderOptions())), fHeader(NULL), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fSegment(NULL), + fNonAtomStabsStartIndex(0), fNonAtomStabsCount(0) +{ + struct stat stat_buf; + + int fd = ::open(path, O_RDONLY, 0); + ::fstat(fd, &stat_buf); + void* p = ::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE, fd, 0); + ::close(fd); + if ( ((macho_header*)p)->magic() == MH_MAGIC ) { + init((macho_header*)p, path); + return; + } + throw "add fat handling"; +} + + +Reader::~Reader() +{ +} + + +bool Atom::atomCompare(const Atom* lhs, const Atom* rhs) +{ + return lhs->fOffset < rhs->fOffset; +} + + + +void Reader::init(const macho_header* header, const char* path) +{ + // sanity check +#if defined(ARCH_PPC) + if ( (header->magic() != MH_MAGIC) || (header->cputype() != CPU_TYPE_POWERPC) ) + throw "not a valid ppc mach-o file"; +#elif defined(ARCH_I386) + if ( (header->magic() != MH_MAGIC) || (header->cputype() != CPU_TYPE_I386) ) + throw "not a valid i386 mach-o file"; +#elif defined(ARCH_PPC64) + if ( (header->magic() != MH_MAGIC_64) || (header->cputype() != CPU_TYPE_POWERPC64) ) + throw "not a valid ppc64 mach-o file"; +#endif + + // cache intersting pointers + fPath = strdup(path); + fHeader = header; + const uint32_t cmd_count = header->ncmds(); + const macho_load_command* const cmds = (macho_load_command*)((char*)header + macho_header::size); + const macho_load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_SYMTAB: + { + const macho_symtab_command* symtab = (macho_symtab_command*)cmd; + fSymbolCount = symtab->nsyms(); + fSymbols = (const macho_nlist*)((char*)header + symtab->symoff()); + fStrings = (char*)header + symtab->stroff(); + } + break; + case LC_DYSYMTAB: + { + const macho_dysymtab_command* dsymtab = (struct macho_dysymtab_command*)cmd; + fIndirectTable = (uint32_t*)((char*)fHeader + dsymtab->indirectsymoff()); + } + break; + default: + if ( cmd->cmd() == macho_segment_command::command ) { + fSegment= (macho_segment_command*)cmd; + } + break; + } + cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); + } + + // add all atoms that have entries in symbol table + std::set symbolAtomOffsets; + for (uint32_t i=0; i < fSymbolCount; ++i) { + const macho_nlist& sym = fSymbols[i]; + if ( (sym.n_type() & N_STAB) == 0 ) { + uint8_t type = (sym.n_type() & N_TYPE); + if ( (type == N_SECT) || ((type == N_UNDF) && (sym.n_value() != 0)) ) { + // real definition or "tentative definition" + Atom* newAtom = new Atom(*this, &sym); + fAtoms.push_back(newAtom); + symbolAtomOffsets.insert(newAtom->fOffset); + } + } + } + + // add all points referenced in relocations + const macho_section* const sectionsStart = (macho_section*)((char*)fSegment + sizeof(macho_segment_command)); + const macho_section* const sectionsEnd = §ionsStart[fSegment->nsects()]; + std::set cleavePoints; + std::set dontCleavePoints; + for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const macho_relocation_info* relocs = (macho_relocation_info*)((char*)(fHeader) + sect->reloff()); + const uint32_t relocCount = sect->nreloc(); + for (uint32_t r = 0; r < relocCount; ++r) { + buildOffsetsSet(&relocs[r], sect, cleavePoints, dontCleavePoints); + } + } + // add all stub functions and (non)lazy pointers + std::set deadStubOffsets; + for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + uint8_t type (sect->flags() & SECTION_TYPE); + switch ( type ) { + case S_SYMBOL_STUBS: + { + const uint32_t stubSize = sect->reserved2(); + // TVector glue sections are marked as S_SYMBOL_STUBS but are only 8 bytes + if ( stubSize > 8 ) { + for(uint32_t sectOffset=0; sectOffset < sect->size(); sectOffset += stubSize) { + uint32_t stubAddr = sect->addr() + sectOffset; + if ( cleavePoints.count(stubAddr) == 0 ) { + cleavePoints.insert(stubAddr); + deadStubOffsets.insert(stubAddr); + } + } + } + } + break; + case S_NON_LAZY_SYMBOL_POINTERS: + case S_LAZY_SYMBOL_POINTERS: + for(uint32_t sectOffset=0; sectOffset < sect->size(); sectOffset += sizeof(macho_uintptr_t)) { + uint32_t pointerAddr = sect->addr() + sectOffset; + cleavePoints.insert(pointerAddr); + } + break; + } + // also make sure each section break is a cleave point + if ( sect->size() != 0 ) + cleavePoints.insert(sect->addr()); + } + + for (std::set::iterator it=cleavePoints.begin(); it != cleavePoints.end(); it++) { + uint32_t cleavePoint = *it; + //printf("cleave offset 0x%08X\n", cleavePoint); + // only create an atom if it is not a don't-cleave point and there is not already an atom at this offset + if ( (dontCleavePoints.count(cleavePoint) == 0) && (symbolAtomOffsets.count(cleavePoint) == 0) ) + fAtoms.push_back(new Atom(*this, cleavePoint)); + } + + const uint32_t atomCount = fAtoms.size(); + if ( atomCount > 0 ) { + // sort the atoms so the occur in source file order + std::sort(fAtoms.begin(), fAtoms.end(), Atom::atomCompare); + + // tell each atom its size and follow on + const bool dontDeadStrip = ((fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS) == 0); + Atom* lastAtom = fAtoms[0]; + for (uint32_t i=1; i < atomCount; ++i) { + Atom* thisAtom = fAtoms[i]; + if ( lastAtom->getSize() == 0 ) { + if ( lastAtom->fSection == thisAtom->fSection ) + lastAtom->setSize(thisAtom->fOffset - lastAtom->fOffset); + else + lastAtom->setSize(lastAtom->fSection->addr() + lastAtom->fSection->size() - lastAtom->fOffset); + } + if ( dontDeadStrip ) + lastAtom->setFollowOnAtom(*thisAtom); + lastAtom = thisAtom; + } + lastAtom = fAtoms[atomCount-1]; + if ( lastAtom->getSize() == 0 ) + lastAtom->setSize(lastAtom->fSection->addr() + lastAtom->fSection->size() - lastAtom->fOffset); + + // add relocation based references + for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const macho_relocation_info* relocs = (macho_relocation_info*)((char*)(fHeader) + sect->reloff()); + const uint32_t relocCount = sect->nreloc(); + for (uint32_t r = 0; r < relocCount; ++r) { + addRelocReference(sect, &relocs[r]); + } + } + + // add dead stubs to list to delete + for (std::set::iterator it=deadStubOffsets.begin(); it != deadStubOffsets.end(); it++) { + Atom* deadStub = findAtomCoveringOffset(*it); + this->deadStub(*deadStub); + } + + // remove dead stubs and lazy pointers + for (std::set::iterator deadIt=fDeadAtoms.begin(); deadIt != fDeadAtoms.end(); deadIt++) { + for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { + if ( *deadIt == *it ) { + fAtoms.erase(it); + break; + } + } + } + } + + // process stabs debugging info + if ( ! fOptions.fStripDebugInfo ) { + // scan symbol table for stabs entries + fNonAtomStabsStartIndex = 0xFFFFFFFF; + fNonAtomStabsCount = 0; + uint32_t possibleStart = 0; + Atom* atom = NULL; + const uint32_t atomCount = fAtoms.size(); + enum { start, inBeginEnd, foundFirst, inFunction } state = start; + for (uint32_t symbolIndex = 0; symbolIndex < fSymbolCount; ++symbolIndex ) { + const macho_nlist* sym = &fSymbols[symbolIndex]; + uint8_t type = sym->n_type(); + if ( (type & N_STAB) != 0 ) { + if ( fNonAtomStabsStartIndex == 0xFFFFFFFF ) + fNonAtomStabsStartIndex = symbolIndex; + switch (state) { + case start: + if ( (type == N_SLINE) || (type == N_SOL) ) { + possibleStart = symbolIndex; + state = foundFirst; + } + else if ( type == N_BNSYM ) { + macho_uintptr_t targetAddr = sym->n_value(); + atom = this->findAtomCoveringOffset(targetAddr); + if ( (atom != NULL) || (atom->fOffset == targetAddr) ) { + atom->fStabsStartIndex = symbolIndex; + if ( fNonAtomStabsCount == 0 ) + fNonAtomStabsCount = symbolIndex - fNonAtomStabsStartIndex; + } + else { + fprintf(stderr, "can't find atom for stabs 0x%02X at %08X in %s\n", type, targetAddr, path); + atom = NULL; + } + state = inBeginEnd; + } + else if ( (type == N_STSYM) || (type == N_LCSYM) ) { + macho_uintptr_t targetAddr = sym->n_value(); + atom = this->findAtomCoveringOffset(targetAddr); + if ( (atom != NULL) || (atom->fOffset == targetAddr) ) { + atom->fStabsStartIndex = symbolIndex; + atom->fStabsCount = 1; + if ( fNonAtomStabsCount == 0 ) + fNonAtomStabsCount = symbolIndex - fNonAtomStabsStartIndex; + } + else { + fprintf(stderr, "can't find atom for stabs 0x%02X at %08X in %s\n", type, targetAddr, path); + atom = NULL; + } + } + else if ( type == N_GSYM ) { + // n_value field is NOT atom address ;-( + // need to find atom by name match + const char* symString = &fStrings[sym->n_strx()]; + const char* colon = strchr(symString, ':'); + bool found = false; + if ( colon != NULL ) { + int nameLen = colon - symString; + for (uint32_t searchIndex = 0; searchIndex < atomCount; ++searchIndex) { + const char* atomName = fAtoms[searchIndex]->getName(); + if ( (atomName != NULL) && (strncmp(&atomName[1], symString, nameLen) == 0) ) { + atom = fAtoms[searchIndex]; + atom->fStabsStartIndex = symbolIndex; + atom->fStabsCount = 1; + if ( fNonAtomStabsCount == 0 ) + fNonAtomStabsCount = symbolIndex - fNonAtomStabsStartIndex; + found = true; + break; + } + } + } + if ( !found ) { + fprintf(stderr, "can't find atom for N_GSYM stabs %s in %s\n", symString, path); + atom = NULL; + } + } + else if ( type == N_LSYM ) { + if ( fNonAtomStabsCount != 0 ) { + // built with -gfull and some type definition not at start of source + fNonAtomExtras.push_back(symbolIndex); + } + } + break; + case inBeginEnd: + if ( type == N_ENSYM ) { + state = start; + if ( atom != NULL ) + atom->fStabsCount = symbolIndex - atom->fStabsStartIndex + 1; + } + break; + case foundFirst: + if ( (type == N_FUN) && (sym->n_sect() != 0) ) { + state = inFunction; + macho_uintptr_t targetAddr = sym->n_value(); + atom = this->findAtomCoveringOffset(targetAddr); + if ( (atom == NULL) || (atom->fOffset != targetAddr) ) { + fprintf(stderr, "can't find atom for stabs FUN index: %d at 0x%08llX in %s\n", symbolIndex, (uint64_t)targetAddr, path); + atom = NULL; + } + else { + atom->fStabsStartIndex = possibleStart; + if ( fNonAtomStabsCount == 0 ) + fNonAtomStabsCount = possibleStart - fNonAtomStabsStartIndex; + } + } + else if ( (type == N_FUN) && (sym->n_sect() == 0) ) { + fprintf(stderr, "end stab FUN found without start FUN, index=%d in %s\n", symbolIndex, path); + state = start; + } + break; + case inFunction: + if ( (type == N_FUN) && (sym->n_sect() == 0) ) { + state = start; + if ( atom != NULL ) + atom->fStabsCount = symbolIndex - atom->fStabsStartIndex + 1; + } + break; + } + } + } + + } +} + + +void Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) +{ + uint32_t srcAddr; + uint32_t dstAddr; + Atom* src; + Atom* dst; +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + uint32_t instruction; +#endif + uint32_t* fixUpPtr; + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + const macho_relocation_info* nextReloc = &reloc[1]; +#endif + switch ( reloc->r_type() ) { +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + case PPC_RELOC_BR24: + { + if ( reloc->r_extern() ) { + instruction = OSSwapBigToHostInt32(*fixUpPtr); + int32_t displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + uint32_t offsetInTarget = sect->addr() + reloc->r_address() + displacement; + srcAddr = sect->addr() + reloc->r_address(); + src = findAtomCoveringOffset(srcAddr); + const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + const char* targetName = &fStrings[targetSymbol->n_strx()]; + src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupBranch24, targetName, offsetInTarget, 0); + } + else { + instruction = OSSwapBigToHostInt32(*fixUpPtr); + if ( (instruction & 0x4C000000) == 0x48000000 ) { + int32_t displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + srcAddr = sect->addr() + reloc->r_address(); + dstAddr = srcAddr + displacement; + src = findAtomCoveringOffset(srcAddr); + dst = findAtomCoveringOffset(dstAddr); + this->addCallSiteReference(*src, srcAddr - src->fOffset, Reference::ppcFixupBranch24, *dst, 0, dstAddr - dst->fOffset); + } + } + } + break; + case PPC_RELOC_BR14: + { + if ( reloc->r_extern() ) { + srcAddr = sect->addr() + reloc->r_address(); + src = findAtomCoveringOffset(srcAddr); + const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + const char* targetName = &fStrings[targetSymbol->n_strx()]; + src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupBranch14, targetName, 0, 0); + } + else { + instruction = OSSwapBigToHostInt32(*fixUpPtr); + int32_t displacement = (instruction & 0x0000FFFC); + if ( (displacement & 0x00008000) != 0 ) + displacement |= 0xFFFF0000; + srcAddr = sect->addr() + reloc->r_address(); + dstAddr = srcAddr + displacement; + src = findAtomCoveringOffset(srcAddr); + dst = findAtomCoveringOffset(dstAddr); + this->addCallSiteReference(*src, srcAddr - src->fOffset, Reference::ppcFixupBranch14, *dst, 0, dstAddr - dst->fOffset); + } + } + break; + case PPC_RELOC_PAIR: + // skip, processed by a previous look ahead + break; + case PPC_RELOC_LO16: + { + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + printf("PPC_RELOC_LO16 missing following pair\n"); + break; + } + srcAddr = sect->addr() + reloc->r_address(); + if ( reloc->r_extern() ) { + const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + const char* targetName = &fStrings[targetSymbol->n_strx()]; + src = findAtomCoveringOffset(srcAddr); + instruction = OSSwapBigToHostInt32(*fixUpPtr); + dstAddr = (nextReloc->r_address() << 16) | (instruction & 0x0000FFFF); + src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupAbsLow16, targetName, dstAddr, 0); + } + else { + instruction = OSSwapBigToHostInt32(*fixUpPtr); + int16_t lowBits = (instruction & 0xFFFF); + dstAddr = (nextReloc->r_address() << 16) + (int32_t)lowBits; + if ( (lowBits & 0x8000) != 0 ) + dstAddr += 0x10000; + addFixUp(srcAddr, dstAddr, Reference::ppcFixupAbsLow16, 0); + } + } + break; + case PPC_RELOC_LO14: + { + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + printf("PPC_RELOC_LO14 missing following pair\n"); + break; + } + srcAddr = sect->addr() + reloc->r_address(); + if ( reloc->r_extern() ) { + const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + const char* targetName = &fStrings[targetSymbol->n_strx()]; + src = findAtomCoveringOffset(srcAddr); + instruction = OSSwapBigToHostInt32(*fixUpPtr); + dstAddr = (nextReloc->r_address() << 16) | (instruction & 0x0000FFFC); + src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupAbsLow14, targetName, dstAddr, 0); + } + else { + instruction = OSSwapBigToHostInt32(*fixUpPtr); + dstAddr = (nextReloc->r_address() << 16) | (instruction & 0x0000FFFC); + addFixUp(srcAddr, dstAddr, Reference::ppcFixupAbsLow14, 0); + } + } + break; + case PPC_RELOC_HI16: + { + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + printf("PPC_RELOC_HI16 missing following pair\n"); + break; + } + srcAddr = sect->addr() + reloc->r_address(); + if ( reloc->r_extern() ) { + const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + const char* targetName = &fStrings[targetSymbol->n_strx()]; + src = findAtomCoveringOffset(srcAddr); + instruction = OSSwapBigToHostInt32(*fixUpPtr); + dstAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); + src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupAbsHigh16, targetName, dstAddr, 0); + } + else { + instruction = OSSwapBigToHostInt32(*fixUpPtr); + dstAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); + addFixUp(srcAddr, dstAddr, Reference::ppcFixupAbsHigh16, 0); + } + } + break; + case PPC_RELOC_HA16: + { + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + printf("PPC_RELOC_HA16 missing following pair\n"); + break; + } + srcAddr = sect->addr() + reloc->r_address(); + if ( reloc->r_extern() ) { + const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + const char* targetName = &fStrings[targetSymbol->n_strx()]; + instruction = OSSwapBigToHostInt32(*fixUpPtr); + int16_t lowBits = (nextReloc->r_address() & 0x0000FFFF); + dstAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; + src = findAtomCoveringOffset(srcAddr); + src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupAbsHigh16AddLow, targetName, dstAddr, 0); + } + else { + instruction = OSSwapBigToHostInt32(*fixUpPtr); + int16_t lowBits = (nextReloc->r_address() & 0x0000FFFF); + dstAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; + addFixUp(srcAddr, dstAddr, Reference::ppcFixupAbsHigh16AddLow, 0); + } + } + break; + case GENERIC_RELOC_VANILLA: + { + srcAddr = sect->addr() + reloc->r_address(); + Atom* srcAtom = findAtomCoveringOffset(srcAddr); + // lazy pointers have references to dyld_stub_binding_helper which need to be ignored + if ( (srcAtom->fSection->flags() & SECTION_TYPE) != S_LAZY_SYMBOL_POINTERS ) { + uint32_t offsetInSrcAtom = srcAddr - srcAtom->fOffset; + macho_uintptr_t pointerValue = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr)); + if ( reloc->r_extern() ) { + const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + uint8_t type = targetSymbol->n_type() & N_TYPE; + if ( type == N_UNDF ) { + const char* targetName = &fStrings[targetSymbol->n_strx()]; + macho_uintptr_t addend = pointerValue; + srcAtom->addByNameReference(offsetInSrcAtom, Reference::pointer, targetName, addend, 0); + } + else { + dstAddr = targetSymbol->n_value(); + Atom* dstAtom = findAtomCoveringOffset(dstAddr); + macho_uintptr_t addend = pointerValue; + srcAtom->addReference(offsetInSrcAtom, Reference::pointer, *dstAtom, addend, 0); + } + } + else { + Atom* dstAtom = findAtomCoveringOffset(pointerValue); + srcAtom->addReference(offsetInSrcAtom, Reference::pointer, *dstAtom, pointerValue-dstAtom->fOffset, 0); + } + } + } + break; + case PPC_RELOC_JBSR: + // ignore for now + break; +#endif +#if defined(ARCH_I386) + case GENERIC_RELOC_VANILLA: + { + srcAddr = sect->addr() + reloc->r_address(); + src = findAtomCoveringOffset(srcAddr); + // lazy pointers have references to dyld_stub_binding_helper which need to be ignored + if ( (src->fSection->flags() & SECTION_TYPE) != S_LAZY_SYMBOL_POINTERS ) { + if ( reloc->r_length() != 2 ) + throw "bad vanilla relocation length"; + Reference::Kind kind; + macho_uintptr_t pointerValue = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr)); + if ( reloc->r_pcrel() ) { + kind = Reference::x86FixupBranch32; + pointerValue += reloc->r_address() + sizeof(macho_uintptr_t); + } + else { + kind = Reference::pointer; + } + uint32_t offsetInSrcAtom = srcAddr - src->fOffset; + if ( reloc->r_extern() ) { + const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + uint8_t type = targetSymbol->n_type() & N_TYPE; + if ( type == N_UNDF ) { + const char* targetName = &fStrings[targetSymbol->n_strx()]; + macho_uintptr_t addend = pointerValue; + src->addByNameReference(offsetInSrcAtom, kind, targetName, addend, 0); + } + else { + dstAddr = targetSymbol->n_value(); + dst = findAtomCoveringOffset(dstAddr); + macho_uintptr_t addend = pointerValue - dstAddr; + src->addReference(offsetInSrcAtom, kind, *dst, addend, 0); + } + } + else { + dst = findAtomCoveringOffset(pointerValue); + src->addReference(offsetInSrcAtom, kind, *dst, 0, 0); + } + } + } + break; +#endif + + default: + printf("unknown relocation type %d\n", reloc->r_type()); + } + } + else { + const macho_scattered_relocation_info* sreloc = (macho_scattered_relocation_info*)reloc; + srcAddr = sect->addr() + sreloc->r_address(); + dstAddr = sreloc->r_value(); + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); + const macho_scattered_relocation_info* nextSReloc = &sreloc[1]; + const macho_relocation_info* nextReloc = &reloc[1]; + // file format allows pair to be scattered or not + bool nextRelocIsPair = false; + uint32_t nextRelocAddress = 0; + uint32_t nextRelocValue = 0; + if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { + if ( nextReloc->r_type() == PPC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextReloc->r_address(); + } + } + else { + if ( nextSReloc->r_type() == PPC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextSReloc->r_address(); + nextRelocValue = nextSReloc->r_value(); + } + } + switch (sreloc->r_type()) { + case GENERIC_RELOC_VANILLA: + { + macho_uintptr_t betterDstAddr = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr)); + //fprintf(stderr, "pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08lX\n", srcAddr, dstAddr, betterDstAddr); + // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) + Atom* src = findAtomCoveringOffset(srcAddr); + Atom* dst = findAtomCoveringOffset(dstAddr); + src->addReference(srcAddr - src->fOffset, Reference::pointer, *dst, betterDstAddr - dst->fOffset, 0); + } + break; +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + case PPC_RELOC_BR24: + { + instruction = OSSwapBigToHostInt32(*fixUpPtr); + if ( (instruction & 0x4C000000) == 0x48000000 ) { + int32_t displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + srcAddr = sect->addr() + sreloc->r_address(); + dstAddr = sreloc->r_value(); + src = findAtomCoveringOffset(srcAddr); + dst = findAtomCoveringOffset(dstAddr); + this->addCallSiteReference(*src, srcAddr - src->fOffset, Reference::ppcFixupBranch24, *dst, 0, srcAddr + displacement - sreloc->r_value()); + } + } + break; + case PPC_RELOC_LO16_SECTDIFF: + { + if ( ! nextRelocIsPair) { + printf("PPC_RELOC_LO16_SECTDIFF missing following PAIR\n"); + break; + } + src = findAtomCoveringOffset(srcAddr); + dst = findAtomCoveringOffset(dstAddr); + instruction = OSSwapBigToHostInt32(*fixUpPtr); + int16_t lowBits = (instruction & 0xFFFF); + int32_t displacement = (nextRelocAddress << 16) + (int32_t)lowBits; + if ( (lowBits & 0x8000) != 0 ) + displacement += 0x10000; + uint32_t picBaseOffset = nextRelocValue - src->fOffset; + int64_t dstOffset = src->fOffset + picBaseOffset + displacement - dst->fOffset; + src->addReference(srcAddr - src->fOffset, Reference::ppcFixupPicBaseLow16, *dst, dstOffset, picBaseOffset); + } + break; + case PPC_RELOC_LO14_SECTDIFF: + { + if ( ! nextRelocIsPair) { + printf("PPC_RELOC_LO14_SECTDIFF missing following PAIR\n"); + break; + } + src = findAtomCoveringOffset(srcAddr); + dst = findAtomCoveringOffset(dstAddr); + instruction = OSSwapBigToHostInt32(*fixUpPtr); + int16_t lowBits = (instruction & 0xFFFC); + int32_t displacement = (nextRelocAddress << 16) + (int32_t)lowBits; + if ( (lowBits & 0x8000) != 0 ) + displacement += 0x10000; + uint32_t picBaseOffset = nextRelocValue - src->fOffset; + int64_t dstOffset = src->fOffset + picBaseOffset + displacement - dst->fOffset; + src->addReference(srcAddr - src->fOffset, Reference::ppcFixupPicBaseLow14, *dst, dstOffset, picBaseOffset); + } + break; + case PPC_RELOC_HA16_SECTDIFF: + { + if ( ! nextRelocIsPair) { + printf("PPC_RELOC_LO14_SECTDIFF missing following PAIR\n"); + break; + } + src = findAtomCoveringOffset(srcAddr); + dst = findAtomCoveringOffset(dstAddr); + instruction = OSSwapBigToHostInt32(*fixUpPtr); + int16_t lowBits = (nextRelocAddress & 0x0000FFFF); + int32_t displacement = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; + uint32_t picBaseOffset = nextRelocValue - src->fOffset; + int64_t dstOffset = src->fOffset + picBaseOffset + displacement - dst->fOffset; + src->addReference(srcAddr - src->fOffset, Reference::ppcFixupPicBaseHigh16, *dst, dstOffset, picBaseOffset); + } + break; + case PPC_RELOC_LO14: + { + if ( ! nextRelocIsPair) { + printf("PPC_RELOC_LO14 missing following PAIR\n"); + break; + } + src = findAtomCoveringOffset(srcAddr); + dst = findAtomCoveringOffset(dstAddr); + instruction = OSSwapBigToHostInt32(*fixUpPtr); + int16_t lowBits = (instruction & 0xFFFC); + uint32_t betterDstAddr = (nextRelocAddress << 16) + (int32_t)lowBits; + if ( (lowBits & 0x8000) != 0 ) + betterDstAddr += 0x10000; + src->addReference(srcAddr - src->fOffset, Reference::ppcFixupAbsLow14, *dst, betterDstAddr - dst->fOffset, 0); + } + break; + case PPC_RELOC_LO16: + { + if ( ! nextRelocIsPair) { + printf("PPC_RELOC_LO16 missing following PAIR\n"); + break; + } + src = findAtomCoveringOffset(srcAddr); + dst = findAtomCoveringOffset(dstAddr); + instruction = OSSwapBigToHostInt32(*fixUpPtr); + int16_t lowBits = (instruction & 0xFFFF); + uint32_t betterDstAddr = (nextRelocAddress << 16) + (int32_t)lowBits; + if ( (lowBits & 0x8000) != 0 ) + betterDstAddr += 0x10000; + src->addReference(srcAddr - src->fOffset, Reference::ppcFixupAbsLow16, *dst, betterDstAddr - dst->fOffset, 0); + } + break; + case PPC_RELOC_HA16: + { + if ( ! nextRelocIsPair) { + printf("PPC_RELOC_HA16 missing following PAIR\n"); + break; + } + src = findAtomCoveringOffset(srcAddr); + dst = findAtomCoveringOffset(dstAddr); + instruction = OSSwapBigToHostInt32(*fixUpPtr); + int16_t lowBits = (nextRelocAddress & 0xFFFF); + uint32_t betterDstAddr = ((instruction & 0xFFFF) << 16) + (int32_t)lowBits; + src->addReference(srcAddr - src->fOffset, Reference::ppcFixupAbsHigh16AddLow, *dst, betterDstAddr - dst->fOffset, 0); + } + break; + case PPC_RELOC_SECTDIFF: + case PPC_RELOC_LOCAL_SECTDIFF: + { + const macho_scattered_relocation_info* nextReloc = &sreloc[1]; + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + printf("PPC_RELOC_SECTDIFF missing following pair\n"); + break; + } + srcAddr = sect->addr() + sreloc->r_address(); + uint32_t toAddr = sreloc->r_value(); + uint32_t fromAddr = nextReloc->r_value(); + src = findAtomCoveringOffset(srcAddr); + Atom* to = findAtomCoveringOffset(toAddr); + Atom* from = findAtomCoveringOffset(fromAddr); + //macho_intptr_t pointerValue = *(macho_intptr_t*)fixUpPtr; + //uint64_t toOffset = to->fOffset; + //uint64_t fromOffset = from->fOffset; + //int64_t pointerValue64 = pointerValue; + //uint64_t addend = pointerValue64 - (toOffset - fromOffset); + Reference::Kind kind = Reference::pointer32Difference; + if ( sreloc->r_length() == 3 ) + kind = Reference::pointer64Difference; + src->addDifferenceReference(srcAddr - src->fOffset, kind, *to, toAddr - to->fOffset, *from, fromAddr - from->fOffset); + } + break; + case PPC_RELOC_PAIR: + break; + case PPC_RELOC_HI16_SECTDIFF: + printf("unexpected scattered relocation type PPC_RELOC_HI16_SECTDIFF\n"); + break; +#endif +#if defined(ARCH_I386) + case GENERIC_RELOC_SECTDIFF: + case GENERIC_RELOC_LOCAL_SECTDIFF: + { + if ( nextSReloc->r_type() != GENERIC_RELOC_PAIR ) { + printf("GENERIC_RELOC_SECTDIFF missing following pair\n"); + break; + } + srcAddr = sect->addr() + sreloc->r_address(); + uint32_t toAddr = sreloc->r_value(); + uint32_t fromAddr = nextSReloc->r_value(); + src = findAtomCoveringOffset(srcAddr); + Atom* to = findAtomCoveringOffset(toAddr); + Atom* from = findAtomCoveringOffset(fromAddr); + Reference::Kind kind = Reference::pointer32Difference; + if ( sreloc->r_length() != 2 ) + throw "bad length for GENERIC_RELOC_SECTDIFF"; + src->addDifferenceReference(srcAddr - src->fOffset, kind, *to, toAddr - to->fOffset, *from, fromAddr - from->fOffset); + } + break; + case GENERIC_RELOC_PAIR: + // do nothing, already used via a look ahead + break; +#endif + default: + printf("unknown scattered relocation type %d\n", sreloc->r_type()); + } + + } +} + + +void Reader::addFixUp(uint32_t srcAddr, uint32_t dstAddr, Reference::Kind kind, uint32_t picBaseAddr) +{ + Atom* src = findAtomCoveringOffset(srcAddr); + Atom* dst = findAtomCoveringOffset(dstAddr); + src->addReference(srcAddr - src->fOffset, kind, *dst, dstAddr - dst->fOffset, picBaseAddr - src->fOffset); +} + +Atom* Reader::findAtomCoveringOffset(uint32_t offset) +{ +#if 1 + // binary search of sorted atoms + Atom** base = &fAtoms[0]; + for (uint32_t n = fAtoms.size(); n > 0; n /= 2) { + Atom** pivot = &base[n/2]; + Atom* pivotAtom = *pivot; + if ( pivotAtom->fOffset <= offset ) { + if ( offset < (pivotAtom->fOffset + pivotAtom->fSize) ) + return pivotAtom; + // key > pivot + // move base to symbol after pivot + base = &pivot[1]; + --n; + } + else { + // key < pivot + // keep same base + } + } +#else + const uint32_t atomCount = fAtoms.size(); + for (uint32_t i=0; i < atomCount; ++i) { + Atom* atom = fAtoms[i]; + if ( (atom->fOffset <= offset) && (offset < (atom->fOffset + atom->fSize)) ) + return atom; + } +#endif + return NULL; +} + +uint32_t Reader::findAtomIndex(const Atom& atom) +{ + const Atom* target = &atom; + const uint32_t atomCount = fAtoms.size(); + for (uint32_t i=0; i < atomCount; ++i) { + Atom* anAtom = fAtoms[i]; + if ( anAtom == target ) + return i; + } + return 0xffffffff; +} + +static void insertOffset(std::set& offsets, uint32_t value) +{ + //fprintf(stderr, "cleave point at 0x%08X\n", value); + offsets.insert(value); +} + +void Reader::insertOffsetIfNotText(std::set& offsets, uint32_t value) +{ + const macho_section* sect = findSectionCoveringOffset(value); + if ( (sect == NULL) || (strcmp(sect->segname(),"__TEXT") != 0) || (strncmp(sect->sectname(),"__text", 6) != 0) ) { + offsets.insert(value); + } +} + +void Reader::insertOffsetIfText(std::set& offsets, uint32_t value) +{ + const macho_section* sect = findSectionCoveringOffset(value); + if ( (sect != NULL) && (strcmp(sect->segname(),"__TEXT") == 0) && (strncmp(sect->sectname(),"__text", 6) == 0) ) { + //fprintf(stderr, "don't cleave point at 0x%08X\n", value); + offsets.insert(value); + } +} + +const macho_section* Reader::findSectionCoveringOffset(uint32_t offset) +{ + const macho_section* const sectionsStart = (macho_section*)((char*)fSegment + sizeof(macho_segment_command)); + const macho_section* const sectionsEnd = §ionsStart[fSegment->nsects()]; + for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->addr() <= offset) && (offset < (sect->addr() + sect->size())) ) + return sect; + } + return NULL; +} + + +void Reader::buildOffsetsSet(const macho_relocation_info* reloc, const macho_section* sect, std::set& cleavePoints, std::set& dontCleavePoints) +{ +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + uint32_t targetAddr; +#endif + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + uint32_t* fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + uint32_t instruction; +#endif + switch ( reloc->r_type() ) { +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + case PPC_RELOC_BR14: + // do nothing. local branch should not cleave + break; + case PPC_RELOC_BR24: + { + if ( ! reloc->r_extern() ) { + instruction = OSSwapBigToHostInt32(*fixUpPtr); + if ( (instruction & 0x4C000000) == 0x48000000 ) { + int32_t displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + //cleavePoints.insert(reloc->r_address() + displacement); + insertOffset(cleavePoints, sect->addr() + reloc->r_address() + displacement); + } + } + } + break; + case PPC_RELOC_PAIR: + // skip, processed by a look ahead + break; + case PPC_RELOC_LO16: + { + const macho_relocation_info* nextReloc = &reloc[1]; + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + printf("PPC_RELOC_LO16 missing following pair\n"); + break; + } + if ( ! reloc->r_extern() ) { + instruction = OSSwapBigToHostInt32(*fixUpPtr); + targetAddr = (nextReloc->r_address() << 16) | (instruction & 0x0000FFFF); + //cleavePoints.insert(targetAddr); + insertOffset(cleavePoints, (targetAddr)); + } + } + break; + case PPC_RELOC_LO14: + { + const macho_relocation_info* nextReloc = &reloc[1]; + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + printf("PPC_RELOC_LO14 missing following pair\n"); + break; + } + if ( ! reloc->r_extern() ) { + instruction = OSSwapBigToHostInt32(*fixUpPtr); + targetAddr = (nextReloc->r_address() << 16) | (instruction & 0x0000FFFC); + //cleavePoints.insert(targetAddr); + insertOffset(cleavePoints, (targetAddr)); + } + } + break; + case PPC_RELOC_HI16: + { + const macho_relocation_info* nextReloc = &reloc[1]; + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + printf("PPC_RELOC_HI16 missing following pair\n"); + break; + } + if ( ! reloc->r_extern() ) { + instruction = OSSwapBigToHostInt32(*fixUpPtr); + targetAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); + //cleavePoints.insert(targetAddr); + insertOffset(cleavePoints, targetAddr); + } + } + break; + case PPC_RELOC_HA16: + { + const macho_relocation_info* nextReloc = &reloc[1]; + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + printf("PPC_RELOC_HA16 missing following pair\n"); + break; + } + if ( ! reloc->r_extern() ) { + instruction = OSSwapBigToHostInt32(*fixUpPtr); + int16_t lowBits = (nextReloc->r_address() & 0x0000FFFF); + targetAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; + //cleavePoints.insert(targetAddr); + insertOffset(cleavePoints, targetAddr); + } + } + break; + case PPC_RELOC_JBSR: + // ignore for now + break; +#endif + case GENERIC_RELOC_VANILLA: + { +#if defined(ARCH_PPC64) + if ( reloc->r_length() != 3 ) + throw "vanilla pointer relocation found that is not 8-bytes"; +#elif defined(ARCH_PPC) || defined(ARCH_I386) + if ( reloc->r_length() != 2 ) + throw "vanilla pointer relocation found that is not 4-bytes"; +#endif + //fprintf(stderr, "pcrel=%d, len=%d, extern=%d, type=%d\n", reloc->r_pcrel(), reloc->r_length(), reloc->r_extern(), reloc->r_type()); + if ( !reloc->r_extern() ) { + macho_uintptr_t pointerValue = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr)); + if ( reloc->r_pcrel() ) + pointerValue += reloc->r_address() + sizeof(macho_uintptr_t); + // a pointer into code does not cleave the code (gcc always pointers to labels) + insertOffsetIfNotText(cleavePoints, pointerValue); + } + } + break; + default: + printf("unknown relocation type %d\n", reloc->r_type()); + } + } + else { + const macho_scattered_relocation_info* sreloc = (macho_scattered_relocation_info*)reloc; + switch (sreloc->r_type()) { + case GENERIC_RELOC_VANILLA: + insertOffset(cleavePoints, sreloc->r_value()); + break; +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + case PPC_RELOC_BR24: + insertOffset(cleavePoints, sreloc->r_value()); + break; + case PPC_RELOC_HA16: + case PPC_RELOC_HI16: + case PPC_RELOC_LO16: + case PPC_RELOC_LO14: + case PPC_RELOC_LO16_SECTDIFF: + case PPC_RELOC_LO14_SECTDIFF: + case PPC_RELOC_HA16_SECTDIFF: + case PPC_RELOC_HI16_SECTDIFF: + //cleavePoints.insert(sreloc->r_value()); + insertOffset(cleavePoints, sreloc->r_value()); + insertOffsetIfText(dontCleavePoints, sreloc->r_value()); + break; + case PPC_RELOC_SECTDIFF: + case PPC_RELOC_LOCAL_SECTDIFF: + // these do not cleave up a .o file + // a SECTDIFF in __TEXT probably means a jump table, and should prevent a cleave + { + const macho_scattered_relocation_info* nextReloc = &sreloc[1]; + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + printf("PPC_RELOC_SECTDIFF missing following pair\n"); + break; + } + insertOffsetIfText(dontCleavePoints, sreloc->r_value()); + insertOffsetIfText(dontCleavePoints, nextReloc->r_value()); + } + break; + case PPC_RELOC_PAIR: + // do nothing, already used via a look ahead + break; +#endif +#if defined(ARCH_I386) + case GENERIC_RELOC_SECTDIFF: + case GENERIC_RELOC_LOCAL_SECTDIFF: + // these do not cleave up a .o file + // a SECTDIFF in __TEXT probably means a jump table, and should prevent a cleave + { + const macho_scattered_relocation_info* nextReloc = &sreloc[1]; + if ( nextReloc->r_type() != GENERIC_RELOC_PAIR ) { + printf("GENERIC_RELOC_SECTDIFF missing following pair\n"); + break; + } + insertOffsetIfText(dontCleavePoints, sreloc->r_value()); + insertOffsetIfText(dontCleavePoints, nextReloc->r_value()); + } + break; + case GENERIC_RELOC_PAIR: + // do nothing, already used via a look ahead + break; +#endif + default: + printf("unknown relocation type %d\n", sreloc->r_type()); + } + + } +} + + +Segment* Reader::makeSegmentFromSection(const macho_section* sect) +{ + // make segment object if one does not already exist + const uint32_t segmentCount = fSegments.size(); + for (uint32_t i=0; i < segmentCount; ++i) { + Segment* seg = fSegments[i]; + if ( strcmp(sect->segname(), seg->getName()) == 0 ) + return seg; + } + Segment* seg = new Segment(sect); + fSegments.push_back(seg); + return seg; +} + +macho_uintptr_t Reader::commonsOffset() +{ + return fSegment->vmsize(); +} + +const char* Reader::getPath() +{ + return fPath; +} + +std::vector& Reader::getAtoms() +{ + return (std::vector&)(fAtoms); +} + +std::vector* Reader::getJustInTimeAtomsFor(const char* name) +{ + // object files have no just-in-time atoms + return NULL; +} + + +std::vector* Reader::getStabsDebugInfo() +{ + if ( fNonAtomStabsCount == 0 ) + return NULL; + + std::vector* stabs = new std::vector(); + stabs->reserve(fNonAtomStabsCount); + + for (uint32_t i=0; i < fNonAtomStabsCount; ++i) { + const macho_nlist* sym = &fSymbols[fNonAtomStabsStartIndex+i]; + if ( (sym->n_type() & N_STAB) != 0 ) { + ObjectFile::StabsInfo stab; + stab.atomOffset = sym->n_value(); + stab.string = &fStrings[sym->n_strx()]; + stab.type = sym->n_type(); + stab.other = sym->n_sect(); + stab.desc = sym->n_desc(); + // for N_SO n_value is offset of first atom, but our gdb ignores this, so we omit that calculation + if ( stab.type == N_SO ) + stab.atomOffset = 0; + stabs->push_back(stab); + } + } + + // add any extra N_LSYM's not at start of symbol table + for (std::vector::iterator it=fNonAtomExtras.begin(); it != fNonAtomExtras.end(); ++it) { + const macho_nlist* sym = &fSymbols[*it]; + ObjectFile::StabsInfo stab; + stab.atomOffset = sym->n_value(); + stab.string = &fStrings[sym->n_strx()]; + stab.type = sym->n_type(); + stab.other = sym->n_sect(); + stab.desc = sym->n_desc(); + stabs->push_back(stab); + } + + return stabs; +} + +void Reader::deadStub(Atom& target) +{ + // remove stub + fDeadAtoms.insert(&target); + + // remove lazy pointer + const int stubNameLen = strlen(target.fSynthesizedName); + char lazyName[stubNameLen+8]; + strcpy(lazyName, target.fSynthesizedName); + strcpy(&lazyName[stubNameLen-5], "$lazy_ptr"); + const uint32_t atomCount = fAtoms.size(); + for (uint32_t i=1; i < atomCount; ++i) { + Atom* atom = fAtoms[i]; + if ( (atom->fSynthesizedName != NULL) && (strcmp(atom->fSynthesizedName, lazyName) == 0) ) { + fDeadAtoms.insert(atom); + break; + } + } +} + + +void Reader::addCallSiteReference(Atom& src, uint32_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint32_t picBaseOffset, uint32_t offsetInTargetAtom) +{ + // the compiler some times produces stub to static functions and then calls the stubs + // we need to skip the stub if a static function exists with the same name and remove the stub + if ( target.isLazyStub() ) { + const macho_section* section = target.fSection; + uint32_t index = (target.fOffset - section->addr()) / section->reserved2(); + uint32_t indirectTableIndex = section->reserved1() + index; + uint32_t symbolIndex = ENDIAN_READ32(fIndirectTable[indirectTableIndex]); + if ( (symbolIndex & INDIRECT_SYMBOL_LOCAL) == 0 ) { + const macho_nlist* sym = &fSymbols[symbolIndex]; + if ( (sym->n_value() != 0) && ((sym->n_type() & N_EXT) == 0) ) { + Atom* betterTarget = this->findAtomCoveringOffset(sym->n_value()); + if ( (betterTarget != NULL) && (betterTarget->getScope() == ObjectFile::Atom::scopeTranslationUnit) ) { + // use direct reference to static function + src.addDirectReference(offsetInSrcAtom, kind, *betterTarget, offsetInTargetAtom, picBaseOffset); + + // remove stub and lazy pointer + this->deadStub(target); + return; + } + } + } + } + + // fall through to general case + src.addReference(offsetInSrcAtom, kind, target, offsetInTargetAtom, picBaseOffset); +} + + +Atom::Atom(Reader& owner, const macho_nlist* symbol) + : fOwner(owner), fSymbol(symbol), fOffset(0), fSize(0), fSection(NULL), fSegment(NULL), fSynthesizedName(NULL), + fStabsStartIndex(0), fStabsCount(0) +{ + uint8_t type = symbol->n_type(); + if ( (type & N_EXT) == 0 ) + fScope = ObjectFile::Atom::scopeTranslationUnit; + else if ( (type & N_PEXT) != 0 ) + fScope = ObjectFile::Atom::scopeLinkageUnit; + else + fScope = ObjectFile::Atom::scopeGlobal; + if ( (type & N_TYPE) == N_SECT ) { + // real definition + const macho_section* sections = (macho_section*)((char*)fOwner.fSegment + sizeof(macho_segment_command)); + fSection = §ions[fSymbol->n_sect()-1]; + fSegment = owner.makeSegmentFromSection(fSection); + fOffset = fSymbol->n_value(); + uint8_t type = fSection->flags() & SECTION_TYPE; + switch ( type ) { + case S_LAZY_SYMBOL_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + { + // get target name out of indirect symbol table + uint32_t index = (fOffset - fSection->addr()) / sizeof(macho_uintptr_t); + index += fSection->reserved1(); + uint32_t symbolIndex = ENDIAN_READ32(fOwner.fIndirectTable[index]); + uint32_t strOffset = fOwner.fSymbols[symbolIndex].n_strx(); + const char* name = &fOwner.fStrings[strOffset]; + Reference* ref = this->addByNameReference(0, Reference::pointer, name, 0, 0); + if ( type == S_LAZY_SYMBOL_POINTERS ) { + ref->setLazy(true); + ref->setFromTargetName("dyld_stub_binding_helper"); + } + } + break; + } + } + else if ( ((type & N_TYPE) == N_UNDF) && (symbol->n_value() != 0) ) { + // tentative definition + fSize = symbol->n_value(); + fSection = getCommonsSection(); + fSegment = owner.makeSegmentFromSection(fSection); + fOffset = owner.commonsOffset(); + } + else { + printf("unknown symbol type: %d\n", type); + } +} + +Atom::Atom(Reader& owner, uint32_t offset) + : fOwner(owner), fSymbol(NULL), fOffset(offset), fSize(0), fSection(NULL), fSegment(NULL), fSynthesizedName(NULL), + fStabsStartIndex(0), fStabsCount(0) +{ + fSection = findSectionFromOffset(offset); + fScope = ObjectFile::Atom::scopeLinkageUnit; + fSegment = owner.makeSegmentFromSection(fSection); + uint8_t type = fSection->flags() & SECTION_TYPE; + switch ( type ) { + case S_SYMBOL_STUBS: + { + uint32_t index = (offset - fSection->addr()) / fSection->reserved2(); + index += fSection->reserved1(); + uint32_t symbolIndex = ENDIAN_READ32(fOwner.fIndirectTable[index]); + uint32_t strOffset = fOwner.fSymbols[symbolIndex].n_strx(); + const char* name = &fOwner.fStrings[strOffset]; + char* str = new char[strlen(name)+8]; + strcpy(str, name); + strcat(str, "$stub"); + fSynthesizedName = str; + } + break; + case S_LAZY_SYMBOL_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + { + uint32_t index = (offset - fSection->addr()) / sizeof(macho_uintptr_t); + index += fSection->reserved1(); + uint32_t symbolIndex = ENDIAN_READ32(fOwner.fIndirectTable[index]); + uint32_t strOffset = fOwner.fSymbols[symbolIndex].n_strx(); + const char* name = &fOwner.fStrings[strOffset]; + char* str = new char[strlen(name)+16]; + strcpy(str, name); + if ( type == S_LAZY_SYMBOL_POINTERS ) + strcat(str, "$lazy_ptr"); + else + strcat(str, "$non_lazy_ptr"); + fSynthesizedName = str; + Reference* ref = this->addByNameReference(0, Reference::pointer, name, 0, 0); + if ( type == S_LAZY_SYMBOL_POINTERS ) { + ref->setLazy(true); + ref->setFromTargetName("dyld_stub_binding_helper"); + } + const macho_nlist* sym = &fOwner.fSymbols[symbolIndex]; + if ( (sym->n_type() & N_TYPE) == N_UNDF ) { + if ( (sym->n_desc() & N_WEAK_REF) != 0 ) + ref->setWeak(true); + } + } + break; + } +} + + +Atom::~Atom() +{ +} + +macho_section Atom::fgCommonsSection; + + +bool Atom::isLazyStub() +{ + return ( (fSection->flags() & SECTION_TYPE) == S_SYMBOL_STUBS); +} + +const macho_section* Atom::getCommonsSection() { + if ( strcmp(fgCommonsSection.sectname(), "__common") != 0 ) { + fgCommonsSection.set_sectname("__common"); + fgCommonsSection.set_segname("__DATA"); + fgCommonsSection.set_flags(S_ZEROFILL); + } + return &fgCommonsSection; +} + +ObjectFile::Reader* Atom::getFile() const +{ + return &fOwner; +} + + +const char* Atom::getName() const +{ + if ( fSymbol != NULL ) + return &fOwner.fStrings[fSymbol->n_strx()]; + else + return fSynthesizedName; +} + +const char* Atom::getDisplayName() const +{ + if ( fSymbol != NULL ) + return &fOwner.fStrings[fSymbol->n_strx()]; + + if ( fSynthesizedName != NULL ) + return fSynthesizedName; + + static char temp[32]; + sprintf(temp, "atom #%u", fOwner.findAtomIndex(*this)); + return temp; +} + +ObjectFile::Atom::Scope Atom::getScope() const +{ + return fScope; +} + +void Atom::setScope(ObjectFile::Atom::Scope newScope) +{ + fScope = newScope; +} + + +bool Atom::isWeakDefinition() const +{ + if ( isTentativeDefinition() ) + return true; + if ( fSymbol != NULL ) + return ( (fSymbol->n_desc() & N_WEAK_DEF) != 0 ); + uint8_t type = fSection->flags() & SECTION_TYPE; + switch ( type ) { + case S_SYMBOL_STUBS: + case S_LAZY_SYMBOL_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + return true; + } + return false; +} + +bool Atom::isTentativeDefinition() const +{ + return (fSection == &fgCommonsSection); +} + +bool Atom::isCoalesableByName() const +{ + uint8_t type = fSection->flags() & SECTION_TYPE; + switch ( type ) { + case S_SYMBOL_STUBS: + case S_COALESCED: + return true; + }; + if ( isTentativeDefinition() ) + return true; + return false; +} + +bool Atom::isCoalesableByValue() const +{ + uint8_t type = fSection->flags() & SECTION_TYPE; + switch ( type ) { + case S_CSTRING_LITERALS: + case S_4BYTE_LITERALS: + case S_8BYTE_LITERALS: + return true; + }; + return false; +} + +bool Atom::isZeroFill() const +{ + return ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL); +} + +bool Atom::dontDeadStrip() const +{ + if ( fSymbol != NULL ) + return ( (fSymbol->n_desc() & N_NO_DEAD_STRIP) != 0 ); + return false; +} + + +bool Atom::dontStripName() const +{ + if ( fSymbol != NULL ) + return ( (fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0 ); + return false; +} + +bool Atom::isImportProxy() const +{ + return false; +} + + +uint64_t Atom::getSize() const +{ + //return fOffset; + return fSize; +} + + +std::vector& Atom::getReferences() const +{ + return (std::vector&)(fReferences); +} + +bool Atom::mustRemainInSection() const +{ + return true; +} + +const char* Atom::getSectionName() const +{ + if ( strlen(fSection->sectname()) > 15 ) { + static char temp[18]; + strncpy(temp, fSection->sectname(), 16); + temp[17] = '\0'; + return temp; + } + return fSection->sectname(); +} + +Segment& Atom::getSegment() const +{ + return *fSegment; +} + +bool Atom::requiresFollowOnAtom() const +{ + // requires follow-on if built with old compiler and not the last atom + if ( (fOwner.fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS) == 0) { + if ( fOwner.findAtomIndex(*this) < (fOwner.fAtoms.size()-1) ) + return true; + } + return false; +} + +ObjectFile::Atom& Atom::getFollowOnAtom() const +{ + uint32_t myIndex = fOwner.findAtomIndex(*this); + return *fOwner.fAtoms[myIndex+1]; +} + +std::vector* Atom::getStabsDebugInfo() const +{ + if ( fStabsCount == 0 ) + return NULL; + + std::vector* stabs = new std::vector(); + stabs->reserve(fStabsCount); + + for (uint32_t i=0; i < fStabsCount; ++i) { + const macho_nlist* sym = &fOwner.fSymbols[fStabsStartIndex+i]; + if ( (sym->n_type() & N_STAB) != 0 ) { + ObjectFile::StabsInfo stab; + stab.atomOffset = sym->n_value(); + stab.string = &fOwner.fStrings[sym->n_strx()]; + stab.type = sym->n_type(); + stab.other = sym->n_sect(); + stab.desc = sym->n_desc(); + switch ( stab.type ) { + case N_FUN: + if ( stab.other == 0 ) + break; + // end of function N_FUN has size (not address) so should not be adjusted + // fall through + case N_BNSYM: + case N_ENSYM: + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + case N_STSYM: + case N_LCSYM: + // all these stab types need their value changed from an absolute address to the atom offset + stab.atomOffset -= fOffset; + break; + } + stabs->push_back(stab); + } + } + + return stabs; +} + +uint8_t Atom::getAlignment() const +{ + // mach-o file format has no alignment information for atoms - just whole sections + if ( fSection != NULL ) { + if ( isTentativeDefinition() ) { + // common symbols align to their size + // that is, a 4-byte common aligns to 4-bytes + // to be safe, odd size commons align to the next power-of-2 size + uint8_t alignment = (uint8_t)ceil(log2(this->getSize())); + // limit alignment of extremely large commons to 2^15 bytes (8-page) + if ( alignment < 15 ) + return alignment; + else + return 15; + } + else { + // so we assume every atom requires the same alignment as the whole section + return fSection->align(); + } + } + else { + return 2; + } +} + +void Atom::copyRawContent(uint8_t buffer[]) const +{ + // copy base bytes + if ( isZeroFill() ) + bzero(buffer, fSize); + else { + uint32_t fileOffset = fSection->offset() - fSection->addr() + fOffset; + memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize); + } +} + +void Atom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + const uint32_t referencesCount = fReferences.size(); + + // skip copy if no fix-ups + if ( referencesCount == 0 ) { + uint32_t fileOffset = fSection->offset() - fSection->addr() + fOffset; + writer.write(0, (char*)(fOwner.fHeader)+fileOffset, fSize); + return; + } + + // copy base bytes + uint8_t buffer[this->getSize()]; + this->copyRawContent(buffer); + + // apply any fix-ups + for (uint32_t i=0; i < referencesCount; ++i) { + Reference* ref = fReferences[i]; + uint32_t offset = ref->getFixUpOffset(); + uint32_t* instructionPtr = (uint32_t*)&buffer[offset]; + ObjectFile::Atom& target = ref->getTarget(); + if ( &target == NULL ) { + if ( finalLinkedImage ) + throw "target not found"; + else + continue; + } + uint32_t instruction; + uint32_t newInstruction; + switch ( ref->getKind() ) { + case Reference::noFixUp: + break; + case Reference::pointer: + { + //fprintf(stderr, "writeContent: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); + if ( target.isImportProxy() ) { + if ( ref->isLazyReference() && finalLinkedImage ) { + // lazy-symbol ==> pointer contains address of dyld_stub_binding_helper (stored in "from" target) + *((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(ref->getFromTarget().getAddress()); + } + else { + // external realocation ==> pointer contains addend + *((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(ref->getTargetOffset()); + } + } + else { + // internal relocation + if ( finalLinkedImage || (strcmp(target.getSectionName(), "__common") != 0) ) { + // pointer contains target address + //printf("Atom::writeContent() target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); + *((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(target.getAddress() + ref->getTargetOffset()); + } + else { + // pointer contains addend + *((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(ref->getTargetOffset()); + } + } + } + break; + case Reference::ppcFixupBranch24: + { + //fprintf(stderr, "bl fixup to %s at 0x%08llX, ", target.getDisplayName(), target.getAddress()); + int64_t displacement = (target.getAddress() + ref->getTargetOffset() ) - (this->getAddress() + offset); + if ( !finalLinkedImage && target.isImportProxy() ) { + // doing "ld -r" to an external symbol + // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target + displacement -= target.getAddress(); + } + else { + const int64_t bl_eightMegLimit = 0x00FFFFFF; + if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) { + //fprintf(stderr, "bl out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throw "bl out of range"; + } + } + instruction = OSReadBigInt32(instructionPtr, 0); + newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); + //fprintf(stderr, "bl fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction); + OSWriteBigInt32(instructionPtr, 0, newInstruction); + } + break; + case Reference::ppcFixupBranch14: + break; + case Reference::ppcFixupPicBaseLow16: + { + uint64_t targetAddr = target.getAddress() + ref->getTargetOffset(); + uint64_t picBaseAddr = this->getAddress() + ref->getFromTargetOffset(); + int64_t displacement = targetAddr - picBaseAddr; + const int64_t picbase_twoGigLimit = 0x80000000; + if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) + throw "32-bit pic-base out of range"; + uint16_t instructionLowHalf = (displacement & 0xFFFF); + instruction = OSReadBigInt32(instructionPtr, 0); + newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; + OSWriteBigInt32(instructionPtr, 0, newInstruction); + } + break; + case Reference::ppcFixupPicBaseLow14: + { + uint64_t targetAddr = target.getAddress() + ref->getTargetOffset(); + uint64_t picBaseAddr = this->getAddress() + ref->getFromTargetOffset(); + int64_t displacement = targetAddr - picBaseAddr; + const int64_t picbase_twoGigLimit = 0x80000000; + if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) + throw "32-bit pic-base out of range"; + uint16_t instructionLowHalf = (displacement & 0xFFFF); + if ( (instructionLowHalf & 0x3) != 0 ) + throw "bad address for lo14 instruction fix-up"; + instruction = OSReadBigInt32(instructionPtr, 0); + newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; + OSWriteBigInt32(instructionPtr, 0, newInstruction); + } + break; + case Reference::ppcFixupPicBaseHigh16: + { + uint64_t targetAddr = target.getAddress() + ref->getTargetOffset(); + uint64_t picBaseAddr = this->getAddress() + ref->getFromTargetOffset(); + int64_t displacement = targetAddr - picBaseAddr; + const int64_t picbase_twoGigLimit = 0x80000000; + if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) + throw "32-bit pic-base out of range"; + uint16_t instructionLowHalf = displacement >> 16; + if ( (displacement & 0x00008000) != 0 ) + ++instructionLowHalf; + instruction = OSReadBigInt32(instructionPtr, 0); + newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; + OSWriteBigInt32(instructionPtr, 0, newInstruction); + } + break; + case Reference::ppcFixupAbsLow16: + { + int64_t addr = target.getAddress() + ref->getTargetOffset(); + if ( !finalLinkedImage && target.isImportProxy() ) + addr -= target.getAddress() ; + uint16_t instructionLowHalf = (addr & 0xFFFF); + instruction = OSReadBigInt32(instructionPtr, 0); + newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; + OSWriteBigInt32(instructionPtr, 0, newInstruction); + } + break; + case Reference::ppcFixupAbsLow14: + { + int64_t addr = target.getAddress() + ref->getTargetOffset(); + if ( !finalLinkedImage && target.isImportProxy() ) + addr -= target.getAddress() ; + uint16_t instructionLowHalf = (addr & 0xFFFF); + if ( (instructionLowHalf & 0x3) != 0 ) + throw "bad address for lo14 instruction fix-up"; + instruction = OSReadBigInt32(instructionPtr, 0); + newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; + OSWriteBigInt32(instructionPtr, 0, newInstruction); + } + break; + case Reference::ppcFixupAbsHigh16: + { + int64_t addr = target.getAddress() + ref->getTargetOffset(); + if ( !finalLinkedImage && target.isImportProxy() ) + addr -= target.getAddress() ; + uint16_t hi16 = (addr >> 16); + instruction = OSReadBigInt32(instructionPtr, 0); + newInstruction = (instruction & 0xFFFF0000) | hi16; + OSWriteBigInt32(instructionPtr, 0, newInstruction); + } + break; + case Reference::ppcFixupAbsHigh16AddLow: + { + int64_t addr = target.getAddress() + ref->getTargetOffset(); + if ( !finalLinkedImage && target.isImportProxy() ) + addr -= target.getAddress() ; + if ( addr & 0x00008000 ) + addr += 0x00010000; + instruction = OSReadBigInt32(instructionPtr, 0); + newInstruction = (instruction & 0xFFFF0000) | (addr >> 16); + OSWriteBigInt32(instructionPtr, 0, newInstruction); + } + break; + case Reference::pointer32Difference: + ENDIAN_WRITE32(*instructionPtr, target.getAddress() + ref->getTargetOffset() - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset())); + break; + case Reference::pointer64Difference: + *((uint64_t*)instructionPtr) = ENDIAN_SWAP64(target.getAddress() + ref->getTargetOffset() - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset())); + break; + case Reference::x86FixupBranch32: + { + int64_t displacement = target.getAddress() - (this->getAddress() + offset); + if ( target.isImportProxy() ) { + displacement = 0; + } + else { + const int64_t bl_twoGigLimit = 0x7FFFFFFF; + if ( (displacement > bl_twoGigLimit) || (displacement < (-bl_twoGigLimit)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throw "call out of range"; + } + } + OSWriteLittleInt32(instructionPtr, 0, (int32_t)displacement); + } + break; + } + } + + // write out + writer.write(0, buffer, getSize()); +} + + + +const macho_section* Atom::findSectionFromOffset(macho_uintptr_t offset) +{ + const macho_section* const sectionsStart = (const macho_section*)( (char*)fOwner.fSegment + sizeof(macho_segment_command) ); + const macho_section* const sectionsEnd = §ionsStart[fOwner.fSegment->nsects()]; + for (const macho_section* s = sectionsStart; s < sectionsEnd; ++s) { + if ( (s->addr() <= offset) && (offset < (s->addr()+s->size())) ) + return s; + } + throw "section not found"; +} + +void Atom::setSize(macho_uintptr_t size) +{ + fSize = size; +} + + +void Atom::setFollowOnAtom(Atom&) +{ + +} + +Reference* Atom::addReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget) +{ + if ( (target.getScope() != ObjectFile::Atom::scopeTranslationUnit) && ((target.fSymbol != NULL) || (target.fSynthesizedName != NULL)) ) + return this->addByNameReference(offsetInSrcAtom, kind, target.getName(), offsetInTarget, offsetInFromTarget); + else + return this->addDirectReference(offsetInSrcAtom, kind, target, offsetInTarget, offsetInFromTarget); +} + + +Reference* Atom::addDirectReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget) +{ + Reference* ref = new Reference(offsetInSrcAtom, kind, target, offsetInTarget, offsetInFromTarget); + // in rare cases, there may already be a by-name reference to the same atom. If so, replace with this direct reference + for (std::vector::iterator it=fReferences.begin(); it != fReferences.end(); it++) { + ObjectFile::Reference* aRef = *it; + if ( (aRef->getFixUpOffset() == offsetInSrcAtom) && (aRef->getKind() == kind) ) { + *it = ref; + return ref; + } + } + + // note: adding to start of list because mach-o relocs are in reverse offset order in the .o file + fReferences.insert(fReferences.begin(), ref); + return ref; +} + +Reference* Atom::addByNameReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, const char* targetName, uint64_t offsetInTarget, uint64_t offsetInFromTarget) +{ + Reference* ref = new Reference(offsetInSrcAtom, kind, targetName, offsetInTarget, offsetInFromTarget); + // note: adding to start of list because mach-o relocs are in reverse offset order in the .o file + fReferences.insert(fReferences.begin(), ref); + return ref; +} + +Reference* Atom::addDifferenceReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, Atom& fromTarget, uint64_t offsetInFromTarget) +{ + Reference* ref = new Reference(offsetInSrcAtom, kind, target, offsetInTarget, fromTarget, offsetInFromTarget); + // note: adding to start of list because mach-o relocs are in reverse offset order in the .o file + fReferences.insert(fReferences.begin(), ref); + return ref; +} + + +Segment::Segment(const macho_section* sect) + : fSection(sect) +{ +} + +const char* Segment::getName() const +{ + return fSection->segname(); +} + +bool Segment::isContentReadable() const +{ + return true; +} + +bool Segment::isContentWritable() const +{ + if ( strcmp(fSection->segname(), "__DATA") == 0 ) + return true; + if ( strcmp(fSection->segname(), "__OBJC") == 0 ) + return true; + return false; +} + +bool Segment::isContentExecutable() const +{ + return ( strcmp(fSection->segname(), "__TEXT") == 0 ); +} + + +Reference::Reference(macho_uintptr_t fixUpOffset, Kind kind, const char* targetName, uint64_t offsetInTarget, uint64_t offsetInFromTarget) + : fTarget(NULL), fFromTarget(NULL), fTargetName(targetName), fFromTargetName(NULL), fTargetOffset(offsetInTarget), fFromTargetOffset(offsetInFromTarget), + fFixUpOffsetInSrc(fixUpOffset), fKind(kind), fLazy(false), fWeak(false) +{ +} + +Reference::Reference(macho_uintptr_t fixUpOffset, Kind kind, class Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget) + : fTarget(&target), fFromTarget(NULL), fTargetName(NULL), fFromTargetName(NULL), fTargetOffset(offsetInTarget), fFromTargetOffset(offsetInFromTarget), + fFixUpOffsetInSrc(fixUpOffset), fKind(kind), fLazy(false), fWeak(false) +{ +} + +Reference::Reference(macho_uintptr_t fixUpOffset, Kind kind, class Atom& target, uint64_t offsetInTarget, class Atom& fromTarget, uint64_t offsetInFromTarget) + : fTarget(&target), fFromTarget(&fromTarget), fTargetName(NULL), fFromTargetName(NULL), fTargetOffset(offsetInTarget), fFromTargetOffset(offsetInFromTarget), + fFixUpOffsetInSrc(fixUpOffset), fKind(kind), fLazy(false), fWeak(false) +{ + // assure no direct references to something that might be coalesced + if ( (target.isWeakDefinition() || target.isCoalesableByName()) && (target.getScope() != ObjectFile::Atom::scopeTranslationUnit) && (target.getName() != NULL) ) { + //fprintf(stderr, "change TO direct reference to by-name: from %s to %s in %p\n", fromTarget.getDisplayName(), target.getName(), this); + fTargetName = target.getName(); + fTarget = NULL; + } +// Note: We should also allow by-name from references, but many other chunks of code assume from targets are always direct// +// if ( (fromTarget.isWeakDefinition() || fromTarget.isCoalesableByName()) && (fromTarget.getScope() != ObjectFile::Atom::scopeTranslationUnit) && (fromTarget.getName() != NULL)) { +// fprintf(stderr, "change FROM direct reference to by-name: from %s to %s in %p\n", fromTarget.getDisplayName(), target.getName(), this); +// fFromTargetName = fromTarget.getName(); +// fFromTarget = NULL; +// } +} + + + +Reference::~Reference() +{ +} + +bool Reference::isUnbound() const +{ + if ( fTarget == NULL ) + return true; + if ( (fFromTargetName!=NULL) && (fFromTarget==NULL) ) + return true; + return false; +} + +bool Reference::isWeakReference() const +{ + return fWeak; +} + +bool Reference::requiresRuntimeFixUp() const +{ + return ( fKind == Reference::pointer ); +} + +bool Reference::isLazyReference() const +{ + return fLazy; +} + +ObjectFile::Reference::Kind Reference::getKind() const +{ + return fKind; +} + +uint64_t Reference::getFixUpOffset() const +{ + return fFixUpOffsetInSrc; +} + +const char* Reference::getTargetName() const +{ + if ( fTargetName != NULL ) + return fTargetName; + return fTarget->getName(); +} + +ObjectFile::Atom& Reference::getTarget() const +{ + return *fTarget; +} + +void Reference::setTarget(ObjectFile::Atom& target) +{ + fTarget = ⌖ +} + + +ObjectFile::Atom& Reference::getFromTarget() const +{ + return *fFromTarget; +} + +const char* Reference::getFromTargetName() const +{ + if ( fFromTargetName != NULL ) + return fFromTargetName; + return fFromTarget->getName(); +} + +void Reference::setFromTarget(ObjectFile::Atom& target) +{ + fFromTarget = ⌖ +} + +void Reference::setFromTargetName(const char* name) +{ + fFromTargetName = name; +} + +uint64_t Reference::getTargetOffset() const +{ + return fTargetOffset; +} + + +uint64_t Reference::getFromTargetOffset() const +{ + return fFromTargetOffset; +} + +void Reference::setLazy(bool lazy) +{ + fLazy = lazy; +} + +void Reference::setWeak(bool weak) +{ + fWeak = weak; +} + +const char* Reference::getDescription() const +{ + static char temp[256]; + if ( fKind == pointer32Difference ) { + // by-name references have quoted names + bool targetByName = ( &(this->getTarget()) == NULL ); + bool fromByName = ( &(this->getFromTarget()) == NULL ); + const char* targetQuotes = targetByName ? "\"" : ""; + const char* fromQuotes = fromByName ? "\"" : ""; + sprintf(temp, "offset 0x%04llX, 32-bit pointer difference: (&%s%s%s + %lld) - (&%s%s%s + %lld)", + this->getFixUpOffset(), targetQuotes, this->getTargetName(), targetQuotes, this->getTargetOffset(), + fromQuotes, this->getFromTargetName(), fromQuotes, this->getFromTargetOffset() ); + } + else if ( fKind == pointer64Difference ) { + // by-name references have quoted names + bool targetByName = ( &(this->getTarget()) == NULL ); + bool fromByName = ( &(this->getFromTarget()) == NULL ); + const char* targetQuotes = targetByName ? "\"" : ""; + const char* fromQuotes = fromByName ? "\"" : ""; + sprintf(temp, "offset 0x%04llX, 64-bit pointer difference: (&%s%s%s + %lld) - (&%s%s%s + %lld)", + this->getFixUpOffset(), targetQuotes, this->getTargetName(), targetQuotes, this->getTargetOffset(), + fromQuotes, this->getFromTargetName(), fromQuotes, this->getFromTargetOffset() ); + } + else { + switch( fKind ) { + case noFixUp: + sprintf(temp, "reference to "); + break; + case pointer: + { + const char* weak = ""; + if ( fWeak ) + weak = "weak "; + const char* lazy = ""; + if ( fLazy ) + lazy = "lazy "; + sprintf(temp, "offset 0x%04llX, %s%spointer to ", this->getFixUpOffset(), weak, lazy); + } + break; + case ppcFixupBranch14: + case ppcFixupBranch24: + sprintf(temp, "offset 0x%04llX, bl pc-rel fixup to ", this->getFixUpOffset()); + break; + case ppcFixupPicBaseLow16: + sprintf(temp, "offset 0x%04llX, low 16 fixup from pic-base offset 0x%04llX to ", this->getFixUpOffset(), this->getFromTargetOffset()); + break; + case ppcFixupPicBaseLow14: + sprintf(temp, "offset 0x%04llX, low 14 fixup from pic-base offset 0x%04llX to ", this->getFixUpOffset(), this->getFromTargetOffset()); + break; + case ppcFixupPicBaseHigh16: + sprintf(temp, "offset 0x%04llX, high 16 fixup from pic-base offset 0x%04llX to ", this->getFixUpOffset(), this->getFromTargetOffset()); + break; + case ppcFixupAbsLow16: + sprintf(temp, "offset 0x%04llX, low 16 fixup to absolute address of ", this->getFixUpOffset()); + break; + case ppcFixupAbsLow14: + sprintf(temp, "offset 0x%04llX, low 14 fixup to absolute address of ", this->getFixUpOffset()); + break; + case ppcFixupAbsHigh16: + sprintf(temp, "offset 0x%04llX, high 16 fixup to absolute address of ", this->getFixUpOffset()); + break; + case ppcFixupAbsHigh16AddLow: + sprintf(temp, "offset 0x%04llX, high 16 fixup to absolute address of ", this->getFixUpOffset()); + break; + case pointer32Difference: + case pointer64Difference: + // handled above + break; + case x86FixupBranch32: + sprintf(temp, "offset 0x%04llX, call pc-rel fixup to ", this->getFixUpOffset()); + break; + } + // always quote by-name references + if ( fTargetName != NULL ) { + strcat(temp, "\""); + strcat(temp, fTargetName); + strcat(temp, "\""); + } + else if ( fTarget != NULL ) { + strcat(temp, fTarget->getDisplayName()); + } + else { + strcat(temp, "NULL target"); + } + if ( fTargetOffset != 0 ) + sprintf(&temp[strlen(temp)], " plus 0x%08llX", this->getTargetOffset()); + if ( (fKind==pointer) && fLazy ) { + strcat(temp, " initially bound to \""); + strcat(temp, this->getFromTargetName()); + strcat(temp, "\""); + } + } + return temp; +} + +}; + + + + + + + diff --git a/src/SectCreate.cpp b/src/SectCreate.cpp new file mode 100644 index 0000000..4d48a37 --- /dev/null +++ b/src/SectCreate.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +#include "ObjectFile.h" + +namespace SectCreate { + + +class Segment : public ObjectFile::Segment +{ +public: + Segment(const char* name) { fName = name; } + virtual const char* getName() const { return fName; } + virtual bool isContentReadable() const { return true; } + virtual bool isContentWritable() const { return false; } + virtual bool isContentExecutable() const { return false; } +private: + const char* fName; +}; + + +class Reader : public ObjectFile::Reader +{ +public: + Reader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], uint64_t fileLength); + virtual ~Reader(); + + virtual const char* getPath() { return fPath; } + virtual std::vector& getAtoms() { return fAtoms; } + virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } + virtual std::vector* getStabsDebugInfo() { return NULL; } + +private: + const char* fPath; + std::vector fAtoms; +}; + + +class Atom : public ObjectFile::Atom { +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual const char* getName() const { return NULL; } + virtual const char* getDisplayName() const; + virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } + virtual bool isTentativeDefinition() const { return false; } + virtual bool isWeakDefinition() const { return false; } + virtual bool isCoalesableByName() const { return false; } + virtual bool isCoalesableByValue() const { return false; } + virtual bool isZeroFill() const { return false; } + virtual bool dontDeadStrip() const { return true; } + virtual bool dontStripName() const { return false; } + virtual bool isImportProxy() const { return false; } + virtual uint64_t getSize() const { return fFileLength; } + virtual std::vector& getReferences() const { return fgEmptyReferenceList; } + virtual bool mustRemainInSection() const { return false; } + virtual const char* getSectionName() const { return fSectionName; } + virtual Segment& getSegment() const { return fSegment; } + virtual bool requiresFollowOnAtom() const{ return false; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } + virtual std::vector* getStabsDebugInfo() const { return NULL; } + virtual uint8_t getAlignment() const { return 4; } + virtual WeakImportSetting getImportWeakness() const { return ObjectFile::Atom::kWeakUnset; } + virtual void copyRawContent(uint8_t buffer[]) const; + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; + + virtual void setScope(Scope) { } + virtual void setImportWeakness(bool) { } + +protected: + friend class Reader; + + Atom(Reader& owner, Segment& segment, const char* sectionName, const uint8_t fileContent[], uint64_t fileLength) + : fOwner(owner), fSegment(segment), fSectionName(sectionName), fFileContent(fileContent), fFileLength(fileLength) {} + virtual ~Atom() {} + + Reader& fOwner; + Segment& fSegment; + const char* fSectionName; + const uint8_t* fFileContent; + uint64_t fFileLength; + + static std::vector fgEmptyReferenceList; +}; + + +std::vector Atom::fgEmptyReferenceList; + + + +Reader::Reader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], uint64_t fileLength) + : fPath(path) +{ + fAtoms.push_back(new Atom(*this, *(new Segment(segmentName)), sectionName, fileContent, fileLength)); +} + +Reader::~Reader() +{ +} + + +const char* Atom::getDisplayName() const +{ + static char name[64]; + sprintf(name, "-sectcreate %s %s", fSegment.getName(), fSectionName); + return name; +} + + +void Atom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, fFileContent, fFileLength); +} + +void Atom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + writer.write(0, fFileContent, fFileLength); +} + + +Reader* MakeReader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], uint64_t fileLength) +{ + return new Reader(segmentName, sectionName, path, fileContent, fileLength); +} + + + +}; + + + + + + + diff --git a/src/SectCreate.h b/src/SectCreate.h new file mode 100644 index 0000000..73b49a5 --- /dev/null +++ b/src/SectCreate.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __SECTCREATE__ +#define __SECTCREATE__ + + +#include "ObjectFile.h" + +namespace SectCreate { + + extern ObjectFile::Reader* MakeReader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], uint64_t fileLength); + +}; + + +#endif + + + + diff --git a/src/Writers/ExecutableFileMachO-all.cpp b/src/Writers/ExecutableFileMachO-all.cpp new file mode 100644 index 0000000..661a3df --- /dev/null +++ b/src/Writers/ExecutableFileMachO-all.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ObjectFile.h" +#include "ExecutableFile.h" +#include "Options.h" + +// buiild Writer for -arch ppc64 +#undef MACHO_32_SAME_ENDIAN +#undef MACHO_32_OPPOSITE_ENDIAN +#undef MACHO_64_SAME_ENDIAN +#undef MACHO_64_OPPOSITE_ENDIAN +#if __ppc__ || __ppc64__ + #define MACHO_64_SAME_ENDIAN +#elif __i386__ + #define MACHO_64_OPPOSITE_ENDIAN +#else + #error unknown architecture +#endif +namespace ppc64 { + #undef ARCH_PPC + #define ARCH_PPC64 + #undef ARCH_I386 + #include "MachOAbstraction.h" + #include "ExecutableFileMachO.cpp" +}; + +// buiild Writer for -arch ppc +#undef MACHO_32_SAME_ENDIAN +#undef MACHO_32_OPPOSITE_ENDIAN +#undef MACHO_64_SAME_ENDIAN +#undef MACHO_64_OPPOSITE_ENDIAN +#if __ppc__ || __ppc64__ + #define MACHO_32_SAME_ENDIAN +#elif __i386__ + #define MACHO_32_OPPOSITE_ENDIAN +#else + #error unknown architecture +#endif +namespace ppc { + #define ARCH_PPC + #undef ARCH_PPC64 + #undef ARCH_I386 + #include "MachOAbstraction.h" + #include "ExecutableFileMachO.cpp" +}; + +// buiild Writer for -arch i386 +#undef MACHO_32_SAME_ENDIAN +#undef MACHO_32_OPPOSITE_ENDIAN +#undef MACHO_64_SAME_ENDIAN +#undef MACHO_64_OPPOSITE_ENDIAN +#if __ppc__ || __ppc64__ + #define MACHO_32_OPPOSITE_ENDIAN +#elif __i386__ + #define MACHO_32_SAME_ENDIAN +#else + #error unknown architecture +#endif +#undef i386 // compiler sometimes #defines this +namespace i386 { + #undef ARCH_PPC + #undef ARCH_PPC64 + #define ARCH_I386 + #include "MachOAbstraction.h" + #include "ExecutableFileMachO.cpp" +}; + + diff --git a/src/Writers/ExecutableFileMachO-all.h b/src/Writers/ExecutableFileMachO-all.h new file mode 100644 index 0000000..08d4ae6 --- /dev/null +++ b/src/Writers/ExecutableFileMachO-all.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __EXECUTABLEFILEMACHO__ +#define __EXECUTABLEFILEMACHO__ + +class Options; + +namespace ppc { + namespace ExecutableFileMachO { + extern class ExecutableFile::Writer* MakeWriter(const char* path, Options&, std::vector&); + } +}; + +namespace ppc64 { + namespace ExecutableFileMachO { + extern class ExecutableFile::Writer* MakeWriter(const char* path, Options&, std::vector&); + } +}; + +#undef i386 // compiler sometimes #defines this +namespace i386 { + namespace ExecutableFileMachO { + extern class ExecutableFile::Writer* MakeWriter(const char* path, Options&, std::vector&); + } +}; + + + +#endif // __EXECUTABLEFILEMACHO__ + + + diff --git a/src/Writers/ExecutableFileMachO.cpp b/src/Writers/ExecutableFileMachO.cpp new file mode 100644 index 0000000..5020cfe --- /dev/null +++ b/src/Writers/ExecutableFileMachO.cpp @@ -0,0 +1,2554 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + +namespace ExecutableFileMachO { + +class Writer : public ExecutableFile::Writer +{ +public: + Writer(const char* path, Options& options, std::vector& dynamicLibraries); + virtual ~Writer(); + + virtual const char* getPath(); + virtual std::vector& getAtoms(); + virtual std::vector* getJustInTimeAtomsFor(const char* name); + virtual std::vector* getStabsDebugInfo(); + + virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name); + virtual void write(std::vector& atoms, class ObjectFile::Atom* entryPointAtom); + +private: + void assignFileOffsets(); + void partitionIntoSections(); + void adjustLoadCommandsAndPadding(); + void createDynamicLinkerCommand(); + void createDylibCommands(); + void buildLinkEdit(); + void writeAtoms(); + void collectExportedAndImportedAndLocalAtoms(); + void setNlistRange(std::vector& atoms, uint32_t startIndex, uint32_t count); + void buildSymbolTable(); + void setExportNlist(const ObjectFile::Atom* atom, macho_nlist* entry); + void setImportNlist(const ObjectFile::Atom* atom, macho_nlist* entry); + void setLocalNlist(const ObjectFile::Atom* atom, macho_nlist* entry); + uint64_t getAtomLoadAddress(const ObjectFile::Atom* atom); + uint8_t ordinalForLibrary(ObjectFile::Reader* file); + bool shouldExport(ObjectFile::Atom& atom); + void buildFixups(); + void adjustLinkEditSections(); + void buildObjectFileFixups(); + void buildExecutableFixups(); + uint32_t symbolIndex(ObjectFile::Atom& atom); + uint32_t addRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref); + unsigned int collectStabs(); + macho_uintptr_t valueForStab(const ObjectFile::StabsInfo& stab, const ObjectFile::Atom* atom); + void addStabs(uint32_t startIndex, uint32_t count); + + + class SectionInfo : public ObjectFile::Section { + public: + SectionInfo(); + void setIndex(unsigned int index) { fIndex=index; } + std::vector fAtoms; + char fSegmentName[20]; + char fSectionName[20]; + uint64_t fFileOffset; + uint64_t fSize; + uint32_t fRelocCount; + uint32_t fRelocOffset; + uint32_t fIndirectSymbolOffset; + uint8_t fAlignment; + bool fAllLazyPointers; + bool fAllNonLazyPointers; + bool fAllZeroFill; + bool fVirtualSection; + }; + + class SegmentInfo + { + public: + SegmentInfo(); + std::vector fSections; + char fName[20]; + uint32_t fInitProtection; + uint32_t fMaxProtection; + uint64_t fFileOffset; + uint64_t fFileSize; + uint64_t fBaseAddress; + uint64_t fSize; + }; + + + struct DirectLibrary { + class ObjectFile::Reader* fLibrary; + bool fWeak; + bool fReExport; + }; + + struct IndirectEntry { + uint32_t indirectIndex; + uint32_t symbolIndex; + }; + + struct StabChunks { + ObjectFile::Atom* fAtom; + ObjectFile::Reader* fReader; + unsigned int fOrderInReader; + std::vector* fStabs; + }; + + static bool stabChunkCompare(const StabChunks& lhs, const StabChunks& rhs); + + friend class WriterAtom; + friend class PageZeroAtom; + friend class CustomStackAtom; + friend class MachHeaderAtom; + friend class SegmentLoadCommandsAtom; + friend class SymbolTableLoadCommandsAtom; + friend class ThreadsLoadCommandsAtom; + friend class DylibIDLoadCommandsAtom; + friend class RoutinesLoadCommandsAtom; + friend class DyldLoadCommandsAtom; + friend class LinkEditAtom; + friend class LocalRelocationsLinkEditAtom; + friend class ExternalRelocationsLinkEditAtom; + friend class SymbolTableLinkEditAtom; + friend class IndirectTableLinkEditAtom; + friend class StringsLinkEditAtom; + + const char* fFilePath; + Options& fOptions; + int fFileDescriptor; + std::vector* fAllAtoms; + class SectionInfo* fLoadCommandsSection; + class SegmentInfo* fLoadCommandsSegment; + class SegmentLoadCommandsAtom* fSegmentCommands; + class SymbolTableLoadCommandsAtom* fSymbolTableCommands; + class LoadCommandsPaddingAtom* fHeaderPadding; + std::vector fWriterSynthesizedAtoms; + std::vector fSegmentInfos; + class ObjectFile::Atom* fEntryPoint; + std::vector fDirectLibraries; + std::map fLibraryToOrdinal; + std::vector fStabChunks; + std::vector fExportedAtoms; + std::vector fImportedAtoms; + std::vector fLocalSymbolAtoms; + LocalRelocationsLinkEditAtom* fLocalRelocationsAtom; + ExternalRelocationsLinkEditAtom* fExternalRelocationsAtom; + SymbolTableLinkEditAtom* fSymbolTableAtom; + IndirectTableLinkEditAtom* fIndirectTableAtom; + StringsLinkEditAtom* fStringsAtom; + macho_nlist* fSymbolTable; + //char* fStringPool; + //uint32_t fStringPoolUsed; + //uint32_t fStringPoolSize; + std::vector fInternalRelocs; + std::vector fExternalRelocs; + std::vector fIndirectSymbolTable; + uint32_t fSymbolTableCount; + uint32_t fSymbolTableStabsCount; + uint32_t fSymbolTableStabsStartIndex; + uint32_t fSymbolTableLocalCount; + uint32_t fSymbolTableLocalStartIndex; + uint32_t fSymbolTableExportCount; + uint32_t fSymbolTableExportStartIndex; + uint32_t fSymbolTableImportCount; + uint32_t fSymbolTableImportStartIndex; + bool fEmitVirtualSections; + bool fHasWeakExports; + bool fReferencesWeakImports; +}; + + +class WriterAtom : public ObjectFile::Atom +{ +protected: + class Segment; +public: + enum Kind { zeropage, machHeaderApp, machHeaderDylib, machHeaderBundle, machHeaderObject, loadCommands, undefinedProxy }; + WriterAtom(Writer& writer, class WriterAtom::Segment& segment) : fWriter(writer), fSegment(segment) {} + + virtual ObjectFile::Reader* getFile() const { return &fWriter; } + virtual const char* getName() const { return NULL; } + virtual const char* getDisplayName() const { return this->getName(); } + virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } + virtual bool isTentativeDefinition() const { return false; } + virtual bool isWeakDefinition() const { return false; } + virtual bool isCoalesableByName() const { return false; } + virtual bool isCoalesableByValue() const { return false; } + virtual bool isZeroFill() const { return false; } + virtual bool dontDeadStrip() const { return true; } + virtual bool dontStripName() const { return false; } + virtual bool isImportProxy() const { return false; } + virtual std::vector& getReferences() const { return fgEmptyReferenceList; } + virtual bool mustRemainInSection() const { return true; } + virtual ObjectFile::Segment& getSegment() const { return fSegment; } + virtual bool requiresFollowOnAtom() const { return false; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } + virtual std::vector* getStabsDebugInfo() const { return NULL; } + virtual uint8_t getAlignment() const { return 2; } + virtual WeakImportSetting getImportWeakness() const { return Atom::kWeakUnset; } + virtual void copyRawContent(uint8_t buffer[]) const { throw "don't use copyRawContent"; } + virtual void setScope(Scope) { } + virtual void setImportWeakness(bool weakImport) { } + + +protected: + virtual ~WriterAtom() {} + + class Segment : public ObjectFile::Segment + { + public: + Segment(const char* name, bool readable, bool writable, bool executable) + : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable) {} + virtual const char* getName() const { return fName; } + virtual bool isContentReadable() const { return fReadable; } + virtual bool isContentWritable() const { return fWritable; } + virtual bool isContentExecutable() const { return fExecutable; } + private: + const char* fName; + const bool fReadable; + const bool fWritable; + const bool fExecutable; + }; + + static std::vector fgEmptyReferenceList; + static Segment fgTextSegment; + static Segment fgPageZeroSegment; + static Segment fgLinkEditSegment; + static Segment fgStackSegment; + + + Writer& fWriter; + Segment& fSegment; +}; + + +WriterAtom::Segment WriterAtom::fgPageZeroSegment("__PAGEZERO", false, false, false); +WriterAtom::Segment WriterAtom::fgTextSegment("__TEXT", true, false, true); +WriterAtom::Segment WriterAtom::fgLinkEditSegment("__LINKEDIT", true, false, false); +WriterAtom::Segment WriterAtom::fgStackSegment("__UNIXSTACK", true, true, false); +std::vector WriterAtom::fgEmptyReferenceList; + +class PageZeroAtom : public WriterAtom +{ +public: + PageZeroAtom(Writer& writer) : WriterAtom(writer, fgPageZeroSegment) {} + virtual const char* getDisplayName() const { return "page zero content"; } + virtual bool isZeroFill() const { return true; } + virtual uint64_t getSize() const { return fWriter.fOptions.zeroPageSize(); } + virtual const char* getSectionName() const { return "._zeropage"; } + virtual uint8_t getAlignment() const { return 12; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const {} +}; + +class MachHeaderAtom : public WriterAtom +{ +public: + MachHeaderAtom(Writer& writer) : WriterAtom(writer, fgTextSegment) {} + virtual const char* getName() const; + virtual const char* getDisplayName() const; + virtual Scope getScope() const; + virtual bool dontStripName() const; + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 12; } + virtual const char* getSectionName() const { return "._mach_header"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; +}; + +class CustomStackAtom : public WriterAtom +{ +public: + CustomStackAtom(Writer& writer); + virtual const char* getDisplayName() const { return "custom stack content"; } + virtual bool isZeroFill() const { return true; } + virtual uint64_t getSize() const { return fWriter.fOptions.customStackSize(); } + virtual const char* getSectionName() const { return "._stack"; } + virtual uint8_t getAlignment() const { return 12; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const {} +}; + +class SegmentLoadCommandsAtom : public WriterAtom +{ +public: + SegmentLoadCommandsAtom(Writer& writer) : WriterAtom(writer, fgTextSegment), fCommandCount(0), fSize(0) { writer.fSegmentCommands = this; } + virtual const char* getDisplayName() const { return "segment load commands"; } + virtual uint64_t getSize() const { return fSize; } + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; + + void computeSize(); + void setup(); + unsigned int commandCount() { return fCommandCount; } + void assignFileOffsets(); +private: + unsigned int fCommandCount; + uint32_t fSize; +}; + +class SymbolTableLoadCommandsAtom : public WriterAtom +{ +public: + SymbolTableLoadCommandsAtom(Writer&); + virtual const char* getDisplayName() const { return "symbol table load commands"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; + +private: + macho_symtab_command fSymbolTable; + macho_dysymtab_command fDynamicSymbolTable; +}; + +class ThreadsLoadCommandsAtom : public WriterAtom +{ +public: + ThreadsLoadCommandsAtom(Writer& writer) : WriterAtom(writer, fgTextSegment) {} + virtual const char* getDisplayName() const { return "thread load commands"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; +private: + uint8_t* fBuffer; + uint32_t fBufferSize; +}; + +class DyldLoadCommandsAtom : public WriterAtom +{ +public: + DyldLoadCommandsAtom(Writer& writer) : WriterAtom(writer, fgTextSegment) {} + virtual const char* getDisplayName() const { return "dyld load command"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; +}; + +class DylibLoadCommandsAtom : public WriterAtom +{ +public: + DylibLoadCommandsAtom(Writer& writer, ExecutableFile::DyLibUsed& info) : WriterAtom(writer, fgTextSegment), fInfo(info) {} + virtual const char* getDisplayName() const { return "dylib load command"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; +private: + ExecutableFile::DyLibUsed& fInfo; +}; + +class DylibIDLoadCommandsAtom : public WriterAtom +{ +public: + DylibIDLoadCommandsAtom(Writer& writer) : WriterAtom(writer, fgTextSegment) {} + virtual const char* getDisplayName() const { return "dylib ID load command"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; +}; + +class RoutinesLoadCommandsAtom : public WriterAtom +{ +public: + RoutinesLoadCommandsAtom(Writer& writer) : WriterAtom(writer, fgTextSegment) {} + virtual const char* getDisplayName() const { return "routines load command"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; +}; + +class SubUmbrellaLoadCommandsAtom : public WriterAtom +{ +public: + SubUmbrellaLoadCommandsAtom(Writer& writer, const char* name) : WriterAtom(writer, fgTextSegment), fName(name) {} + virtual const char* getDisplayName() const { return "sub-umbrella load command"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; +private: + const char* fName; +}; + +class SubLibraryLoadCommandsAtom : public WriterAtom +{ +public: + SubLibraryLoadCommandsAtom(Writer& writer, const char* nameStart, int nameLen) + : WriterAtom(writer, fgTextSegment), fNameStart(nameStart), fNameLength(nameLen) {} + virtual const char* getDisplayName() const { return "sub-library load command"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; +private: + const char* fNameStart; + int fNameLength; +}; + +class UmbrellaLoadCommandsAtom : public WriterAtom +{ +public: + UmbrellaLoadCommandsAtom(Writer& writer, const char* name) + : WriterAtom(writer, fgTextSegment), fName(name) {} + virtual const char* getDisplayName() const { return "umbrella load command"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; +private: + const char* fName; +}; + +class LoadCommandsPaddingAtom : public WriterAtom +{ +public: + LoadCommandsPaddingAtom(Writer& writer) + : WriterAtom(writer, fgTextSegment), fSize(0) {} + virtual const char* getDisplayName() const { return "header padding"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_cmds_pad"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; + + void setSize(uint64_t newSize) { fSize = newSize; } +private: + uint64_t fSize; +}; + +class LinkEditAtom : public WriterAtom +{ +public: + LinkEditAtom(Writer& writer) : WriterAtom(writer, fgLinkEditSegment) {} + uint64_t getFileOffset() const; +}; + +class LocalRelocationsLinkEditAtom : public LinkEditAtom +{ +public: + LocalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "local relocations"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 3; } + virtual const char* getSectionName() const { return "._local_relocs"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; +}; + +class SymbolTableLinkEditAtom : public LinkEditAtom +{ +public: + SymbolTableLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "symbol table"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._symbol_table"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; +}; + +class ExternalRelocationsLinkEditAtom : public LinkEditAtom +{ +public: + ExternalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "external relocations"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 3; } + virtual const char* getSectionName() const { return "._extern_relocs"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; +}; + +class IndirectTableLinkEditAtom : public LinkEditAtom +{ +public: + IndirectTableLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "indirect symbol table"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._indirect_syms"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; +}; + +class StringsLinkEditAtom : public LinkEditAtom +{ +public: + StringsLinkEditAtom(Writer& writer); + virtual const char* getDisplayName() const { return "string pool"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._string_pool"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; + + int32_t add(const char* name); + int32_t emptyString(); + +private: + enum { kBufferSize = 0x01000000 }; + + std::vector fFullBuffers; + char* fCurrentBuffer; + uint32_t fCurrentBufferUsed; +}; + + + +class UndefinedSymbolProxyAtom : public WriterAtom +{ +public: + UndefinedSymbolProxyAtom(Writer& writer, const char* name) : WriterAtom(writer, fgLinkEditSegment), fName(name), fWeakImportSetting(Atom::kWeakUnset) {} + virtual const char* getName() const { return fName; } + virtual Scope getScope() const { return ObjectFile::Atom::scopeGlobal; } + virtual uint64_t getSize() const { return 0; } + virtual bool isWeakDefinition() const { return true; } + virtual bool isImportProxy() const { return true; } + virtual const char* getSectionName() const { return "._imports"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const {} + virtual WeakImportSetting getImportWeakness() const { return fWeakImportSetting; } + virtual void setImportWeakness(bool weakImport) { fWeakImportSetting = weakImport ? kWeakImport : kNonWeakImport; } +private: + const char* fName; + WeakImportSetting fWeakImportSetting; +}; + + + +struct ExportSorter +{ + bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right) + { + return (strcmp(left->getName(), right->getName()) < 0); + } +}; + + +ExecutableFile::Writer* MakeWriter(const char* path, Options& options, std::vector& dynamicLibraries) +{ + return new Writer(path, options, dynamicLibraries); +} + +Writer::SectionInfo::SectionInfo() + : fFileOffset(0), fSize(0), fRelocCount(0), fRelocOffset(0), fIndirectSymbolOffset(0), fAlignment(0), + fAllLazyPointers(false), fAllNonLazyPointers(false), fAllZeroFill(false), fVirtualSection(false) +{ + fSegmentName[0] = '\0'; + fSectionName[0] = '\0'; +} + +Writer::SegmentInfo::SegmentInfo() + : fInitProtection(0), fMaxProtection(0), fFileOffset(0), fFileSize(0), fBaseAddress(0), fSize(0) +{ + fName[0] = '\0'; +} + + +Writer::Writer(const char* path, Options& options, std::vector& dynamicLibraries) + : ExecutableFile::Writer(dynamicLibraries), fFilePath(strdup(path)), fOptions(options), fLoadCommandsSection(NULL), + fLoadCommandsSegment(NULL), + //fStringPool(NULL), fStringPoolUsed(0), fStringPoolSize(0), + fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(false) +{ + int permissions = 0777; + if ( fOptions.outputKind() == Options::kObjectFile ) + permissions = 0666; + // Calling unlink first assures the file is gone so that open creates it with correct permissions + // It also handles the case where fFilePath file is not writeable but its directory is + // And it means we don't have to truncate the file when done writing (in case new is smaller than old) + (void)unlink(fFilePath); + fFileDescriptor = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions); + if ( fFileDescriptor == -1 ) { + throw "can't open file for writing"; + } + + switch ( fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + fWriterSynthesizedAtoms.push_back(new PageZeroAtom(*this)); + fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); + if ( fOptions.outputKind() == Options::kDynamicExecutable ) + fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this)); + if ( fOptions.hasCustomStack() ) + fWriterSynthesizedAtoms.push_back(new CustomStackAtom(*this)); + fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: + fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + if ( fOptions.outputKind() == Options::kDynamicLibrary ) { + fWriterSynthesizedAtoms.push_back(new DylibIDLoadCommandsAtom(*this)); + if ( fOptions.initFunctionName() != NULL ) + fWriterSynthesizedAtoms.push_back(new RoutinesLoadCommandsAtom(*this)); + } + fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); + break; + case Options::kDyld: + fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); + break; + } + + // add extra commmands + uint8_t ordinal = 1; + switch ( fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + { + // add dylib load command atoms for all dynamic libraries + const unsigned int libCount = dynamicLibraries.size(); + for (unsigned int i=0; i < libCount; ++i) { + ExecutableFile::DyLibUsed& dylibInfo = dynamicLibraries[i]; + if ( dylibInfo.indirect ) { + // find ordinal of direct reader + if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) { + bool found = false; + for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { + if ( it->first == dylibInfo.directReader ) { + //fprintf(stderr, "ordinal %d for indirect %s\n", it->second, dylibInfo.reader->getPath()); + fLibraryToOrdinal[dylibInfo.reader] = it->second; + found = true; + break; + } + } + if ( ! found ) + fprintf(stderr, "ld64 warning: ordinal not found for %s, parent %s\n", dylibInfo.reader->getPath(), dylibInfo.directReader != NULL ? dylibInfo.directReader->getPath() : NULL); + } + } + else { + // see if a DylibLoadCommandsAtom has already been created for this install path + bool newDylib = true; + const char* dylibInstallPath = dylibInfo.reader->getInstallPath(); + if ( dylibInfo.options.fInstallPathOverride != NULL ) + dylibInstallPath = dylibInfo.options.fInstallPathOverride; + for (unsigned int seenLib=0; seenLib < i; ++seenLib) { + ExecutableFile::DyLibUsed& seenDylibInfo = dynamicLibraries[seenLib]; + if ( !seenDylibInfo.indirect ) { + const char* seenDylibInstallPath = seenDylibInfo.reader->getInstallPath(); + if ( seenDylibInfo.options.fInstallPathOverride != NULL ) + seenDylibInstallPath = dylibInfo.options.fInstallPathOverride; + if ( strcmp(seenDylibInstallPath, dylibInstallPath) == 0 ) { + fLibraryToOrdinal[dylibInfo.reader] = fLibraryToOrdinal[seenDylibInfo.reader]; + newDylib = false; + break; + } + } + } + + if ( newDylib ) { + // assign new ordinal and check for other paired load commands + fLibraryToOrdinal[dylibInfo.reader] = ordinal++; + fWriterSynthesizedAtoms.push_back(new DylibLoadCommandsAtom(*this, dylibInfo)); + if ( dylibInfo.options.fReExport ) { + // this dylib also needs a sub_x load command + bool isFrameworkReExport = false; + const char* lastSlash = strrchr(dylibInstallPath, '/'); + if ( lastSlash != NULL ) { + char frameworkName[strlen(lastSlash)+20]; + sprintf(frameworkName, "/%s.framework/", &lastSlash[1]); + isFrameworkReExport = (strstr(dylibInstallPath, frameworkName) != NULL); + } + if ( isFrameworkReExport ) { + // needs a LC_SUB_UMBRELLA command + fWriterSynthesizedAtoms.push_back(new SubUmbrellaLoadCommandsAtom(*this, &lastSlash[1])); + } + else { + // needs a LC_SUB_LIBRARY command + const char* nameStart = &lastSlash[1]; + if ( lastSlash == NULL ) + nameStart = dylibInstallPath; + int len = strlen(nameStart); + const char* dot = strchr(nameStart, '.'); + if ( dot != NULL ) + len = dot - nameStart; + fWriterSynthesizedAtoms.push_back(new SubLibraryLoadCommandsAtom(*this, nameStart, len)); + } + } + } + } + } + // add umbrella command if needed + if ( fOptions.umbrellaName() != NULL ) { + fWriterSynthesizedAtoms.push_back(new UmbrellaLoadCommandsAtom(*this, fOptions.umbrellaName())); + } + } + break; + case Options::kStaticExecutable: + case Options::kObjectFile: + case Options::kDyld: + break; + } + + //fprintf(stderr, "ordinals table:\n"); + //for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { + // fprintf(stderr, "%d <== %s\n", it->second, it->first->getPath()); + //} +} + +Writer::~Writer() +{ + if ( fFilePath != NULL ) + free((void*)fFilePath); + if ( fSymbolTable != NULL ) + delete [] fSymbolTable; + //if ( fStringPool != NULL ) + // delete [] fStringPool; +} + +const char* Writer::getPath() +{ + return fFilePath; +} + + +std::vector& Writer::getAtoms() +{ + return fWriterSynthesizedAtoms; +} + +std::vector* Writer::getJustInTimeAtomsFor(const char* name) +{ + return NULL; +} + +std::vector* Writer::getStabsDebugInfo() +{ + return NULL; +} + +ObjectFile::Atom* Writer::getUndefinedProxyAtom(const char* name) +{ + if ( (fOptions.outputKind() == Options::kObjectFile) + || (fOptions.undefinedTreatment() != Options::kUndefinedError) ) + return new UndefinedSymbolProxyAtom(*this, name); + else + return NULL; +} + +uint8_t Writer::ordinalForLibrary(ObjectFile::Reader* lib) +{ + // flat namespace images use zero for all ordinals + if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace ) + return 0; + + // is an UndefinedSymbolProxyAtom + if ( lib == this ) + if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) + return DYNAMIC_LOOKUP_ORDINAL; + + std::map::iterator pos = fLibraryToOrdinal.find(lib); + if ( pos != fLibraryToOrdinal.end() ) + return pos->second; + + throw "can't find ordinal for imported symbol"; +} + + +void Writer::write(std::vector& atoms, class ObjectFile::Atom* entryPointAtom) +{ + fAllAtoms = &atoms; + fEntryPoint = entryPointAtom; + + // create SegmentInfo and SectionInfo objects and assign all atoms to a section + partitionIntoSections(); + + // segment load command can now be sized and padding can be set + adjustLoadCommandsAndPadding(); + + // assign each section a file offset + assignFileOffsets(); + + // build symbol table and relocations + buildLinkEdit(); + + // write everything + writeAtoms(); +} + +void Writer::buildLinkEdit() +{ + this->collectExportedAndImportedAndLocalAtoms(); + this->buildSymbolTable(); + this->buildFixups(); + this->adjustLinkEditSections(); +} + + + +uint64_t Writer::getAtomLoadAddress(const ObjectFile::Atom* atom) +{ + return atom->getAddress(); +// SectionInfo* info = (SectionInfo*)atom->getSection(); +// return info->getBaseAddress() + atom->getSectionOffset(); +} + +void Writer::setExportNlist(const ObjectFile::Atom* atom, macho_nlist* entry) +{ + // set n_type + entry->set_n_type(N_EXT | N_SECT); + if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) && fOptions.keepPrivateExterns() && (fOptions.outputKind() == Options::kObjectFile) ) + entry->set_n_type(N_EXT | N_SECT | N_PEXT); + + // set n_sect (section number of implementation ) + uint8_t sectionIndex = atom->getSection()->getIndex(); + entry->set_n_sect(sectionIndex); + + // the __mh_execute_header is magic and must be an absolute symbol + if ( (fOptions.outputKind() == Options::kDynamicExecutable) && (sectionIndex==0) && atom->dontStripName()) + entry->set_n_type(N_EXT | N_ABS); + + // set n_desc + uint16_t desc = 0; + if ( atom->dontStripName() ) + desc |= REFERENCED_DYNAMICALLY; + if ( atom->isWeakDefinition() && (strcmp(atom->getSectionName(), "__common") != 0) ) { + desc |= N_WEAK_DEF; + fHasWeakExports = true; + } + entry->set_n_desc(desc); + + // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) + entry->set_n_value(this->getAtomLoadAddress(atom)); +} + +void Writer::setImportNlist(const ObjectFile::Atom* atom, macho_nlist* entry) +{ + // set n_type + entry->set_n_type(N_UNDF | N_EXT); + + // set n_sect + entry->set_n_sect(0); + + uint16_t desc = 0; + if ( fOptions.outputKind() != Options::kObjectFile ) { + // set n_desc ( high byte is library ordinal, low byte is reference type ) + desc = REFERENCE_FLAG_UNDEFINED_LAZY; // FIXME + try { + uint8_t ordinal = this->ordinalForLibrary(atom->getFile()); + SET_LIBRARY_ORDINAL(desc, ordinal); + } + catch (const char* msg) { + throwf("%s %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath()); + } + } + if ( atom->dontStripName() ) + desc |= REFERENCED_DYNAMICALLY; + // an import proxy is always weak (overridden by definition in .o files) + // so we ask its reader if the exported symbol in its dylib is weak + if ( ( fOptions.outputKind() != Options::kObjectFile) && atom->getFile()->isDefinitionWeak(*atom) ) { + desc |= N_REF_TO_WEAK; + fReferencesWeakImports = true; + } + // set weak_import attribute + if ( atom->getImportWeakness() == ObjectFile::Atom::kWeakImport ) + desc |= N_WEAK_REF; + entry->set_n_desc(desc); + + // set n_value, zero for import proxy and size for tentative definition + entry->set_n_value(atom->getSize()); +} + +void Writer::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist* entry) +{ + // set n_type + uint8_t type = N_SECT; + if ( atom->getScope() == ObjectFile::Atom::scopeLinkageUnit ) + type |= N_PEXT; + entry->set_n_type(type); + + // set n_sect (section number of implementation ) + uint8_t sectIndex = atom->getSection()->getIndex(); + if ( sectIndex == 0 ) { + // see synthesized lable for mach_header needs special section number... + if ( strcmp(atom->getSectionName(), "._mach_header") == 0 ) + sectIndex = 1; + } + entry->set_n_sect(sectIndex); + + // set n_desc + uint16_t desc = 0; + if ( atom->isWeakDefinition() && (strcmp(atom->getSectionName(), "__common") != 0) ) // commons on not weak + desc |= N_WEAK_DEF; + entry->set_n_desc(desc); + + // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) + entry->set_n_value(this->getAtomLoadAddress(atom)); +} + + +void Writer::setNlistRange(std::vector& atoms, uint32_t startIndex, uint32_t count) +{ + macho_nlist* entry = &fSymbolTable[startIndex]; + for (uint32_t i=0; i < count; ++i, ++entry) { + ObjectFile::Atom* atom = atoms[i]; + entry->set_n_strx(this->fStringsAtom->add(atom->getName())); + if ( &atoms == &fExportedAtoms ) { + this->setExportNlist(atom, entry); + } + else if ( &atoms == &fImportedAtoms ) { + this->setImportNlist(atom, entry); + } + else { + this->setLocalNlist(atom, entry); + } + } +} + +void Writer::buildSymbolTable() +{ + fSymbolTableStabsStartIndex = 0; + fSymbolTableStabsCount = this->collectStabs(); + fSymbolTableLocalStartIndex = fSymbolTableStabsStartIndex + fSymbolTableStabsCount; + fSymbolTableLocalCount = fLocalSymbolAtoms.size(); + fSymbolTableExportStartIndex = fSymbolTableLocalStartIndex + fSymbolTableLocalCount; + fSymbolTableExportCount = fExportedAtoms.size(); + fSymbolTableImportStartIndex = fSymbolTableExportStartIndex + fSymbolTableExportCount; + fSymbolTableImportCount = fImportedAtoms.size(); + + // allocate symbol table + fSymbolTableCount = fSymbolTableStabsCount + fSymbolTableLocalCount + fSymbolTableExportCount + fSymbolTableImportCount; + fSymbolTable = new macho_nlist[fSymbolTableCount]; + + // fill in symbol table and string pool (do stabs last so strings are at end of pool) + setNlistRange(fLocalSymbolAtoms, fSymbolTableLocalStartIndex, fSymbolTableLocalCount); + setNlistRange(fExportedAtoms, fSymbolTableExportStartIndex, fSymbolTableExportCount); + setNlistRange(fImportedAtoms, fSymbolTableImportStartIndex, fSymbolTableImportCount); + addStabs(fSymbolTableStabsStartIndex, fSymbolTableStabsCount); +} + + + +bool Writer::shouldExport(ObjectFile::Atom& atom) +{ + switch ( atom.getScope() ) { + case ObjectFile::Atom::scopeGlobal: + return true; + case ObjectFile::Atom::scopeLinkageUnit: + return ( fOptions.keepPrivateExterns() && (fOptions.outputKind() == Options::kObjectFile) ); + default: + return false; + } +} + +void Writer::collectExportedAndImportedAndLocalAtoms() +{ + const int atomCount = fAllAtoms->size(); + for (int i=0; i < atomCount; ++i) { + ObjectFile::Atom* atom = (*fAllAtoms)[i]; + // only named atoms go in symbol table + if ( atom->getName() != NULL ) { + // put atom into correct bucket: imports, exports, locals + //printf("collectExportedAndImportedAndLocalAtoms() name=%s\n", atom->getDisplayName()); + if ( atom->isImportProxy() || ((fOptions.outputKind() == Options::kObjectFile) && (strcmp(atom->getSectionName(), "__common") == 0)) ) + fImportedAtoms.push_back(atom); + else if ( this->shouldExport(*atom) ) + fExportedAtoms.push_back(atom); + else if ( !fOptions.stripLocalSymbols() ) + fLocalSymbolAtoms.push_back(atom); + } + } + + // sort exported atoms by name + std::sort(fExportedAtoms.begin(), fExportedAtoms.end(), ExportSorter()); +} + + +bool Writer::stabChunkCompare(const struct StabChunks& lhs, const struct StabChunks& rhs) +{ + if ( lhs.fReader != rhs.fReader ) { + return lhs.fReader < rhs.fReader; + } + return lhs.fOrderInReader < rhs.fOrderInReader; +} + +unsigned int Writer::collectStabs() +{ + unsigned int count = 0; + + // collect all stabs chunks + std::set seenReaders; + const int atomCount = fAllAtoms->size(); + for (int i=0; i < atomCount; ++i) { + ObjectFile::Atom* atom = (*fAllAtoms)[i]; + ObjectFile::Reader* atomsReader = atom->getFile(); + if ( (atomsReader != NULL) && (seenReaders.count(atomsReader) == 0) ) { + seenReaders.insert(atomsReader); + std::vector* readerStabs = atomsReader->getStabsDebugInfo(); + if ( readerStabs != NULL ) { + StabChunks chunk; + chunk.fAtom = NULL; + chunk.fReader = atomsReader; + chunk.fOrderInReader = 0; + chunk.fStabs = readerStabs; + fStabChunks.push_back(chunk); + count += readerStabs->size() + 1; // extra one is for trailing N_SO + } + } + std::vector* atomStabs = atom->getStabsDebugInfo(); + if ( atomStabs != NULL ) { + StabChunks chunk; + chunk.fAtom = atom; + chunk.fReader = atomsReader; + chunk.fOrderInReader = atom->getSortOrder(); + chunk.fStabs = atomStabs; + fStabChunks.push_back(chunk); + count += atomStabs->size(); + } + } + + // sort by order in original .o file + std::sort(fStabChunks.begin(), fStabChunks.end(), stabChunkCompare); + + return count; +} + +macho_uintptr_t Writer::valueForStab(const ObjectFile::StabsInfo& stab, const ObjectFile::Atom* atom) +{ + switch ( stab.type ) { + case N_FUN: + if ( stab.other == 0 ) + break; + // end of function N_FUN has size (not address) so should not be adjusted + // fall through + case N_BNSYM: + case N_ENSYM: + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + case N_STSYM: + case N_LCSYM: + // all these stab types need their value changed from an offset in the atom to an address + if ( atom != NULL ) + return getAtomLoadAddress(atom) + stab.atomOffset; + } + return stab.atomOffset; +} + + +void Writer::addStabs(uint32_t startIndex, uint32_t count) +{ + macho_nlist* entry = &fSymbolTable[startIndex]; + const int chunkCount = fStabChunks.size(); + for (int i=0; i < chunkCount; ++i ) { + const StabChunks& chunk = fStabChunks[i]; + const int stabCount = chunk.fStabs->size(); + for (int j=0; j < stabCount; ++j ) { + const ObjectFile::StabsInfo& stab = (*chunk.fStabs)[j]; + entry->set_n_type(stab.type); + entry->set_n_sect(stab.other); + entry->set_n_desc(stab.desc); + entry->set_n_value(valueForStab(stab, chunk.fAtom)); + entry->set_n_strx(this->fStringsAtom->add(stab.string)); + ++entry; + } + if ( (i == chunkCount-1) || (fStabChunks[i+1].fReader != chunk.fReader) ) { + // need to add empty SO at end of each file + entry->set_n_type(N_SO); + entry->set_n_sect(1); + entry->set_n_desc(0); + entry->set_n_value(0); + entry->set_n_strx(this->fStringsAtom->emptyString()); + ++entry; + } + } +} + + + + +uint32_t Writer::symbolIndex(ObjectFile::Atom& atom) +{ + // search imports + const int importCount = fImportedAtoms.size(); + for (int i=0; i < importCount; ++i) { + if ( &atom == fImportedAtoms[i] ) + return i + fSymbolTableImportStartIndex; + } + + // search locals + const int localCount = fLocalSymbolAtoms.size(); + for (int i=0; i < localCount; ++i) { + if ( &atom == fLocalSymbolAtoms[i] ) + return i + fSymbolTableLocalStartIndex; + } + + // search exports + const int exportCount = fExportedAtoms.size(); + for (int i=0; i < exportCount; ++i) { + if ( &atom == fExportedAtoms[i] ) + return i + fSymbolTableExportStartIndex; + } + + fprintf(stderr, "symbolIndex(%s)\n", atom.getDisplayName()); + fprintf(stderr, "from %s\n", atom.getFile()->getPath()); + throw "atom not found"; +} + + +void Writer::buildFixups() +{ + if ( fOptions.outputKind() == Options::kObjectFile ) + this->buildObjectFileFixups(); + else + this->buildExecutableFixups(); +} + +uint32_t Writer::addRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + ObjectFile::Atom& target = ref->getTarget(); + bool isExtern = target.isImportProxy() || ( strcmp(target.getSectionName(), "__common") == 0 ); + uint32_t symbolIndex = 0; + if ( isExtern ) + symbolIndex = this->symbolIndex(target); + uint32_t sectionNum = target.getSection()->getIndex(); + uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); + macho_relocation_info reloc1; + macho_relocation_info reloc2; + macho_scattered_relocation_info* sreloc1 = (macho_scattered_relocation_info*)&reloc1; + macho_scattered_relocation_info* sreloc2 = (macho_scattered_relocation_info*)&reloc2; + + switch ( ref->getKind() ) { + case ObjectFile::Reference::noFixUp: + return 0; + + case ObjectFile::Reference::pointer: + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(macho_relocation_info::pointer_length); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + fInternalRelocs.insert(fInternalRelocs.begin(), reloc1); + return 1; + + case ObjectFile::Reference::ppcFixupBranch24: + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_type(PPC_RELOC_BR24); + reloc1.set_r_extern(isExtern); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR24); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + fInternalRelocs.insert(fInternalRelocs.begin(), reloc1); + return 1; + + case ObjectFile::Reference::ppcFixupBranch14: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(false); + reloc1.set_r_type(PPC_RELOC_BR14); + fInternalRelocs.insert(fInternalRelocs.begin(), reloc1); + return 1; + + case ObjectFile::Reference::ppcFixupPicBaseLow14: + case ObjectFile::Reference::ppcFixupPicBaseLow16: + { + macho_uintptr_t fromAddr = atom->getAddress() + ref->getFromTargetOffset(); + macho_uintptr_t toAddr = target.getAddress() + ref->getTargetOffset(); + uint32_t overflow = 0; + if ( ((toAddr-fromAddr) & 0x00008000) != 0 ) + overflow = 1; + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + if ( ref->getKind() == ObjectFile::Reference::ppcFixupPicBaseLow16 ) + sreloc1->set_r_type(PPC_RELOC_LO16_SECTDIFF); + else + sreloc1->set_r_type(PPC_RELOC_LO14_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(((toAddr-fromAddr) >> 16)); + sreloc2->set_r_value(fromAddr); + fInternalRelocs.insert(fInternalRelocs.begin(), reloc2); + fInternalRelocs.insert(fInternalRelocs.begin(), reloc1); + return 2; + } + + case ObjectFile::Reference::ppcFixupPicBaseHigh16: + { + macho_uintptr_t fromAddr = atom->getAddress() + ref->getFromTargetOffset(); + macho_uintptr_t toAddr = target.getAddress() + ref->getTargetOffset(); + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + fInternalRelocs.insert(fInternalRelocs.begin(), reloc2); + fInternalRelocs.insert(fInternalRelocs.begin(), reloc1); + return 2; + } + + case ObjectFile::Reference::ppcFixupAbsLow14: + case ObjectFile::Reference::ppcFixupAbsLow16: + { + macho_uintptr_t toAddr = target.getAddress() + ref->getTargetOffset(); + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + if ( ref->getKind() == ObjectFile::Reference::ppcFixupAbsLow16 ) + reloc1.set_r_type(PPC_RELOC_LO16); + else + reloc1.set_r_type(PPC_RELOC_LO14); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + if ( ref->getKind() == ObjectFile::Reference::ppcFixupAbsLow16 ) + sreloc1->set_r_type(PPC_RELOC_LO16); + else + sreloc1->set_r_type(PPC_RELOC_LO14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + if ( isExtern ) + reloc2.set_r_address(ref->getTargetOffset() >> 16); + else + reloc2.set_r_address(toAddr >> 16); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + fInternalRelocs.insert(fInternalRelocs.begin(), reloc2); + fInternalRelocs.insert(fInternalRelocs.begin(), reloc1); + return 2; + } + + case ObjectFile::Reference::ppcFixupAbsHigh16: + { + macho_uintptr_t toAddr = target.getAddress() + ref->getTargetOffset(); + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(PPC_RELOC_HI16); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HI16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + if ( isExtern ) + reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF); + else + reloc2.set_r_address(toAddr & 0xFFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + fInternalRelocs.insert(fInternalRelocs.begin(), reloc2); + fInternalRelocs.insert(fInternalRelocs.begin(), reloc1); + return 2; + } + + case ObjectFile::Reference::ppcFixupAbsHigh16AddLow: + { + macho_uintptr_t toAddr = target.getAddress() + ref->getTargetOffset(); + uint32_t overflow = 0; + if ( (toAddr & 0x00008000) != 0 ) + overflow = 0x10000; + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(PPC_RELOC_HA16); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + if ( isExtern ) + reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF); + else + reloc2.set_r_address(toAddr & 0xFFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + fInternalRelocs.insert(fInternalRelocs.begin(), reloc2); + fInternalRelocs.insert(fInternalRelocs.begin(), reloc1); + return 2; + } + + case ObjectFile::Reference::pointer32Difference: + case ObjectFile::Reference::pointer64Difference: + { + macho_uintptr_t toAddr = target.getAddress() + ref->getTargetOffset(); + macho_uintptr_t fromAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + if ( ref->getKind() == ObjectFile::Reference::pointer64Difference ) + sreloc1->set_r_length(3); + else + sreloc1->set_r_length(2); + if ( ref->getTargetOffset() != 0 ) + sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(PPC_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(toAddr); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(macho_relocation_info::pointer_length); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(0); + sreloc2->set_r_value(fromAddr); + fInternalRelocs.insert(fInternalRelocs.begin(), reloc2); + fInternalRelocs.insert(fInternalRelocs.begin(), reloc1); + return 2; + } + + case ObjectFile::Reference::x86FixupBranch32: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(false); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + fInternalRelocs.insert(fInternalRelocs.begin(), reloc1); + return 1; + + } + return 0; +} + + +void Writer::buildObjectFileFixups() +{ + uint32_t relocIndex = 0; + std::vector& segmentInfos = fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* curSegment = segmentInfos[i]; + std::vector& sectionInfos = curSegment->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + std::vector& sectionAtoms = curSection->fAtoms; + if ( ! curSection->fAllZeroFill ) { + if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers ) + curSection->fIndirectSymbolOffset = fIndirectSymbolTable.size(); + curSection->fRelocOffset = relocIndex; + const int atomCount = sectionAtoms.size(); + for (int k=0; k < atomCount; ++k) { + ObjectFile::Atom* atom = sectionAtoms[k]; + std::vector& refs = atom->getReferences(); + const int refCount = refs.size(); + for (int l=0; l < refCount; ++l) { + ObjectFile::Reference* ref = refs[l]; + relocIndex += this->addRelocs(atom, ref); + if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers ) { + uint32_t offsetInSection = atom->getSectionOffset(); + uint32_t indexInSection = offsetInSection / sizeof(macho_uintptr_t); + uint32_t undefinedSymbolIndex = this->symbolIndex(ref->getTarget()); + uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; + IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; + //printf("fIndirectSymbolTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, ref->getTarget().getName(), atom->getSize()); + fIndirectSymbolTable.push_back(entry); + } + } + } + curSection->fRelocCount = relocIndex - curSection->fRelocOffset; + } + } + } + + // now reverse reloc entries + for(int i=0; i < segCount; ++i) { + SegmentInfo* curSegment = segmentInfos[i]; + std::vector& sectionInfos = curSegment->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + curSection->fRelocOffset = relocIndex - curSection->fRelocOffset - curSection->fRelocCount; + } + } + +} + + +void Writer::buildExecutableFixups() +{ + const bool slideable = (fOptions.outputKind() != Options::kDynamicExecutable) && (fOptions.outputKind() != Options::kStaticExecutable); + std::vector& segmentInfos = fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* curSegment = segmentInfos[i]; + std::vector& sectionInfos = curSegment->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + std::vector& sectionAtoms = curSection->fAtoms; + if ( ! curSection->fAllZeroFill ) { + if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers ) + curSection->fIndirectSymbolOffset = fIndirectSymbolTable.size(); + const int atomCount = sectionAtoms.size(); + for (int k=0; k < atomCount; ++k) { + ObjectFile::Atom* atom = sectionAtoms[k]; + std::vector& refs = atom->getReferences(); + const int refCount = refs.size(); + //printf("atom %s has %d references\n", atom->getDisplayName(), refCount); + + for (int l=0; l < refCount; ++l) { + ObjectFile::Reference* ref = refs[l]; + // only care about references that need dyld fixups + if ( ref->requiresRuntimeFixUp() ) { + if ( ! atom->getSegment().isContentWritable() ) + throwf("relocations in read-only segments not supported. %s in %s reference to %s", atom->getDisplayName(), atom->getFile()->getPath(), ref->getTarget().getDisplayName()); + if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers ) { + // if atom is in (non)lazy_pointer section, this is encoded as an indirect symbol + if ( atom->getSize() != sizeof(macho_uintptr_t) ) { + printf("oversize pointer atom %s from file %s\n", atom->getDisplayName(), atom->getFile()->getPath()); + } + uint32_t offsetInSection = atom->getSectionOffset(); + uint32_t indexInSection = offsetInSection / sizeof(macho_uintptr_t); + uint32_t undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; + //printf("indirect pointer atom %s section offset = %d\n", atom->getDisplayName(), offsetInSection); + if ( ref->getTarget().isImportProxy() + || ref->getTarget().isWeakDefinition() + || (fOptions.interposable() && fOptions.shouldExport(ref->getTarget().getName())) + || (fOptions.nameSpace() == Options::kFlatNameSpace) + || (fOptions.nameSpace() == Options::kForceFlatNameSpace) ) { + undefinedSymbolIndex = this->symbolIndex(ref->getTarget()); + } + uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; + IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; + //printf("fIndirectSymbolTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, ref->getTarget().getName(), atom->getSize()); + fIndirectSymbolTable.push_back(entry); + if ( slideable && curSection->fAllLazyPointers ) { + // if this is a dylib/bundle, need PBLAPTR internal relocation to fix up binding handler if image slides + macho_relocation_info pblaReloc; + macho_scattered_relocation_info* pblaSReloc = (macho_scattered_relocation_info*)&pblaReloc; + pblaSReloc->set_r_scattered(true); + pblaSReloc->set_r_pcrel(false); + pblaSReloc->set_r_length(macho_relocation_info::pointer_length); + pblaSReloc->set_r_type(PPC_RELOC_PB_LA_PTR); + pblaSReloc->set_r_address(atom->getAddress()-fOptions.baseAddress()); + pblaSReloc->set_r_value(ref->getFromTarget().getAddress()); // helper is stored in "from" address + fInternalRelocs.push_back(pblaReloc); + } + } + else if ( ref->getTarget().isImportProxy() ) { + // if import is to antoher dylib, this is encoded as an external relocation + macho_relocation_info externalReloc; + externalReloc.set_r_address(atom->getAddress()+ref->getFixUpOffset()-fOptions.baseAddress()); + externalReloc.set_r_symbolnum(this->symbolIndex(ref->getTarget())); + externalReloc.set_r_pcrel(false); + externalReloc.set_r_length(macho_relocation_info::pointer_length); + externalReloc.set_r_extern(true); + externalReloc.set_r_type(GENERIC_RELOC_VANILLA); + fExternalRelocs.push_back(externalReloc); + } + else if ( slideable ) { + // if this is a dylib/bundle, need fix-up encoded as an internal relocation + macho_relocation_info internalReloc; + SectionInfo* sectInfo = (SectionInfo*)ref->getTarget().getSection(); + uint32_t sectionNum = sectInfo->getIndex(); + // special case _mh_dylib_header and friends which are not in any real section + if ( (sectionNum ==0) && sectInfo->fVirtualSection && (strcmp(sectInfo->fSectionName, "._mach_header") == 0) ) + sectionNum = 1; + internalReloc.set_r_address(atom->getAddress()+ref->getFixUpOffset()-fOptions.baseAddress()); + internalReloc.set_r_symbolnum(sectionNum); + internalReloc.set_r_pcrel(false); + internalReloc.set_r_length(macho_relocation_info::pointer_length); + internalReloc.set_r_extern(false); + internalReloc.set_r_type(GENERIC_RELOC_VANILLA); + fInternalRelocs.push_back(internalReloc); + } + } + } + } + } + } + } +} + +class ContentWriter : public ObjectFile::ContentWriter +{ +public: + ContentWriter(int fd, uint64_t fileOffset) : fFileDescriptor(fd), fFileOffset(fileOffset) {} + virtual void write(uint64_t atomOffset, const void* buffer, uint64_t size) { + ::pwrite(fFileDescriptor, buffer, size, fFileOffset+atomOffset); + } +private: + int fFileDescriptor; + uint64_t fFileOffset; +}; + + +void Writer::writeAtoms() +{ + const bool requireAllFixUps = (fOptions.outputKind() != Options::kObjectFile); + + std::vector& segmentInfos = fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* curSegment = segmentInfos[i]; + bool isText = ((curSegment->fInitProtection & VM_PROT_EXECUTE) != 0); + std::vector& sectionInfos = curSegment->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + std::vector& sectionAtoms = curSection->fAtoms; + //printf("writing %d atoms for section %s\n", (int)sectionAtoms.size(), curSection->fSectionName); + if ( ! curSection->fAllZeroFill ) { + const int atomCount = sectionAtoms.size(); + uint32_t end = curSection->fFileOffset; + for (int k=0; k < atomCount; ++k) { + ObjectFile::Atom* atom = sectionAtoms[k]; + if ( !atom->isImportProxy() ) { + uint32_t offset = curSection->fFileOffset + atom->getSectionOffset(); + if ( isText && (offset != end) ) { + // fill gaps with no-ops + #if defined(ARCH_PPC) || defined(ARCH_PPC64) + uint32_t ppcNop; + OSWriteBigInt32(&ppcNop, 0, 0x60000000); + for (uint32_t p=end; p < offset; p += 4) + ::pwrite(fFileDescriptor, &ppcNop, 4, p); + #else defined(ARCH_I386) + uint8_t x86Nop = 0x90; + for (uint32_t p=end; p < offset; ++p) + ::pwrite(fFileDescriptor, &x86Nop, 1, p); + #endif + } + ContentWriter writer(fFileDescriptor, offset); + atom->writeContent(requireAllFixUps, writer); + end = offset+atom->getSize(); + //printf("wrote 0x%08X -> 0x%08X, atom %s\n", offset, end, atom->getDisplayName()); + } + } + } + } + } +} + + +void Writer::partitionIntoSections() +{ + const bool oneSegmentCommand = (fOptions.outputKind() == Options::kObjectFile); + + // for every atom, set its sectionInfo object and section offset + // build up fSegmentInfos along the way + ObjectFile::Section* curSection = NULL; + SectionInfo* currentSectionInfo = NULL; + SegmentInfo* currentSegmentInfo = NULL; + unsigned int sectionIndex = 1; + for (unsigned int i=0; i < fAllAtoms->size(); ++i) { + ObjectFile::Atom* atom = (*fAllAtoms)[i]; + if ( atom->getSection() != curSection ) { + if ( oneSegmentCommand ) { + if ( currentSegmentInfo == NULL ) { + currentSegmentInfo = new SegmentInfo(); + currentSegmentInfo->fInitProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + this->fSegmentInfos.push_back(currentSegmentInfo); + } + currentSectionInfo = new SectionInfo(); + strcpy(currentSectionInfo->fSectionName, atom->getSectionName()); + strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName()); + currentSectionInfo->fAlignment = atom->getAlignment(); + currentSectionInfo->fAllZeroFill = atom->isZeroFill(); + currentSectionInfo->fVirtualSection = ( currentSectionInfo->fSectionName[0] == '.'); + if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections ) + currentSectionInfo->setIndex(sectionIndex++); + currentSegmentInfo->fSections.push_back(currentSectionInfo); + } + else { + if ( (currentSegmentInfo == NULL) || (strcmp(currentSegmentInfo->fName, atom->getSegment().getName()) != 0) ) { + currentSegmentInfo = new SegmentInfo(); + strcpy(currentSegmentInfo->fName, atom->getSegment().getName()); + uint32_t initprot = 0; + if ( atom->getSegment().isContentReadable() ) + initprot |= VM_PROT_READ; + if ( atom->getSegment().isContentWritable() ) + initprot |= VM_PROT_WRITE; + if ( atom->getSegment().isContentExecutable() ) + initprot |= VM_PROT_EXECUTE; + currentSegmentInfo->fInitProtection = initprot; + if ( initprot == 0 ) + currentSegmentInfo->fMaxProtection = 0; // pagezero should have maxprot==initprot==0 + else + currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + currentSegmentInfo->fBaseAddress = atom->getSegment().getBaseAddress(); + this->fSegmentInfos.push_back(currentSegmentInfo); + } + currentSectionInfo = new SectionInfo(); + strcpy(currentSectionInfo->fSectionName, atom->getSectionName()); + strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName()); + currentSectionInfo->fAlignment = atom->getAlignment(); + // check for -sectalign override + std::vector& alignmentOverrides = fOptions.sectionAlignments(); + for(std::vector::iterator it=alignmentOverrides.begin(); it != alignmentOverrides.end(); ++it) { + if ( (strcmp(it->segmentName, currentSectionInfo->fSegmentName) == 0) && (strcmp(it->sectionName, currentSectionInfo->fSectionName) == 0) ) + currentSectionInfo->fAlignment = it->alignment; + } + currentSectionInfo->fAllZeroFill = atom->isZeroFill(); + currentSectionInfo->fVirtualSection = ( currentSectionInfo->fSectionName[0] == '.'); + if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections ) + currentSectionInfo->setIndex(sectionIndex++); + currentSegmentInfo->fSections.push_back(currentSectionInfo); + } + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "._load_commands") == 0) ) { + fLoadCommandsSection = currentSectionInfo; + fLoadCommandsSegment = currentSegmentInfo; + } + if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_symbol_ptr") == 0) ) + currentSectionInfo->fAllLazyPointers = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__nl_symbol_ptr") == 0) ) + currentSectionInfo->fAllNonLazyPointers = true; + curSection = atom->getSection(); + } + // any non-zero fill atoms make whole section marked not-zero-fill + if ( currentSectionInfo->fAllZeroFill && ! atom->isZeroFill() ) + currentSectionInfo->fAllZeroFill = false; + // change section object to be Writer's SectionInfo object + atom->setSection(currentSectionInfo); + // section alignment is that of a contained atom with the greatest alignment + uint8_t atomAlign = atom->getAlignment(); + if ( currentSectionInfo->fAlignment < atomAlign ) + currentSectionInfo->fAlignment = atomAlign; + // calculate section offset for this atom + uint64_t offset = currentSectionInfo->fSize; + uint64_t alignment = 1 << atomAlign; + offset = ( (offset+alignment-1) & (-alignment) ); + atom->setSectionOffset(offset); + currentSectionInfo->fSize = offset + atom->getSize(); + // add atom to section vector + currentSectionInfo->fAtoms.push_back(atom); + } +} + + +void Writer::adjustLoadCommandsAndPadding() +{ + fSegmentCommands->computeSize(); + + // recompute load command section offsets + uint64_t offset = 0; + std::vector& loadCommandAtoms = fLoadCommandsSection->fAtoms; + const unsigned int atomCount = loadCommandAtoms.size(); + for (unsigned int i=0; i < atomCount; ++i) { + ObjectFile::Atom* atom = loadCommandAtoms[i]; + uint64_t alignment = 1 << atom->getAlignment(); + offset = ( (offset+alignment-1) & (-alignment) ); + atom->setSectionOffset(offset); + offset += atom->getSize(); + fLoadCommandsSection->fSize = offset; + } + + std::vector& sectionInfos = fLoadCommandsSegment->fSections; + const int sectionCount = sectionInfos.size(); + uint64_t paddingSize = 0; + if ( fOptions.outputKind() == Options::kDyld ) { + // dyld itself has special padding requirements. We want the beginning __text section to start at a stable address + uint32_t totalSizeOfHeaderAndLoadCommands = 0; + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + totalSizeOfHeaderAndLoadCommands += curSection->fSize; + if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) + break; + } + paddingSize = 4096 - (totalSizeOfHeaderAndLoadCommands % 4096); + } + else { + // calculate max padding to keep segment size same, but all free space at end of load commands + uint64_t totalSize = 0; + uint64_t worstCaseAlignmentPadding = 0; + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + totalSize += curSection->fSize; + if ( j != 0 ) // don't count aligment of mach_header which is page-aligned + worstCaseAlignmentPadding += (1 << curSection->fAlignment) - 1; + } + uint64_t segmentSize = ((totalSize+worstCaseAlignmentPadding+4095) & (-4096)); + // don't know exactly how it will layout, but we can inflate padding atom this big and still keep aligment constraints + paddingSize = segmentSize - totalSize; + + // if command line requires more padding than this + if ( paddingSize < fOptions.minimumHeaderPad() ) { + int extraPages = (fOptions.minimumHeaderPad() - paddingSize + 4095)/4096; + paddingSize += extraPages * 4096; + } + } + + // adjust atom size and update section size + fHeaderPadding->setSize(paddingSize); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) + curSection->fSize = paddingSize; + } +} + +// assign file offsets and logical address to all segments +void Writer::assignFileOffsets() +{ + bool haveFixedSegments = false; + uint64_t fileOffset = 0; + uint64_t nextContiguousAddress = fOptions.baseAddress(); + std::vector& segmentInfos = fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* curSegment = segmentInfos[i]; + fileOffset = (fileOffset+4095) & (-4096); + curSegment->fFileOffset = fileOffset; + if ( curSegment->fBaseAddress == 0 ) { + // segment has uses next address + curSegment->fBaseAddress = nextContiguousAddress; + } + else { + // segment has fixed address + haveFixedSegments = true; + } + uint64_t address = curSegment->fBaseAddress; + std::vector& sectionInfos = curSegment->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + uint64_t alignment = 1 << curSection->fAlignment; + fileOffset = ( (fileOffset+alignment-1) & (-alignment) ); + address = ( (address+alignment-1) & (-alignment) ); + curSection->fFileOffset = fileOffset; + curSection->setBaseAddress(address); + //printf("assignFileOffsets(): setBaseAddress(%s, 0x%08llX)\n", curSection->fSectionName, address); + curSegment->fSize = curSection->getBaseAddress() + curSection->fSize - curSegment->fBaseAddress; + if ( (fOptions.outputKind() != Options::kObjectFile) || ! curSection->fVirtualSection ) + address += curSection->fSize; + if ( !curSection->fAllZeroFill ) { + curSegment->fFileSize = curSegment->fSize; + fileOffset += curSection->fSize; + } + } + if ( curSegment->fBaseAddress == nextContiguousAddress ) + nextContiguousAddress = (curSegment->fBaseAddress+curSegment->fSize+4095) & (-4096); + } + + // check for segment overlaps + if ( haveFixedSegments ) { + for(int i=0; i < segCount; ++i) { + SegmentInfo* segment1 = segmentInfos[i]; + for(int j=0; j < segCount; ++j) { + if ( i != j ) { + SegmentInfo* segment2 = segmentInfos[j]; + if ( segment1->fBaseAddress < segment2->fBaseAddress ) { + if ( (segment1->fBaseAddress+segment1->fSize) > segment2->fBaseAddress ) + throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", + segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); + } + else if ( segment1->fBaseAddress > segment2->fBaseAddress ) { + if ( (segment2->fBaseAddress+segment2->fSize) > segment1->fBaseAddress ) + throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", + segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); + } + else { + throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", + segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); + } + } + } + } + } +} + +void Writer::adjustLinkEditSections() +{ + // link edit content is always in last segment + SegmentInfo* lastSeg = fSegmentInfos[fSegmentInfos.size()-1]; + unsigned int firstLinkEditSectionIndex = 0; + while ( strcmp(lastSeg->fSections[firstLinkEditSectionIndex]->fSegmentName, "__LINKEDIT") != 0 ) + ++firstLinkEditSectionIndex; + + const unsigned int sectionCount = lastSeg->fSections.size(); + uint64_t fileOffset = lastSeg->fSections[firstLinkEditSectionIndex]->fFileOffset; + uint64_t address = lastSeg->fSections[firstLinkEditSectionIndex]->getBaseAddress(); + for (unsigned int i=firstLinkEditSectionIndex; i < sectionCount; ++i) { + std::vector& atoms = lastSeg->fSections[i]->fAtoms; + const unsigned int atomCount = atoms.size(); + uint64_t sectionOffset = 0; + lastSeg->fSections[i]->fFileOffset = fileOffset; + lastSeg->fSections[i]->setBaseAddress(address); + for (unsigned int j=0; j < atomCount; ++j) { + ObjectFile::Atom* atom = atoms[j]; + uint64_t alignment = 1 << atom->getAlignment(); + sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); + atom->setSectionOffset(sectionOffset); + sectionOffset += atom->getSize(); + } + lastSeg->fSections[i]->fSize = sectionOffset; + fileOffset += sectionOffset; + address += sectionOffset; + } + if ( fOptions.outputKind() == Options::kObjectFile ) { + + //lastSeg->fBaseAddress = 0; + //lastSeg->fSize = lastSeg->fSections[firstLinkEditSectionIndex]-> + //lastSeg->fFileOffset = 0; + //lastSeg->fFileSize = + } + else { + lastSeg->fFileSize = fileOffset - lastSeg->fFileOffset; + lastSeg->fSize = address - lastSeg->fBaseAddress; + } +} + + +ObjectFile::Atom::Scope MachHeaderAtom::getScope() const +{ + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + return ObjectFile::Atom::scopeGlobal; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + case Options::kObjectFile: + return ObjectFile::Atom::scopeLinkageUnit; + } + throw "unknown header type"; +} + +bool MachHeaderAtom::dontStripName() const +{ + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + return true; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + case Options::kObjectFile: + return false; + } + throw "unknown header type"; +} + +const char* MachHeaderAtom::getName() const +{ + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + return "__mh_execute_header"; + case Options::kDynamicLibrary: + return "__mh_dylib_header"; + case Options::kDynamicBundle: + return "__mh_bundle_header"; + case Options::kObjectFile: + return NULL; + case Options::kDyld: + return "__mh_dylinker_header"; + } + throw "unknown header type"; +} + +const char* MachHeaderAtom::getDisplayName() const +{ + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + return this->getName(); + case Options::kObjectFile: + return "mach header"; + } + throw "unknown header type"; +} + +uint64_t MachHeaderAtom::getSize() const +{ + return macho_header::size; +} + +void MachHeaderAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + macho_header mh; + + // get file type + uint32_t fileType = 0; + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + fileType = MH_EXECUTE; + break; + case Options::kDynamicLibrary: + fileType = MH_DYLIB; + break; + case Options::kDynamicBundle: + fileType = MH_BUNDLE; + break; + case Options::kObjectFile: + fileType = MH_OBJECT; + break; + case Options::kDyld: + fileType = MH_DYLINKER; + break; + } + + // get flags + uint32_t flags = 0; + if ( fWriter.fOptions.outputKind() == Options::kObjectFile ) { + flags = MH_SUBSECTIONS_VIA_SYMBOLS; + } + else { + flags = MH_DYLDLINK; + if ( fWriter.fOptions.bindAtLoad() ) + flags |= MH_BINDATLOAD; + switch ( fWriter.fOptions.nameSpace() ) { + case Options::kTwoLevelNameSpace: + flags |= MH_TWOLEVEL | MH_NOUNDEFS; + break; + case Options::kFlatNameSpace: + break; + case Options::kForceFlatNameSpace: + flags |= MH_FORCE_FLAT; + break; + } + if ( fWriter.fHasWeakExports ) + flags |= MH_WEAK_DEFINES; + if ( fWriter.fReferencesWeakImports || fWriter.fHasWeakExports ) + flags |= MH_BINDS_TO_WEAK; + } + + // get commands info + uint32_t commandsSize = 0; + uint32_t commandsCount = 0; + + std::vector& loadCommandAtoms = fWriter.fLoadCommandsSection->fAtoms; + const unsigned int atomCount = loadCommandAtoms.size(); + for (unsigned int i=0; i < atomCount; ++i) { + ObjectFile::Atom* atom = loadCommandAtoms[i]; + commandsSize += atom->getSize(); + // segment and symbol table atoms can contain more than one load command + if ( atom == fWriter.fSegmentCommands ) + commandsCount += fWriter.fSegmentCommands->commandCount(); + else if ( atom == fWriter.fSymbolTableCommands ) + commandsCount += 2; + else + ++commandsCount; + } + + // fill out mach_header + mh.set_magic(macho_header::magic_value); + mh.set_cputype(fWriter.fOptions.architecture()); + mh.set_cpusubtype(0); + mh.set_filetype(fileType); + mh.set_ncmds(commandsCount); + mh.set_sizeofcmds(commandsSize); + mh.set_flags(flags); + mh.set_reserved(); + + // write it + writer.write(0, &mh, macho_header::size); +} + + +CustomStackAtom::CustomStackAtom(Writer& writer) + : WriterAtom(writer, fgStackSegment) +{ +#if defined(ARCH_PPC) || defined(ARCH_PPC64) || defined(ARCH_I386) + // stack grows down for these architectures + fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr() - writer.fOptions.customStackSize()); +#else + #error unknown architecture +#endif +} + + +void SegmentLoadCommandsAtom::computeSize() +{ + uint64_t size = 0; + std::vector& segmentInfos = fWriter.fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + size += macho_segment_command::size; + std::vector& sectionInfos = segmentInfos[i]->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + if ( fWriter.fEmitVirtualSections || ! sectionInfos[j]->fVirtualSection ) + size += macho_section::content_size; + } + } + fSize = size; + fCommandCount = segCount; +} + + + +void SegmentLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + uint64_t size = this->getSize(); + uint8_t buffer[size]; + const bool oneSegment =( fWriter.fOptions.outputKind() == Options::kObjectFile ); + bzero(buffer, fSize); + uint8_t* p = buffer; + std::vector& segmentInfos = fWriter.fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + Writer::SegmentInfo* segInfo = segmentInfos[i]; + const int sectionCount = segInfo->fSections.size(); + macho_segment_command* cmd = (macho_segment_command*)p; + cmd->set_cmd(macho_segment_command::command); + cmd->set_segname(segInfo->fName); + cmd->set_vmaddr(segInfo->fBaseAddress); + cmd->set_vmsize(segInfo->fSize); + cmd->set_fileoff(segInfo->fFileOffset); + cmd->set_filesize(segInfo->fFileSize); + cmd->set_maxprot(segInfo->fMaxProtection); + cmd->set_initprot(segInfo->fInitProtection); + // add sections array + macho_section* const sections = (macho_section*)&p[macho_segment_command::size]; + unsigned int sectionsEmitted = 0; + for (int j=0; j < sectionCount; ++j) { + Writer::SectionInfo* sectInfo = segInfo->fSections[j]; + if ( fWriter.fEmitVirtualSections || !sectInfo->fVirtualSection ) { + macho_section* sect = §ions[sectionsEmitted++]; + if ( oneSegment ) { + // .o files have weird segment range + if ( sectionsEmitted == 1 ) { + cmd->set_vmaddr(sectInfo->getBaseAddress()); + cmd->set_fileoff(sectInfo->fFileOffset); + cmd->set_filesize(segInfo->fFileSize-sectInfo->fFileOffset); + } + cmd->set_vmsize(sectInfo->getBaseAddress() + sectInfo->fSize); + } + sect->set_sectname(sectInfo->fSectionName); + sect->set_segname(sectInfo->fSegmentName); + sect->set_addr(sectInfo->getBaseAddress()); + sect->set_size(sectInfo->fSize); + sect->set_offset(sectInfo->fFileOffset); + sect->set_align(sectInfo->fAlignment); + if ( sectInfo->fRelocCount != 0 ) { + sect->set_reloff(sectInfo->fRelocOffset * macho_relocation_info::size + fWriter.fLocalRelocationsAtom->getFileOffset()); + sect->set_nreloc(sectInfo->fRelocCount); + } + if ( sectInfo->fAllZeroFill ) { + sect->set_flags(S_ZEROFILL); + } + else if ( sectInfo->fAllLazyPointers ) { + sect->set_flags(S_LAZY_SYMBOL_POINTERS); + sect->set_reserved1(sectInfo->fIndirectSymbolOffset); + } + else if ( sectInfo->fAllNonLazyPointers ) { + sect->set_flags(S_NON_LAZY_SYMBOL_POINTERS); + sect->set_reserved1(sectInfo->fIndirectSymbolOffset); + } + else if ( (strcmp(sectInfo->fSectionName, "__mod_init_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { + sect->set_flags(S_MOD_INIT_FUNC_POINTERS); + } + else if ( (strcmp(sectInfo->fSectionName, "__mod_term_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { + sect->set_flags(S_MOD_TERM_FUNC_POINTERS); + } + } + } + p = &p[macho_segment_command::size + sectionsEmitted*macho_section::content_size]; + cmd->set_cmdsize(macho_segment_command::size + sectionsEmitted*macho_section::content_size); + cmd->set_nsects(sectionsEmitted); + } + writer.write(0, buffer, size); +} + + +SymbolTableLoadCommandsAtom::SymbolTableLoadCommandsAtom(Writer& writer) + : WriterAtom(writer, fgTextSegment) +{ + bzero(&fSymbolTable, macho_symtab_command::size); + bzero(&fDynamicSymbolTable, macho_dysymtab_command::size); + writer.fSymbolTableCommands = this; +} + +uint64_t SymbolTableLoadCommandsAtom::getSize() const +{ + return macho_symtab_command::size + macho_dysymtab_command::size; +} + +void SymbolTableLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + // build LC_DYSYMTAB command + macho_symtab_command symbolTableCmd; + bzero(&symbolTableCmd, macho_symtab_command::size); + symbolTableCmd.set_cmd(LC_SYMTAB); + symbolTableCmd.set_cmdsize(macho_symtab_command::size); + symbolTableCmd.set_nsyms(fWriter.fSymbolTableCount); + symbolTableCmd.set_symoff(fWriter.fSymbolTableAtom->getFileOffset()); + symbolTableCmd.set_stroff(fWriter.fStringsAtom->getFileOffset()); + symbolTableCmd.set_strsize(fWriter.fStringsAtom->getSize()); + writer.write(0, &symbolTableCmd, macho_symtab_command::size); + + // build LC_DYSYMTAB command + macho_dysymtab_command dynamicSymbolTableCmd; + bzero(&dynamicSymbolTableCmd, macho_dysymtab_command::size); + dynamicSymbolTableCmd.set_cmd(LC_DYSYMTAB); + dynamicSymbolTableCmd.set_cmdsize(macho_dysymtab_command::size); + dynamicSymbolTableCmd.set_ilocalsym(fWriter.fSymbolTableStabsStartIndex); + dynamicSymbolTableCmd.set_nlocalsym(fWriter.fSymbolTableStabsCount + fWriter.fSymbolTableLocalCount); + dynamicSymbolTableCmd.set_iextdefsym(fWriter.fSymbolTableExportStartIndex); + dynamicSymbolTableCmd.set_nextdefsym(fWriter.fSymbolTableExportCount); + dynamicSymbolTableCmd.set_iundefsym(fWriter.fSymbolTableImportStartIndex); + dynamicSymbolTableCmd.set_nundefsym(fWriter.fSymbolTableImportCount); + dynamicSymbolTableCmd.set_indirectsymoff(fWriter.fIndirectTableAtom->getFileOffset()); + dynamicSymbolTableCmd.set_nindirectsyms(fWriter.fIndirectSymbolTable.size()); + if ( fWriter.fOptions.outputKind() != Options::kObjectFile ) { + dynamicSymbolTableCmd.set_extreloff((fWriter.fExternalRelocs.size()==0) ? 0 : fWriter.fExternalRelocationsAtom->getFileOffset()); + dynamicSymbolTableCmd.set_nextrel(fWriter.fExternalRelocs.size()); + dynamicSymbolTableCmd.set_locreloff((fWriter.fInternalRelocs.size()==0) ? 0 : fWriter.fLocalRelocationsAtom->getFileOffset()); + dynamicSymbolTableCmd.set_nlocrel(fWriter.fInternalRelocs.size()); + } + writer.write(macho_symtab_command::size, &dynamicSymbolTableCmd, macho_dysymtab_command::size); +} + +uint64_t DyldLoadCommandsAtom::getSize() const +{ + uint32_t len = macho_dylinker_command::name_offset + strlen("/usr/lib/dyld"); + len = (len+7) & (-8); // 8-byte align + return len; +} + +void DyldLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + uint64_t size = this->getSize(); + uint8_t buffer[size]; + macho_dylinker_command* cmd = (macho_dylinker_command*)buffer; + if ( fWriter.fOptions.outputKind() == Options::kDyld ) + cmd->set_cmd(LC_ID_DYLINKER); + else + cmd->set_cmd(LC_LOAD_DYLINKER); + cmd->set_cmdsize(this->getSize()); + cmd->set_name_offset(); + strcpy((char*)&buffer[macho_dylinker_command::name_offset], "/usr/lib/dyld"); + writer.write(0, buffer, size); +} + + + +uint64_t DylibLoadCommandsAtom::getSize() const +{ + const char* path = fInfo.reader->getInstallPath(); + if ( fInfo.options.fInstallPathOverride != NULL ) + path = fInfo.options.fInstallPathOverride; + uint32_t len = macho_dylib_command::name_offset + strlen(path); + len = (len+7) & (-8); // 8-byte align + return len; +} + +void DylibLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + uint64_t size = this->getSize(); + uint8_t buffer[size]; + bzero(buffer, size); + const char* path = fInfo.reader->getInstallPath(); + if ( fInfo.options.fInstallPathOverride != NULL ) + path = fInfo.options.fInstallPathOverride; + macho_dylib_command* cmd = (macho_dylib_command*)buffer; + if ( fInfo.options.fWeakImport ) + cmd->set_cmd(LC_LOAD_WEAK_DYLIB); + else + cmd->set_cmd(LC_LOAD_DYLIB); + cmd->set_cmdsize(this->getSize()); + cmd->set_timestamp(fInfo.reader->getTimestamp()); + cmd->set_current_version(fInfo.reader->getCurrentVersion()); + cmd->set_compatibility_version(fInfo.reader->getCompatibilityVersion()); + cmd->set_name_offset(); + strcpy((char*)&buffer[macho_dylib_command::name_offset], path); + writer.write(0, buffer, size); +} + + + +uint64_t DylibIDLoadCommandsAtom::getSize() const +{ + uint32_t len = macho_dylib_command::name_offset + strlen(fWriter.fOptions.installPath()); + len = (len+7) & (-8); // 8-byte align + return len; +} + +void DylibIDLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + struct timeval currentTime = { 0 , 0 }; + gettimeofday(¤tTime, NULL); + time_t timestamp = currentTime.tv_sec; + uint64_t size = this->getSize(); + uint8_t buffer[size]; + bzero(buffer, size); + macho_dylib_command* cmd = (macho_dylib_command*)buffer; + cmd->set_cmd(LC_ID_DYLIB); + cmd->set_cmdsize(this->getSize()); + cmd->set_name_offset(); + cmd->set_timestamp(timestamp); + cmd->set_current_version(fWriter.fOptions.currentVersion()); + cmd->set_compatibility_version(fWriter.fOptions.compatibilityVersion()); + strcpy((char*)&buffer[macho_dylib_command::name_offset], fWriter.fOptions.installPath()); + writer.write(0, buffer, size); +} + + +uint64_t RoutinesLoadCommandsAtom::getSize() const +{ + return macho_routines_command::size; +} + +void RoutinesLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + uint64_t initAddr = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + uint8_t buffer[macho_routines_command::size]; + bzero(buffer, macho_routines_command::size); + macho_routines_command* cmd = (macho_routines_command*)buffer; + cmd->set_cmd(macho_routines_command::command); + cmd->set_cmdsize(this->getSize()); + cmd->set_init_address(initAddr); + writer.write(0, buffer, macho_routines_command::size); +} + + +uint64_t SubUmbrellaLoadCommandsAtom::getSize() const +{ + uint32_t len = macho_sub_umbrella_command::name_offset + strlen(fName); + len = (len+7) & (-8); // 8-byte align + return len; +} + +void SubUmbrellaLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + uint64_t size = this->getSize(); + uint8_t buffer[size]; + bzero(buffer, size); + macho_sub_umbrella_command* cmd = (macho_sub_umbrella_command*)buffer; + cmd->set_cmd(LC_SUB_UMBRELLA); + cmd->set_cmdsize(this->getSize()); + cmd->set_name_offset(); + strcpy((char*)&buffer[macho_sub_umbrella_command::name_offset], fName); + writer.write(0, buffer, size); +} + + +uint64_t SubLibraryLoadCommandsAtom::getSize() const +{ + uint32_t len = macho_sub_library_command::name_offset + fNameLength + 1; + len = (len+7) & (-8); // 8-byte align + return len; +} + +void SubLibraryLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + uint64_t size = this->getSize(); + uint8_t buffer[size]; + bzero(buffer, size); + macho_sub_library_command* cmd = (macho_sub_library_command*)buffer; + cmd->set_cmd(LC_SUB_LIBRARY); + cmd->set_cmdsize(this->getSize()); + cmd->set_name_offset(); + strncpy((char*)&buffer[macho_sub_library_command::name_offset], fNameStart, fNameLength); + buffer[macho_sub_library_command::name_offset+fNameLength] = '\0'; + writer.write(0, buffer, size); +} + +uint64_t UmbrellaLoadCommandsAtom::getSize() const +{ + uint32_t len = macho_sub_framework_command::name_offset + strlen(fName); + len = (len+7) & (-8); // 8-byte align + return len; +} + +void UmbrellaLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + uint64_t size = this->getSize(); + uint8_t buffer[size]; + bzero(buffer, size); + macho_sub_framework_command* cmd = (macho_sub_framework_command*)buffer; + cmd->set_cmd(LC_SUB_FRAMEWORK); + cmd->set_cmdsize(this->getSize()); + cmd->set_name_offset(); + strcpy((char*)&buffer[macho_sub_framework_command::name_offset], fName); + writer.write(0, buffer, size); +} + +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ +#if defined(ARCH_PPC) + uint32_t stateSize = 40; // PPC_THREAD_STATE_COUNT; +#elif defined(ARCH_PPC64) + uint32_t stateSize = 76; // PPC_THREAD_STATE64_COUNT; +#elif defined(ARCH_I386) + uint32_t stateSize = 16; // i386_THREAD_STATE_COUNT; +#else + #error unknown architecture +#endif + return macho_thread_command::size + stateSize*4; +} + +void ThreadsLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + uint64_t size = this->getSize(); + uint8_t buffer[size]; + uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, size); + macho_thread_command* cmd = (macho_thread_command*)buffer; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(size); +#if defined(ARCH_PPC) + cmd->set_flavor(1); // PPC_THREAD_STATE + cmd->set_count(40); // PPC_THREAD_STATE_COUNT; + cmd->set_threadState32(0, start); + if ( fWriter.fOptions.hasCustomStack() ) + cmd->set_threadState32(3, fWriter.fOptions.customStackAddr()); // r1 +#elif defined(ARCH_PPC64) + cmd->set_flavor(5); // PPC_THREAD_STATE64 + cmd->set_count(76); // PPC_THREAD_STATE64_COUNT; + cmd->set_threadState64(0, start); + if ( fWriter.fOptions.hasCustomStack() ) + cmd->set_threadState64(6, fWriter.fOptions.customStackAddr()); // r1 +#elif defined(ARCH_I386) + cmd->set_flavor(0xFFFFFFFF); // i386_THREAD_STATE + cmd->set_count(16); // i386_THREAD_STATE_COUNT; + cmd->set_threadState32(0, start); + if ( fWriter.fOptions.hasCustomStack() ) + cmd->set_threadState32(15, fWriter.fOptions.customStackAddr()); // uesp +#else + #error unknown architecture +#endif + writer.write(0, buffer, size); +} + + + +uint64_t LoadCommandsPaddingAtom::getSize() const +{ + return fSize; +} + +void LoadCommandsPaddingAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + uint8_t buffer[fSize]; + bzero(buffer, fSize); + writer.write(0, buffer, fSize); +} + + +uint64_t LinkEditAtom::getFileOffset() const +{ + return ((Writer::SectionInfo*)this->getSection())->fFileOffset + this->getSectionOffset(); +} + + +uint64_t LocalRelocationsLinkEditAtom::getSize() const +{ + return fWriter.fInternalRelocs.size() * macho_relocation_info::size; +} + +void LocalRelocationsLinkEditAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + writer.write(0, &fWriter.fInternalRelocs[0], this->getSize()); +} + + + +uint64_t SymbolTableLinkEditAtom::getSize() const +{ + return fWriter.fSymbolTableCount * macho_nlist::size; +} + +void SymbolTableLinkEditAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + writer.write(0, fWriter.fSymbolTable, this->getSize()); +} + +uint64_t ExternalRelocationsLinkEditAtom::getSize() const +{ + return fWriter.fExternalRelocs.size() * macho_relocation_info::size; +} + +void ExternalRelocationsLinkEditAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + writer.write(0, &fWriter.fExternalRelocs[0], this->getSize()); +} + + + +uint64_t IndirectTableLinkEditAtom::getSize() const +{ + return fWriter.fIndirectSymbolTable.size() * sizeof(uint32_t); +} + +void IndirectTableLinkEditAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + uint64_t size = this->getSize(); + uint8_t buffer[size]; + bzero(buffer, size); + const uint32_t indirectTableSize = fWriter.fIndirectSymbolTable.size(); + uint32_t* indirectTable = (uint32_t*)buffer; + for (uint32_t i=0; i < indirectTableSize; ++i) { + Writer::IndirectEntry& entry = fWriter.fIndirectSymbolTable[i]; + if ( entry.indirectIndex < indirectTableSize ) { + ENDIAN_WRITE32(indirectTable[entry.indirectIndex], entry.symbolIndex); + } + else { + throw "malformed indirect table"; + } + } + writer.write(0, buffer, size); +} + + + +StringsLinkEditAtom::StringsLinkEditAtom(Writer& writer) + : LinkEditAtom(writer), fCurrentBuffer(NULL), fCurrentBufferUsed(0) +{ + fCurrentBuffer = new char[kBufferSize]; + // burn first byte of string pool (so zero is never a valid string offset) + fCurrentBuffer[fCurrentBufferUsed++] = ' '; + // make offset 1 always point to an empty string + fCurrentBuffer[fCurrentBufferUsed++] = '\0'; +} + +uint64_t StringsLinkEditAtom::getSize() const +{ + return kBufferSize * fFullBuffers.size() + fCurrentBufferUsed; +} + +void StringsLinkEditAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + uint64_t offset = 0; + for (unsigned int i=0; i < fFullBuffers.size(); ++i) { + writer.write(offset, fFullBuffers[i], kBufferSize); + offset += kBufferSize; + } + writer.write(offset, fCurrentBuffer, fCurrentBufferUsed); +} + +int32_t StringsLinkEditAtom::add(const char* name) +{ + int lenNeeded = strlen(name)+1; + while ( lenNeeded + fCurrentBufferUsed >= kBufferSize ) { + // first part of string fits in current buffer + int firstLen = kBufferSize - fCurrentBufferUsed; + memcpy(&fCurrentBuffer[fCurrentBufferUsed], name, firstLen); + // alloc next buffer + fFullBuffers.push_back(fCurrentBuffer); + fCurrentBuffer = new char[kBufferSize]; + fCurrentBufferUsed = 0; + // advance name to second part + name += firstLen; + lenNeeded -= firstLen; + } + //fprintf(stderr, "StringsLinkEditAtom::add(): lenNeeded=%d, fCurrentBuffer=%d, fCurrentBufferUsed=%d\n", lenNeeded, fCurrentBuffer, fCurrentBufferUsed); + // string all fits in current buffer + strcpy(&fCurrentBuffer[fCurrentBufferUsed], name); + int32_t offset = kBufferSize * fFullBuffers.size() + fCurrentBufferUsed; + fCurrentBufferUsed += lenNeeded; + return offset; +} + +// returns the index of an empty string +int32_t StringsLinkEditAtom::emptyString() +{ + return 1; +} + + + +}; + + + diff --git a/src/ld.cpp b/src/ld.cpp new file mode 100644 index 0000000..7b05e32 --- /dev/null +++ b/src/ld.cpp @@ -0,0 +1,1304 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "Options.h" + +#include "ObjectFile.h" +#include "ObjectFileMachO-all.h" + +#include "ExecutableFile.h" +#include "ExecutableFileMachO-all.h" + +#include "SectCreate.h" + +#if 0 +static void dumpAtom(ObjectFile::Atom* atom) +{ + //printf("atom: %p\n", atom); + + // name + printf("name: %s\n", atom->getDisplayName()); + + // scope + switch ( atom->getScope() ) { + case ObjectFile::Atom::scopeTranslationUnit: + printf("scope: translation unit\n"); + break; + case ObjectFile::Atom::scopeLinkageUnit: + printf("scope: linkage unit\n"); + break; + case ObjectFile::Atom::scopeGlobal: + printf("scope: global\n"); + break; + default: + printf("scope: unknown\n"); + } + + // segment and section + printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName()); + + // attributes + printf("attrs: "); + if ( atom->isTentativekDefinition() ) + printf("tentative "); + else if ( atom->isWeakDefinition() ) + printf("weak "); + if ( atom->isCoalesableByName() ) + printf("coalesce-by-name "); + if ( atom->isCoalesableByValue() ) + printf("coalesce-by-value "); + if ( atom->dontDeadStrip() ) + printf("dont-dead-strip "); + if ( atom->isZeroFill() ) + printf("zero-fill "); + printf("\n"); + + // size + printf("size: 0x%012llX\n", atom->getSize()); + + // content + uint8_t content[atom->getSize()]; + atom->copyRawContent(content); + printf("content: "); + if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) { + printf("\"%s\"", content); + } + else { + for (unsigned int i=0; i < sizeof(content); ++i) + printf("%02X ", content[i]); + } + printf("\n"); + + // references + std::vector& references = atom->getReferences(); + const int refCount = references.size(); + printf("references: (%u)\n", refCount); + for (int i=0; i < refCount; ++i) { + ObjectFile::Reference* ref = references[i]; + printf(" %s\n", ref->getDescription()); + } + + // attributes + +} + +#endif + +class CStringComparor +{ +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) < 0); } +}; + +class CStringEquals +{ +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; + +class Section : public ObjectFile::Section +{ +public: + static Section* find(const char* sectionName, const char* segmentName, bool zeroFill); + static void assignIndexes(); + +private: + Section(const char* sectionName, const char* segmentName, bool zeroFill); + + struct Sorter { + static int segmentOrdinal(const char* segName); + bool operator()(Section* left, Section* right); + }; + + typedef __gnu_cxx::hash_map, CStringEquals> NameToSection; + //typedef std::map NameToSection; + + const char* fSectionName; + const char* fSegmentName; + bool fZeroFill; + + static NameToSection fgMapping; + static std::vector fgSections; +}; + +Section::NameToSection Section::fgMapping; +std::vector Section::fgSections; + +Section::Section(const char* sectionName, const char* segmentName, bool zeroFill) + : fSectionName(sectionName), fSegmentName(segmentName), fZeroFill(zeroFill) +{ + //fprintf(stderr, "new Section(%s, %s)\n", sectionName, segmentName); +} + +Section* Section::find(const char* sectionName, const char* segmentName, bool zeroFill) +{ +#if 0 + std::pair range = fgMapping.equal_range(sectionName); + for (NameToSection::iterator it=range.first; it != range.second; it++) { + if ( strcmp(it->second->fSegmentName, segmentName) == 0 ) + return it->second; + } +#endif + NameToSection::iterator pos = fgMapping.find(sectionName); + if ( pos != fgMapping.end() ) { + if ( strcmp(pos->second->fSegmentName, segmentName) == 0 ) + return pos->second; + // otherwise same section name is used in different segments, look slow way + for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { + if ( (strcmp((*it)->fSectionName, sectionName) == 0) && (strcmp((*it)->fSegmentName, segmentName) == 0) ) + return *it; + } + } + + // does not exist, so make a new one + Section* sect = new Section(sectionName, segmentName, zeroFill); + sect->fIndex = fgMapping.size(); + fgMapping[sectionName] = sect; + fgSections.push_back(sect); + return sect; +} + +int Section::Sorter::segmentOrdinal(const char* segName) +{ + if ( strcmp(segName, "__PAGEZERO") == 0 ) + return 1; + if ( strcmp(segName, "__TEXT") == 0 ) + return 2; + if ( strcmp(segName, "__DATA") == 0 ) + return 3; + if ( strcmp(segName, "__OBJC") == 0 ) + return 4; + if ( strcmp(segName, "__LINKEDIT") == 0 ) + return INT_MAX; // linkedit segment should always sort last + else + return 5; +} + + +bool Section::Sorter::operator()(Section* left, Section* right) +{ + // Segment is primary sort key + const char* leftSegName = left->fSegmentName; + const char* rightSegName = right->fSegmentName; + int segNameCmp = strcmp(leftSegName, rightSegName); + if ( segNameCmp != 0 ) + { + int leftSegOrdinal = segmentOrdinal(leftSegName); + int rightSegOrdinal = segmentOrdinal(rightSegName); + if ( leftSegOrdinal < rightSegOrdinal ) + return true; + if ( leftSegOrdinal == rightSegOrdinal ) + return segNameCmp < 0; + return false; + } + + // zerofill section sort to the end + if ( !left->fZeroFill && right->fZeroFill ) + return true; + if ( left->fZeroFill && !right->fZeroFill ) + return false; + + // section discovery order is last sort key + return left->fIndex < right->fIndex; +} + +void Section::assignIndexes() +{ + //printf("unsorted:\n"); + //for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { + // printf("section: name=%s, segment: name=%s, discovery order=%d\n", (*it)->fSectionName, (*it)->fSegmentName, (*it)->fIndex); + //} + + // sort it + std::sort(fgSections.begin(), fgSections.end(), Section::Sorter()); + + // assign correct section ordering to each Section object + unsigned int newOrder = 1; + for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) + (*it)->fIndex = newOrder++; + + //printf("sorted:\n"); + //for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { + // printf("section: name=%s\n", (*it)->fSectionName); + //} +} + +class Linker { +public: + Linker(int argc, const char* argv[]); + + void createReaders(); + void createWriter(); + void addInputFile(ObjectFile::Reader* reader); + void setOutputFile(ExecutableFile::Writer* writer); + void link(); + + +private: + ObjectFile::Reader* createReader(const Options::FileInfo&); + void addAtom(ObjectFile::Atom& atom); + void addAtoms(std::vector& atoms); + void buildAtomList(); + void loadUndefines(); + void addWeakAtomOverrides(); + void resolveReferences(); + void deadStrip(); + void sortAtoms(); + void tweakLayout(); + void writeOutput(); + + void resolve(ObjectFile::Reference* reference); + void addJustInTimeAtoms(const char* name); + + void addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info); + void addIndirectLibraries(ObjectFile::Reader* reader); + bool haveIndirectLibrary(const char* path, ObjectFile::Reader* reader); + bool haveDirectLibrary(const char* path); + + struct SegmentAndItsAtoms + { + class Segment* fSegment; + uint64_t fSegmentSize; + uint64_t fSegmentBaseAddress; + std::vector fAtoms; + }; + + + class SymbolTable + { + public: + SymbolTable(Linker&); + void require(const char* name); + bool add(ObjectFile::Atom& atom); + ObjectFile::Atom* find(const char* name); + unsigned int getRequireCount() { return fRequireCount; } + void getNeededNames(bool andWeakDefintions, std::vector& undefines); + private: + typedef std::map Mapper; + Linker& fOwner; + Mapper fTable; + unsigned int fRequireCount; + }; + + struct AtomSorter + { + bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right); + }; + + typedef std::map SectionOrder; + + struct IndirectLibrary { + const char* path; + uint64_t fileLen; + ObjectFile::Reader* reader; + std::set parents; + ObjectFile::Reader* reExportParent; + }; + + Options fOptions; + SymbolTable fGlobalSymbolTable; + unsigned int fWeakSymbolsAddedCount; + std::vector fInputFiles; + ExecutableFile::Writer* fOutputFile; + std::vector fDynamicLibraries; + std::list fIndirectDynamicLibraries; + std::vector fAllAtoms; + std::vector< SegmentAndItsAtoms > fAllAtomsBySegment; + std::set fDeadAtoms; + SectionOrder fSectionOrder; + unsigned int fNextSortOrder; + bool fDirectLibrariesComplete; +}; + + + +Linker::Linker(int argc, const char* argv[]) + : fOptions(argc, argv), fGlobalSymbolTable(*this), fOutputFile(NULL), fNextSortOrder(1), fDirectLibrariesComplete(false) +{ +} + +void Linker::addInputFile(ObjectFile::Reader* reader) +{ + fInputFiles.push_back(reader); +} + +void Linker::setOutputFile(ExecutableFile::Writer* writer) +{ + fOutputFile = writer; +} + +void Linker::link() +{ + this->buildAtomList(); + this->loadUndefines(); + this->resolveReferences(); + this->deadStrip(); + this->sortAtoms(); + this->tweakLayout(); + this->writeOutput(); +} + +inline void Linker::addAtom(ObjectFile::Atom& atom) +{ + // add to list of all atoms + fAllAtoms.push_back(&atom); + + // add atom's references's names to symbol table as to-be-resolved-later + std::vector& references = atom.getReferences(); + for (std::vector::iterator it=references.begin(); it != references.end(); it++) { + ObjectFile::Reference* reference = *it; + if ( reference->isUnbound() ) { + fGlobalSymbolTable.require(reference->getTargetName()); + } + } + + // if in global namespace, add atom itself to symbol table + ObjectFile::Atom::Scope scope = atom.getScope(); + const char* name = atom.getName(); + if ( (scope != ObjectFile::Atom::scopeTranslationUnit) && (name != NULL) ) { + fGlobalSymbolTable.add(atom); + + // update scope based on export list (possible that globals are downgraded to private_extern) + if ( (scope == ObjectFile::Atom::scopeGlobal) && fOptions.hasExportRestrictList() ) { + bool doExport = fOptions.shouldExport(name); + if ( !doExport ) { + atom.setScope(ObjectFile::Atom::scopeLinkageUnit); + } + } + } + + // record section orders so output file can have same order + atom.setSection(Section::find(atom.getSectionName(), atom.getSegment().getName(), atom.isZeroFill())); + + // assign order in which this atom was originally seen + if ( atom.getSortOrder() == 0 ) + fNextSortOrder = atom.setSortOrder(fNextSortOrder); +} + +inline void Linker::addAtoms(std::vector& atoms) +{ + for (std::vector::iterator it=atoms.begin(); it != atoms.end(); it++) { + this->addAtom(**it); + } +} + +void Linker::buildAtomList() +{ + // add initial undefines from -u option + std::vector& initialUndefines = fOptions.initialUndefines(); + for (std::vector::iterator it=initialUndefines.begin(); it != initialUndefines.end(); it++) { + fGlobalSymbolTable.require(*it); + } + + // writer can contribute atoms + this->addAtoms(fOutputFile->getAtoms()); + + // each reader contributes atoms + const int readerCount = fInputFiles.size(); + for (int i=0; i < readerCount; ++i) { + this->addAtoms(fInputFiles[i]->getAtoms()); + } + + // extra command line section always at end + std::vector& extraSections = fOptions.extraSections(); + for( std::vector::iterator it=extraSections.begin(); it != extraSections.end(); ++it) { + this->addAtoms(SectCreate::MakeReader(it->segmentName, it->sectionName, it->path, it->data, it->dataLen)->getAtoms()); + } +} + +void Linker::loadUndefines() +{ + // keep looping until no more undefines were added in last loop + unsigned int undefineCount = 0xFFFFFFFF; + while ( undefineCount != fGlobalSymbolTable.getRequireCount() ) { + undefineCount = fGlobalSymbolTable.getRequireCount(); + std::vector undefineNames; + fGlobalSymbolTable.getNeededNames(true, undefineNames); + const int undefineCount = undefineNames.size(); + for (int i=0; i < undefineCount; ++i) { + const char* name = undefineNames[i]; + ObjectFile::Atom* possibleAtom = fGlobalSymbolTable.find(name); + if ( (possibleAtom == NULL) || (possibleAtom->isWeakDefinition() && (fOptions.outputKind() != Options::kObjectFile)) ) + this->addJustInTimeAtoms(name); + } + } + + if ( fOptions.outputKind() != Options::kObjectFile ) { + // error out on any remaining undefines + bool doPrint = true; + bool doError = true; + switch ( fOptions.undefinedTreatment() ) { + case Options::kUndefinedError: + break; + case Options::kUndefinedDynamicLookup: + doError = false; + break; + case Options::kUndefinedWarning: + doError = false; + break; + case Options::kUndefinedSuppress: + doError = false; + doPrint = false; + break; + } + std::vector unresolvableUndefines; + fGlobalSymbolTable.getNeededNames(false, unresolvableUndefines); + const int unresolvableCount = unresolvableUndefines.size(); + if ( unresolvableCount != 0 ) { + if ( doPrint ) { + fprintf(stderr, "can't resolve symbols:\n"); + for (int i=0; i < unresolvableCount; ++i) { + const char* name = unresolvableUndefines[i]; + const unsigned int nameLen = strlen(name); + fprintf(stderr, " %s, referenced from:\n", name); + char stubName[nameLen+6]; + strcpy(stubName, name); + strcat(stubName, "$stub"); + char nonLazyName[nameLen+16]; + strcpy(nonLazyName, name); + strcat(nonLazyName, "$non_lazy_ptr"); + ObjectFile::Atom* lastStubAtomWithUnresolved = NULL; + ObjectFile::Atom* lastNonLazyAtomWithUnresolved = NULL; + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* reference = *rit; + if ( reference->isUnbound() ) { + if ( (atom != lastStubAtomWithUnresolved) && (strcmp(reference->getTargetName(), stubName) == 0) ) { + const char* path = atom->getFile()->getPath(); + const char* shortPath = strrchr(path, '/'); + if ( shortPath == NULL ) + shortPath = path; + else + shortPath = &shortPath[1]; + fprintf(stderr, " %s in %s\n", atom->getDisplayName(), shortPath); + lastStubAtomWithUnresolved = atom; + } + else if ( (atom != lastNonLazyAtomWithUnresolved) && (strcmp(reference->getTargetName(), nonLazyName) == 0) ) { + const char* path = atom->getFile()->getPath(); + const char* shortPath = strrchr(path, '/'); + if ( shortPath == NULL ) + shortPath = path; + else + shortPath = &shortPath[1]; + fprintf(stderr, " %s in %s\n", atom->getDisplayName(), shortPath); + lastNonLazyAtomWithUnresolved = atom; + } + } + } + } + } + } + if ( doError ) + throw "symbol(s) not found"; + } + + // now verify that -init routine exists + if ( fOptions.initFunctionName() != NULL ) { + if ( fGlobalSymbolTable.find(fOptions.initFunctionName()) == NULL ) + throwf("symbol %s not found for -init", fOptions.initFunctionName()); + } + } +} + + + +void Linker::addJustInTimeAtoms(const char* name) +{ + // give writer a crack at it + ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name); + if ( atom != NULL ) { + this->addAtom(*atom); + } + else { + // give direct readers a chance + const int readerCount = fInputFiles.size(); + for (int i=0; i < readerCount; ++i) { + // if this reader is a static archive that has the symbol we need, pull in all atoms in that module + // if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us. + std::vector* atoms = fInputFiles[i]->getJustInTimeAtomsFor(name); + if ( atoms != NULL ) { + this->addAtoms(*atoms); + delete atoms; + return; // found a definition, no need to search anymore + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file #%d\n", name, i); + } + } + + // give indirect readers a chance + for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { + ObjectFile::Reader* reader = it->reader; + if ( reader != NULL ) { + std::vector* atoms = reader->getJustInTimeAtomsFor(name); + if ( atoms != NULL ) { + this->addAtoms(*atoms); + delete atoms; + break; + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file #%d\n", name, i); + } + } + } + } +} + +void Linker::resolve(ObjectFile::Reference* reference) +{ + ObjectFile::Atom* target = NULL; + const char* targetName = reference->getTargetName(); + const int targetNameLen = strlen(targetName); + if ( (targetNameLen > 5) && (strcmp(&targetName[targetNameLen-5], "$stub") == 0) ) { + // when looking up "_foo$stub", first look for "_foo" + char nonStubTarget[targetNameLen+1]; + strcpy(nonStubTarget, targetName); + nonStubTarget[targetNameLen-5] = '\0'; + // unless interposing and the symbol is exported + if ( !fOptions.interposable() || !fOptions.shouldExport(nonStubTarget) ) { + target = fGlobalSymbolTable.find(nonStubTarget); + // also need indirection to all exported weak symbols for C++ support + if ( (target != NULL) && !target->isImportProxy() && (!target->isWeakDefinition() || (target->getScope() != ObjectFile::Atom::scopeGlobal)) ) { + reference->setTarget(*target); + // mark stub as no longer being needed + ObjectFile::Atom* stub = fGlobalSymbolTable.find(targetName); + if ( stub != NULL ) { + char lazySymbol[targetNameLen+8]; + strcpy(lazySymbol, nonStubTarget); + strcat(lazySymbol, "$lazy_ptr"); + ObjectFile::Atom* lazyPtr = fGlobalSymbolTable.find(lazySymbol); + fDeadAtoms.insert(stub); + if ( lazyPtr != NULL ) + fDeadAtoms.insert(lazyPtr); + } + return; + } + } + } + + // look in global symbol table + target = fGlobalSymbolTable.find(targetName); + if ( target == NULL ) { + fprintf(stderr, "can't resolve: %s\n", targetName); + } + reference->setTarget(*target); + + // handle weak-imports + if ( target->isImportProxy() ) { + bool mismatch = false; + if ( reference->isWeakReference() ) { + switch(target->getImportWeakness()) { + case ObjectFile::Atom::kWeakUnset: + target->setImportWeakness(true); + break; + case ObjectFile::Atom::kWeakImport: + break; + case ObjectFile::Atom::kNonWeakImport: + mismatch = true; + break; + } + } + else { + switch(target->getImportWeakness()) { + case ObjectFile::Atom::kWeakUnset: + target->setImportWeakness(false); + break; + case ObjectFile::Atom::kWeakImport: + mismatch = true; + break; + case ObjectFile::Atom::kNonWeakImport: + break; + } + } + if ( mismatch ) { + switch ( fOptions.weakReferenceMismatchTreatment() ) { + case Options::kWeakReferenceMismatchError: + throwf("mismatching weak references for symbol: %s", target->getName()); + case Options::kWeakReferenceMismatchWeak: + target->setImportWeakness(true); + break; + case Options::kWeakReferenceMismatchNonWeak: + target->setImportWeakness(false); + break; + } + } + } + + // handle references that have two (from and to) targets + if ( reference->isUnbound() ) { + const char* fromTargetName = reference->getFromTargetName(); + ObjectFile::Atom* fromTarget = fGlobalSymbolTable.find(fromTargetName); + if ( target == NULL ) { + fprintf(stderr, "can't resolve: %s\n", fromTargetName); + } + reference->setFromTarget(*fromTarget); + } +} + + +void Linker::resolveReferences() +{ + // note: the atom list may grow during this loop as libraries supply needed atoms + for (unsigned int j=0; j < fAllAtoms.size(); ++j) { + ObjectFile::Atom* atom = fAllAtoms[j]; + std::vector& references = atom->getReferences(); + for (std::vector::iterator it=references.begin(); it != references.end(); it++) { + ObjectFile::Reference* reference = *it; + if ( reference->isUnbound() ) { + this->resolve(reference); + } + } + } +} + +class InSet +{ +public: + InSet(std::set& deadAtoms) : fDeadAtoms(deadAtoms) {} + + bool operator()(ObjectFile::Atom*& atom) const { + return ( fDeadAtoms.count(atom) != 0 ); + } + +private: + std::set& fDeadAtoms; +}; + + +void Linker::deadStrip() +{ + //printf("Stripping atoms:\n"); + //for (std::set::iterator it=fDeadAtoms.begin(); it != fDeadAtoms.end(); it++) { + // printf("\t%s\n", (*it)->getDisplayName()); + //} + + // for now, just remove atoms weak atoms that have been overridden + fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end()); +} + + + +void Linker::sortAtoms() +{ + Section::assignIndexes(); + std::sort(fAllAtoms.begin(), fAllAtoms.end(), Linker::AtomSorter()); +} + + + +// make sure given addresses are within reach of branches, etc +void Linker::tweakLayout() +{ + + + +} + + +void Linker::writeOutput() +{ + // if main executable, find entry point atom + ObjectFile::Atom* entryPoint; + switch ( fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + case Options::kDyld: + entryPoint = fGlobalSymbolTable.find(fOptions.entryName()); + if ( entryPoint == NULL ) { + throwf("could not find entry point: %s", fOptions.entryName()); + } + break; + case Options::kDynamicLibrary: + if ( fOptions.initFunctionName() != NULL ) { + entryPoint = fGlobalSymbolTable.find(fOptions.initFunctionName()); + if ( entryPoint == NULL ) { + throwf("could not find -init function: %s", fOptions.initFunctionName()); + } + } + break; + default: + entryPoint = NULL; + } + + // tell writer about each segment's atoms + fOutputFile->write(fAllAtoms, entryPoint); +} + + + + +ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) +{ + // map in whole file + uint64_t len = info.fileLen; + int fd = ::open(info.path, O_RDONLY, 0); + if ( fd == -1 ) + throw "can't open file"; + if ( info.fileLen < 20 ) + throw "file too small"; + char* p = (char*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE, fd, 0); + if ( p == (char*)(-1) ) + throw "can't map file"; + ::close(fd); + + // if fat file, skip to architecture we want + const mach_header* mh = (mach_header*)p; + if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + // Fat header is always big-endian + const struct fat_header* fh = (struct fat_header*)p; + const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); + for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fOptions.architecture() ) { + mh = (struct mach_header*)((char*)p + OSSwapBigToHostInt32(archs[i].offset)); + len = OSSwapBigToHostInt32(archs[i].size); + break; + } + } + } + + if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + const char* archName = "unknown"; + switch (fOptions.architecture()) { + case CPU_TYPE_POWERPC: + archName = "ppc"; + break; + case CPU_TYPE_POWERPC64: + archName = "ppc64"; + break; + case CPU_TYPE_I386: + archName = "i386"; + break; + } + throwf("missing required architecture %s in fat file", archName); + } + + // pull out cpu-type and file-type in endian-safe way + cpu_type_t cpuType = 0; + uint32_t fileType = 0; + if ( mh->magic == MH_MAGIC ) { + fileType = mh->filetype; + cpuType = mh->cputype; + } + else if ( mh->magic == OSSwapInt32(MH_MAGIC) ) { + fileType = OSSwapInt32(mh->filetype); + cpuType = OSSwapInt32(mh->cputype); + } + else if ( mh->magic == MH_MAGIC_64 ) { + fileType = ((mach_header_64*)mh)->filetype; + cpuType = ((mach_header_64*)mh)->cputype; + } + else if ( mh->magic == OSSwapInt32(MH_MAGIC_64) ) { + fileType = OSSwapInt32(((mach_header_64*)mh)->filetype); + cpuType = OSSwapInt32(((mach_header_64*)mh)->cputype); + } + else if ( strncmp((const char*)mh, "!\n", 8) == 0 ) { + // is static archive + switch ( fOptions.architecture() ) { + case CPU_TYPE_POWERPC: + return ppc::ObjectFileArchiveMachO::MakeReader((const uint8_t*)mh, len, info.path, fOptions.readerOptions()); + case CPU_TYPE_POWERPC64: + return ppc64::ObjectFileArchiveMachO::MakeReader((const uint8_t*)mh, len, info.path, fOptions.readerOptions()); + case CPU_TYPE_I386: + return i386::ObjectFileArchiveMachO::MakeReader((const uint8_t*)mh, len, info.path, fOptions.readerOptions()); + } + throw "no matching archive reader"; + } + else { + throw "unknown file type"; + } + + // bail out if cpu-type does not match requrired architecture + if ( fOptions.architecture() == cpuType ) { + // make appropriate reader object + if ( fileType == MH_OBJECT ) { + switch ( cpuType ) { + case CPU_TYPE_POWERPC: + return ppc::ObjectFileMachO::MakeReader((class ppc::macho_header*)mh, info.path, fOptions.readerOptions()); + case CPU_TYPE_POWERPC64: + return ppc64::ObjectFileMachO::MakeReader((class ppc64::macho_header*)mh, info.path, fOptions.readerOptions()); + case CPU_TYPE_I386: + return i386::ObjectFileMachO::MakeReader((class i386::macho_header*)mh, info.path, fOptions.readerOptions()); + default: + throw "wrong architecture in object file"; + } + } + else if ( fileType == MH_DYLIB ) { + ObjectFile::Reader* dylibReader = NULL; + switch ( cpuType ) { + case CPU_TYPE_POWERPC: + dylibReader = ppc::ObjectFileDylibMachO::MakeReader((class ppc::macho_header*)mh, info.path, fOptions.readerOptions()); + break; + case CPU_TYPE_POWERPC64: + dylibReader = ppc64::ObjectFileDylibMachO::MakeReader((class ppc64::macho_header*)mh, info.path, fOptions.readerOptions()); + break; + case CPU_TYPE_I386: + dylibReader = i386::ObjectFileDylibMachO::MakeReader((class i386::macho_header*)mh, info.path, fOptions.readerOptions()); + break; + default: + throw "wrong architecture in dylib"; + } + this->addDylib(dylibReader, info); + return dylibReader; + } + throw "unknown mach-o file type"; + } + else { + throw "file does not contain requested architecture"; + } + +} + + +void Linker::createReaders() +{ + std::vector& files = fOptions.getInputFiles(); + const int count = files.size(); + if ( count == 0 ) + throw "no object files specified"; + // add all direct object, archives, and dylibs + for (int i=0; i < count; ++i) { + Options::FileInfo& entry = files[i]; + // ignore /usr/lib/dyld on command line in crt.o build + if ( strcmp(entry.path, "/usr/lib/dyld") != 0 ) { + try { + this->addInputFile(this->createReader(entry)); + } + catch (const char* msg) { + if ( strstr(msg, "architecture") != NULL ) { + if ( fOptions.ignoreOtherArchInputFiles() ) { + // ignore, because this is about an architecture not in use + } + else { + fprintf(stderr, "ld64 warning: in %s, %s\n", entry.path, msg); + } + } + else { + throwf("in %s, %s", entry.path, msg); + } + } + } + } + + // add first level of indirect dylibs + fDirectLibrariesComplete = true; + for (std::vector::iterator it=fDynamicLibraries.begin(); it != fDynamicLibraries.end(); it++) { + this->addIndirectLibraries(it->reader); + } + + // indirect handling depends on namespace + switch ( fOptions.nameSpace() ) { + case Options::kFlatNameSpace: + case Options::kForceFlatNameSpace: + // with flat namespace, blindly load all indirect libraries + // the indirect list will grow as indirect libraries are loaded + for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { + struct stat statBuffer; + if ( stat(it->path, &statBuffer) == 0 ) { + Options::FileInfo info; + info.path = it->path; + info.fileLen = statBuffer.st_size; + info.options.fWeakImport = false; + info.options.fReExport = false; + info.options.fInstallPathOverride = NULL; + it->reader = this->createReader(info); + } + else { + fprintf(stderr, "ld64 warning: indirect library not found: %s\n", it->path); + } + } + break; + + case Options::kTwoLevelNameSpace: + // with two-level namespace we only want to use indirect libraries that are re-exported through a library that is used + { + bool indirectAdded = true; + while ( indirectAdded ) { + indirectAdded = false; + // instantiate a reader for each indirect library and try to find parent that re-exports it + for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { + if ( it->reader == NULL ) { + try { + struct stat statBuffer; + if ( stat(it->path, &statBuffer) != 0 ) + throw "file not found"; + + Options::FileInfo info; + info.path = it->path; + info.fileLen = statBuffer.st_size; + info.options.fWeakImport = false; + info.options.fReExport = false; + info.options.fInstallPathOverride = NULL; + it->reader = this->createReader(info); + indirectAdded = true; + } + catch (const char* msg) { + fprintf(stderr, "ld64 warning: indirect library %s could not be loaded: %s\n", it->path, msg); + } + } + // if an indirect library does not have an assigned parent, look for one + if ( (it->reader != NULL) && (it->reExportParent == NULL) ) { + // ask each parent if they re-export this dylib + for (std::set::iterator pit=it->parents.begin(); pit != it->parents.end(); pit++) { + if ( (*pit)->reExports(it->reader) ) { + it->reExportParent = *pit; + break; + } + } + } + } + } + } + break; + } + + // add relevant indirect libraries to the end of fDynamicLibraries + for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { + if ( (it->reExportParent != NULL) || (fOptions.nameSpace() != Options::kTwoLevelNameSpace) ) { + ExecutableFile::DyLibUsed dylibInfo; + dylibInfo.reader = it->reader; + dylibInfo.options.fWeakImport = false; + dylibInfo.options.fReExport = false; + dylibInfo.options.fInstallPathOverride = NULL; + dylibInfo.indirect = true; + dylibInfo.directReader = it->reExportParent; + fDynamicLibraries.push_back(dylibInfo); + if ( fOptions.readerOptions().fTraceIndirectDylibs ) + printf("[Logging for Build & Integration] Used indirect dynamic library: %s\n", it->path); + } + } +} + + + +void Linker::addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info) +{ + if ( fDirectLibrariesComplete ) { + this->addIndirectLibraries(reader); + } + else { + if ( fOptions.readerOptions().fTraceDylibs ) + printf("[Logging for Build & Integration] Used dynamic library: %s\n", reader->getPath()); + ExecutableFile::DyLibUsed dylibInfo; + dylibInfo.reader = reader; + dylibInfo.options = info.options; + dylibInfo.indirect = false; + dylibInfo.directReader = NULL; + fDynamicLibraries.push_back(dylibInfo); + } +} + + +void Linker::addIndirectLibraries(ObjectFile::Reader* reader) +{ + std::vector* dependentLibs = reader->getDependentLibraryPaths(); + if ( dependentLibs != NULL ) { + for (std::vector::iterator it=dependentLibs->begin(); it != dependentLibs->end(); it++) { + if ( this->haveDirectLibrary(*it) ) { + // do nothing, direct library already exists + } + else if ( this->haveIndirectLibrary(*it, reader) ) { + // side effect of haveIndirectLibrary() added reader to parent list + } + else { + // add to list of indirect libraries + IndirectLibrary indirectLib; + indirectLib.path = *it; + indirectLib.fileLen = 0; + indirectLib.reader = NULL; + indirectLib.parents.insert(reader); + indirectLib.reExportParent = NULL; + fIndirectDynamicLibraries.push_back(indirectLib); + //fprintf(stderr, "add indirect library: %s\n", *it); + } + } + } +} + +bool Linker::haveIndirectLibrary(const char* path, ObjectFile::Reader* parentReader) +{ + for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { + if ( strcmp(path, it->path) == 0 ) { + it->parents.insert(parentReader); + return true; + } + if ( it->reader != NULL ) { + const char* installPath = it->reader->getInstallPath(); + if ( (installPath != NULL) && (strcmp(path, installPath) == 0) ) + return true; + } + } + return false; +} + +bool Linker::haveDirectLibrary(const char* path) +{ + for (std::vector::iterator it=fDynamicLibraries.begin(); it != fDynamicLibraries.end(); it++) { + if ( strcmp(path, it->reader->getPath()) == 0 ) + return true; + const char* installPath = it->reader->getInstallPath(); + if ( (installPath != NULL) && (strcmp(path, installPath) == 0) ) + return true; + } + return false; +} + + + + +void Linker::createWriter() +{ + const char* path = fOptions.getOutputFilePath(); + switch ( fOptions.architecture() ) { + case CPU_TYPE_POWERPC: + this->setOutputFile(ppc::ExecutableFileMachO::MakeWriter(path, fOptions, fDynamicLibraries)); + break; + case CPU_TYPE_POWERPC64: + this->setOutputFile(ppc64::ExecutableFileMachO::MakeWriter(path, fOptions, fDynamicLibraries)); + break; + case CPU_TYPE_I386: + this->setOutputFile(i386::ExecutableFileMachO::MakeWriter(path, fOptions, fDynamicLibraries)); + break; + default: + throw "unknown architecture"; + } +} + + +Linker::SymbolTable::SymbolTable(Linker& owner) + : fOwner(owner), fRequireCount(0) +{ +} + +void Linker::SymbolTable::require(const char* name) +{ + //fprintf(stderr, "require(%s)\n", name); + Mapper::iterator pos = fTable.find(name); + if ( pos == fTable.end() ) { + fTable[name] = NULL; + ++fRequireCount; + } +} + +bool Linker::SymbolTable::add(ObjectFile::Atom& atom) +{ + const bool log = false; + const char* name = atom.getName(); + //fprintf(stderr, "map.add(%p: %s => %p)\n", &fTable, name, &atom); + Mapper::iterator pos = fTable.find(name); + if ( pos != fTable.end() ) { + ObjectFile::Atom* existingAtom = pos->second; + if ( existingAtom != NULL ) { + if ( existingAtom->isTentativeDefinition() ) { + if ( atom.isTentativeDefinition() ) { + if ( atom.getSize() > existingAtom->getSize() ) { + // replace common-symbol atom with another larger common-symbol + if ( fOwner.fOptions.warnCommons() ) + fprintf(stderr, "ld64: replacing common symbol %s size %lld from %s with larger symbol size %lld from %s\n", + existingAtom->getName(), existingAtom->getSize(), existingAtom->getFile()->getPath(), atom.getSize(), atom.getFile()->getPath()); + fOwner.fDeadAtoms.insert(existingAtom); + fTable[name] = &atom; + return true; + } + else { + // keep existing common-symbol atom + if ( fOwner.fOptions.warnCommons() ) { + if ( atom.getSize() == existingAtom->getSize() ) + fprintf(stderr, "ld64: ignoring common symbol %s from %s because already have common from %s with same size\n", + atom.getName(), atom.getFile()->getPath(), existingAtom->getFile()->getPath()); + else + fprintf(stderr, "ld64: ignoring common symbol %s size %lld from %s because already have larger symbol size %lld from %s\n", + atom.getName(), atom.getSize(), atom.getFile()->getPath(), existingAtom->getSize(), existingAtom->getFile()->getPath()); + } + fOwner.fDeadAtoms.insert(&atom); + return false; + } + } + else { + // have common symbol, now found true defintion + if ( atom.isImportProxy() ) { + // definition is in a dylib, so commons-mode decides how to handle + switch ( fOwner.fOptions.commonsMode() ) { + case Options::kCommonsIgnoreDylibs: + if ( fOwner.fOptions.warnCommons() ) + fprintf(stderr, "ld64: using common symbol %s from %s and ignoring defintion from dylib %s\n", + existingAtom->getName(), existingAtom->getFile()->getPath(), atom.getFile()->getPath()); + fOwner.fDeadAtoms.insert(&atom); + return false; + case Options::kCommonsOverriddenByDylibs: + if ( fOwner.fOptions.warnCommons() ) + fprintf(stderr, "ld64: replacing common symbol %s from %s with true definition from %s\n", + existingAtom->getName(), existingAtom->getFile()->getPath(), atom.getFile()->getPath()); + fOwner.fDeadAtoms.insert(existingAtom); + fTable[name] = &atom; + return true; + case Options::kCommonsConflictsDylibsError: + throwf("common symbol %s from %s conflicts with defintion from dylib %s", + existingAtom->getName(), existingAtom->getFile()->getPath(), atom.getFile()->getPath()); + } + } + else { + // replace common-symbol atom with true definition from .o file + if ( fOwner.fOptions.warnCommons() ) { + if ( atom.getSize() < existingAtom->getSize() ) + fprintf(stderr, "ld64: warning: replacing common symbol %s size %lld from %s with smaller true definition size %lld from %s\n", + existingAtom->getName(), existingAtom->getSize(), existingAtom->getFile()->getPath(), atom.getSize(), atom.getFile()->getPath()); + else + fprintf(stderr, "ld64: replacing common symbol %s from %s with true definition from %s\n", + existingAtom->getName(), existingAtom->getFile()->getPath(), atom.getFile()->getPath()); + } + fOwner.fDeadAtoms.insert(existingAtom); + fTable[name] = &atom; + return true; + } + } + } + else if ( atom.isTentativeDefinition() ) { + // keep existing true definition, ignore new tentative definition + if ( fOwner.fOptions.warnCommons() ) { + if ( atom.getSize() > existingAtom->getSize() ) + fprintf(stderr, "ld64: warning: ignoring common symbol %s size %lld from %s because already have definition from %s size %lld, even though definition is smaller\n", + atom.getName(), atom.getSize(), atom.getFile()->getPath(), existingAtom->getFile()->getPath(), existingAtom->getSize()); + else + fprintf(stderr, "ld64: ignoring common symbol %s from %s because already have definition from %s\n", + atom.getName(), atom.getFile()->getPath(), existingAtom->getFile()->getPath()); + } + fOwner.fDeadAtoms.insert(&atom); + return false; + } + else { + // neither existing nor new atom are tentative definitions + // if existing is weak, we may replace it + if ( existingAtom->isWeakDefinition() ) { + if ( atom.isImportProxy() ) { + // keep weak definition even though one exists in a dylib, because coalescing means dylib's copy may not be used + if ( log ) fprintf(stderr, "keep weak atom even though also in a dylib: %s\n", atom.getName()); + fOwner.fDeadAtoms.insert(&atom); + return false; + } + else if ( atom.isWeakDefinition() ) { + // have another weak atom, use existing, mark new as dead + if ( log ) fprintf(stderr, "already have weak atom: %s\n", atom.getName()); + fOwner.fDeadAtoms.insert(&atom); + return false; + } + else { + // replace weak atom with non-weak atom + if ( log ) fprintf(stderr, "replacing weak atom %p from %s with %p from %s: %s\n", existingAtom, existingAtom->getFile()->getPath(), &atom, atom.getFile()->getPath(), atom.getName()); + fOwner.fDeadAtoms.insert(existingAtom); + fTable[name] = &atom; + return true; + } + } + } + if ( atom.isWeakDefinition() ) { + // ignore new weak atom, because we already have a non-weak one + return false; + } + if ( atom.isCoalesableByName() && existingAtom->isCoalesableByName() ) { + // both coalesable, so ignore duplicate + return false; + } + fprintf(stderr, "duplicate symbol %s in %s and %s\n", name, atom.getFile()->getPath(), existingAtom->getFile()->getPath()); + } + } + fTable[name] = &atom; + return true; +} + +ObjectFile::Atom* Linker::SymbolTable::find(const char* name) +{ + Mapper::iterator pos = fTable.find(name); + if ( pos != fTable.end() ) { + return pos->second; + } + return NULL; +} + + +void Linker::SymbolTable::getNeededNames(bool andWeakDefintions, std::vector& undefines) +{ + for (Mapper::iterator it=fTable.begin(); it != fTable.end(); it++) { + if ( (it->second == NULL) || (andWeakDefintions && it->second->isWeakDefinition()) ) { + undefines.push_back(it->first); + } + } +} + + + + +bool Linker::AtomSorter::operator()(ObjectFile::Atom* left, ObjectFile::Atom* right) +{ + // first sort by section order (which is already sorted by segment) + unsigned int leftSectionIndex = left->getSection()->getIndex(); + unsigned int rightSectionIndex = right->getSection()->getIndex(); + if ( leftSectionIndex != rightSectionIndex) + return (leftSectionIndex < rightSectionIndex); + + // with a section, sort by original atom order (.o file order and atom order in .o files) + return left->getSortOrder() < right->getSortOrder(); +} + + +int main(int argc, const char* argv[]) +{ + try { + // create linker object given command line arguments + Linker ld(argc, argv); + + // open all input files + ld.createReaders(); + + // open output file + ld.createWriter(); + + // do linking + ld.link(); + } + catch (const char* msg) { + fprintf(stderr, "ld64 failed: %s\n", msg); + return 1; + } + + return 0; +} + + + + + + + -- 2.45.2