diff options
author | sgk@google.com <sgk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-25 13:27:25 +0000 |
---|---|---|
committer | sgk@google.com <sgk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-25 13:27:25 +0000 |
commit | a89043ab38c1a93bcce2951099c470e84e82a0c9 (patch) | |
tree | 9ec165f35137673a77e76b08411373d41573834a /third_party/codesighs | |
parent | 62a5d6c481524a13ffd8421b3c9a5fe5278041a5 (diff) | |
download | chromium_src-a89043ab38c1a93bcce2951099c470e84e82a0c9.zip chromium_src-a89043ab38c1a93bcce2951099c470e84e82a0c9.tar.gz chromium_src-a89043ab38c1a93bcce2951099c470e84e82a0c9.tar.bz2 |
Capture Mozilla's codesighs, for use in executable sizing.
Vanilla code, no changes, except for the addition of:
* LICENSE, copied from elsewhere in the Mozilla tree;
* README.chromium, documenting what's going on.
* codesighs.gyp, for building with the rest of Chromium.
Review URL: http://codereview.chromium.org/93155
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@14522 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'third_party/codesighs')
-rw-r--r-- | third_party/codesighs/LICENSE | 567 | ||||
-rw-r--r-- | third_party/codesighs/Makefile.in | 75 | ||||
-rw-r--r-- | third_party/codesighs/README.chromium | 13 | ||||
-rwxr-xr-x | third_party/codesighs/autosummary.unix.bash | 250 | ||||
-rwxr-xr-x | third_party/codesighs/autosummary.win.bash | 211 | ||||
-rwxr-xr-x | third_party/codesighs/basesummary.unix.bash | 254 | ||||
-rwxr-xr-x | third_party/codesighs/basesummary.win.bash | 224 | ||||
-rw-r--r-- | third_party/codesighs/codesighs.c | 1075 | ||||
-rw-r--r-- | third_party/codesighs/codesighs.gyp | 60 | ||||
-rw-r--r-- | third_party/codesighs/maptsvdifftool.c | 1311 | ||||
-rw-r--r-- | third_party/codesighs/msdump2symdb.c | 1090 | ||||
-rw-r--r-- | third_party/codesighs/msmap.h | 149 | ||||
-rw-r--r-- | third_party/codesighs/msmap2tsv.c | 2237 | ||||
-rw-r--r-- | third_party/codesighs/nm2tsv.c | 505 | ||||
-rwxr-xr-x | third_party/codesighs/nm_wrap_osx.pl | 105 | ||||
-rwxr-xr-x | third_party/codesighs/readelf_wrap.pl | 192 |
16 files changed, 8318 insertions, 0 deletions
diff --git a/third_party/codesighs/LICENSE b/third_party/codesighs/LICENSE new file mode 100644 index 0000000..bc8f037 --- /dev/null +++ b/third_party/codesighs/LICENSE @@ -0,0 +1,567 @@ + MOZILLA PUBLIC LICENSE + Version 1.1 + + --------------- + +1. Definitions. + + 1.0.1. "Commercial Use" means distribution or otherwise making the + Covered Code available to a third party. + + 1.1. "Contributor" means each entity that creates or contributes to + the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Code, prior Modifications used by a Contributor, and the Modifications + made by that particular Contributor. + + 1.3. "Covered Code" means the Original Code or Modifications or the + combination of the Original Code and Modifications, in each case + including portions thereof. + + 1.4. "Electronic Distribution Mechanism" means a mechanism generally + accepted in the software development community for the electronic + transfer of data. + + 1.5. "Executable" means Covered Code in any form other than Source + Code. + + 1.6. "Initial Developer" means the individual or entity identified + as the Initial Developer in the Source Code notice required by Exhibit + A. + + 1.7. "Larger Work" means a work which combines Covered Code or + portions thereof with code not governed by the terms of this License. + + 1.8. "License" means this document. + + 1.8.1. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means any addition to or deletion from the + substance or structure of either the Original Code or any previous + Modifications. When Covered Code is released as a series of files, a + Modification is: + A. Any addition to or deletion from the contents of a file + containing Original Code or previous Modifications. + + B. Any new file that contains any part of the Original Code or + previous Modifications. + + 1.10. "Original Code" means Source Code of computer software code + which is described in the Source Code notice required by Exhibit A as + Original Code, and which, at the time of its release under this + License is not already Covered Code governed by this License. + + 1.10.1. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.11. "Source Code" means the preferred form of the Covered Code 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, or source code + differential comparisons against either the Original Code or another + well known, available Covered Code of the Contributor's choice. The + Source Code can be in a compressed or archival form, provided the + appropriate decompression or de-archiving software is widely available + for no charge. + + 1.12. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, this + License or a future version of this License issued under Section 6.1. + For legal entities, "You" includes any entity which controls, is + controlled by, or is under common control with You. For purposes of + this definition, "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 more than fifty percent + (50%) of the outstanding shares or beneficial ownership of such + entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. + The Initial Developer hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property + claims: + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Code (or portions thereof) with or without Modifications, and/or + as part of a Larger Work; and + + (b) under Patents Claims infringed by the making, using or + selling of Original Code, to make, have made, use, practice, + sell, and offer for sale, and/or otherwise dispose of the + Original Code (or portions thereof). + + (c) the licenses granted in this Section 2.1(a) and (b) are + effective on the date Initial Developer first distributes + Original Code under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: 1) for code that You delete from the Original Code; 2) + separate from the Original Code; or 3) for infringements caused + by: i) the modification of the Original Code or ii) the + combination of the Original Code with other software or devices. + + 2.2. Contributor Grant. + Subject to third party intellectual property claims, each Contributor + hereby grants You a world-wide, royalty-free, non-exclusive license + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor, to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an + unmodified basis, with other Modifications, as Covered Code + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either alone + and/or in combination with its Contributor Version (or portions + of such combination), to make, use, sell, offer for sale, have + made, and/or otherwise dispose of: 1) Modifications made by that + Contributor (or portions thereof); and 2) the combination of + Modifications made by that Contributor with its Contributor + Version (or portions of such combination). + + (c) the licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first makes Commercial Use of + the Covered Code. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: 1) for any code that Contributor has deleted from the + Contributor Version; 2) separate from the Contributor Version; + 3) for infringements caused by: i) third party modifications of + Contributor Version or ii) the combination of Modifications made + by that Contributor with other software (except as part of the + Contributor Version) or other devices; or 4) under Patent Claims + infringed by Covered Code in the absence of Modifications made by + that Contributor. + +3. Distribution Obligations. + + 3.1. Application of License. + The Modifications which You create or to which You contribute are + governed by the terms of this License, including without limitation + Section 2.2. The Source Code version of Covered Code may be + distributed only under the terms of this License or a future version + of this License released under Section 6.1, and You must include a + copy of this License with every copy of the Source Code You + distribute. You may not offer or impose any terms on any Source Code + version that alters or restricts the applicable version of this + License or the recipients' rights hereunder. However, You may include + an additional document offering the additional rights described in + Section 3.5. + + 3.2. Availability of Source Code. + Any Modification which You create or to which You contribute must be + made available in Source Code form under the terms of this License + either on the same media as an Executable version or via an accepted + Electronic Distribution Mechanism to anyone to whom you made an + Executable version available; and if made available via Electronic + Distribution Mechanism, must remain available for at least twelve (12) + months after the date it initially became available, or at least six + (6) months after a subsequent version of that particular Modification + has been made available to such recipients. You are responsible for + ensuring that the Source Code version remains available even if the + Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. + You must cause all Covered Code to which You contribute to contain a + file documenting the changes You made to create that Covered Code and + the date of any change. You must include a prominent statement that + the Modification is derived, directly or indirectly, from Original + Code provided by the Initial Developer and including the name of the + Initial Developer in (a) the Source Code, and (b) in any notice in an + Executable version or related documentation in which You describe the + origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + (a) Third Party Claims. + If Contributor has knowledge that a license under a third party's + intellectual property rights is required to exercise the rights + granted by such Contributor under Sections 2.1 or 2.2, + Contributor must include a text file with the Source Code + distribution titled "LEGAL" which describes the claim and the + party making the claim in sufficient detail that a recipient will + know whom to contact. If Contributor obtains such knowledge after + the Modification is made available as described in Section 3.2, + Contributor shall promptly modify the LEGAL file in all copies + Contributor makes available thereafter and shall take other steps + (such as notifying appropriate mailing lists or newsgroups) + reasonably calculated to inform those who received the Covered + Code that new knowledge has been obtained. + + (b) Contributor APIs. + If Contributor's Modifications include an application programming + interface and Contributor has knowledge of patent licenses which + are reasonably necessary to implement that API, Contributor must + also include this information in the LEGAL file. + + (c) Representations. + Contributor represents that, except as disclosed pursuant to + Section 3.4(a) above, Contributor believes that Contributor's + Modifications are Contributor's original creation(s) and/or + Contributor has sufficient rights to grant the rights conveyed by + this License. + + 3.5. Required Notices. + You must duplicate the notice in Exhibit A in each file of the Source + Code. If it is not possible to put such notice in a particular Source + Code file due to its structure, then You must include such notice in a + location (such as a relevant directory) where a user would be likely + to look for such a notice. If You created one or more Modification(s) + You may add your name as a Contributor to the notice described in + Exhibit A. You must also duplicate this License in any documentation + for the Source Code where You describe recipients' rights or ownership + rights relating to Covered Code. You may choose to offer, and to + charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Code. However, You + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear than + any such warranty, support, indemnity or liability obligation is + offered by You alone, and You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred by the + Initial Developer or such Contributor as a result of warranty, + support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. + You may distribute Covered Code in Executable form only if the + requirements of Section 3.1-3.5 have been met for that Covered Code, + and if You include a notice stating that the Source Code version of + the Covered Code is available under the terms of this License, + including a description of how and where You have fulfilled the + obligations of Section 3.2. The notice must be conspicuously included + in any notice in an Executable version, related documentation or + collateral in which You describe recipients' rights relating to the + Covered Code. You may distribute the Executable version of Covered + Code or ownership rights under a license of Your choice, which may + contain terms different from this License, provided that You are in + compliance with the terms of this License and that the license for the + Executable version does not attempt to limit or alter the recipient's + rights in the Source Code version from the rights set forth in this + License. If You distribute the Executable version under a different + license You must make it absolutely clear that any terms which differ + from this License are offered by You alone, not by the Initial + Developer or any Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such + terms You offer. + + 3.7. 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 such a case, You must make sure the + requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. + + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description + must be included in the LEGAL file described in Section 3.4 and must + be included with all distributions of the Source Code. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Application of this License. + + This License applies to code to which the Initial Developer has + attached the notice in Exhibit A and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions. + Netscape Communications Corporation ("Netscape") may publish revised + and/or new versions of the License from time to time. Each version + will be given a distinguishing version number. + + 6.2. Effect of New Versions. + Once Covered Code has been published under a particular version of the + License, You may always continue to use it under the terms of that + version. You may also choose to use such Covered Code under the terms + of any subsequent version of the License published by Netscape. No one + other than Netscape has the right to modify the terms applicable to + Covered Code created under this License. + + 6.3. Derivative Works. + If You create or use a modified version of this License (which you may + only do in order to apply it to code which is not already Covered Code + governed by this License), You must (a) rename Your license so that + the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", + "MPL", "NPL" or any confusingly similar phrase do not appear in your + license (except to note that your license differs from this License) + and (b) otherwise make it clear that Your version of the license + contains terms which differ from the Mozilla Public License and + Netscape Public License. (Filling in the name of the Initial + Developer, Original Code or Contributor in the notice described in + Exhibit A shall not of themselves be deemed to be modifications of + this License.) + +7. DISCLAIMER OF WARRANTY. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE + IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, + YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE + COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER + OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. TERMINATION. + + 8.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their + nature, must remain in effect beyond the termination of this License + shall survive. + + 8.2. If You initiate litigation by asserting a patent infringement + claim (excluding declatory judgment actions) against Initial Developer + or a Contributor (the Initial Developer or Contributor against whom + You file such action is referred to as "Participant") alleging that: + + (a) such Participant's Contributor Version directly or indirectly + infringes any patent, then any and all rights granted by such + Participant to You under Sections 2.1 and/or 2.2 of this License + shall, upon 60 days notice from Participant terminate prospectively, + unless if within 60 days after receipt of notice You either: (i) + agree in writing to pay Participant a mutually agreeable reasonable + royalty for Your past and future use of Modifications made by such + Participant, or (ii) withdraw Your litigation claim with respect to + the Contributor Version against such Participant. If within 60 days + of notice, a reasonable royalty and payment arrangement are not + mutually agreed upon in writing by the parties or the litigation claim + is not withdrawn, the rights granted by Participant to You under + Sections 2.1 and/or 2.2 automatically terminate at the expiration of + the 60 day notice period specified above. + + (b) any software, hardware, or device, other than such Participant's + Contributor Version, directly or indirectly infringes any patent, then + any rights granted to You by such Participant under Sections 2.1(b) + and 2.2(b) are revoked effective as of the date You first made, used, + sold, distributed, or had made, Modifications made by that + Participant. + + 8.3. If You assert a patent infringement claim against Participant + alleging that such Participant's Contributor Version directly or + indirectly infringes any patent where such claim is resolved (such as + by license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 8.4. In the event of termination under Sections 8.1 or 8.2 above, + all end user license agreements (excluding distributors and resellers) + which have been validly granted by You or any distributor hereunder + prior to termination shall survive termination. + +9. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, + OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR + ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY + CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, + WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO + THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +10. U.S. GOVERNMENT END USERS. + + The Covered Code is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" and "commercial computer software documentation," as such + terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 + C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), + all U.S. Government End Users acquire Covered Code with only those + rights set forth herein. + +11. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + California law provisions (except to the extent applicable law, if + any, provides otherwise), excluding its conflict-of-law provisions. + With respect to disputes in which at least one party is a citizen of, + or an entity chartered or registered to do business in the United + States of America, any litigation relating to this License shall be + subject to the jurisdiction of the Federal Courts of the Northern + District of California, with venue lying in Santa Clara County, + California, with the losing party responsible for costs, including + without limitation, court costs and reasonable attorneys' fees and + expenses. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly excluded. + Any law or regulation which provides that the language of a contract + shall be construed against the drafter shall not apply to this + License. + +12. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +13. MULTIPLE-LICENSED CODE. + + Initial Developer may designate portions of the Covered Code as + "Multiple-Licensed". "Multiple-Licensed" means that the Initial + Developer permits you to utilize portions of the Covered Code under + Your choice of the NPL or the alternative licenses, if any, specified + by the Initial Developer in the file described in Exhibit A. + +EXHIBIT A -Mozilla Public License. + + ``The contents of this file are subject to the Mozilla Public License + Version 1.1 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + License for the specific language governing rights and limitations + under the License. + + The Original Code is ______________________________________. + + The Initial Developer of the Original Code is ________________________. + Portions created by ______________________ are Copyright (C) ______ + _______________________. All Rights Reserved. + + Contributor(s): ______________________________________. + + Alternatively, the contents of this file may be used under the terms + of the _____ license (the "[___] License"), in which case the + provisions of [______] License are applicable instead of those + above. If you wish to allow use of your version of this file only + under the terms of the [____] License and not to allow others to use + your version of this file under the MPL, indicate your decision by + deleting the provisions above and replace them with the notice and + other provisions required by the [___] License. If you do not delete + the provisions above, a recipient may use your version of this file + under either the MPL or the [___] License." + + [NOTE: The text of this Exhibit A may differ slightly from the text of + the notices in the Source Code files of the Original Code. You should + use the text of this Exhibit A rather than the text found in the + Original Code Source Code for Your Modifications.] + + ---------------------------------------------------------------------- + + AMENDMENTS + + The Netscape Public License Version 1.1 ("NPL") consists of the + Mozilla Public License Version 1.1 with the following Amendments, + including Exhibit A-Netscape Public License. Files identified with + "Exhibit A-Netscape Public License" are governed by the Netscape + Public License Version 1.1. + + Additional Terms applicable to the Netscape Public License. + I. Effect. + These additional terms described in this Netscape Public + License -- Amendments shall apply to the Mozilla Communicator + client code and to all Covered Code under this License. + + II. "Netscape's Branded Code" means Covered Code that Netscape + distributes and/or permits others to distribute under one or more + trademark(s) which are controlled by Netscape but which are not + licensed for use under this License. + + III. Netscape and logo. + This License does not grant any rights to use the trademarks + "Netscape", the "Netscape N and horizon" logo or the "Netscape + lighthouse" logo, "Netcenter", "Gecko", "Java" or "JavaScript", + "Smart Browsing" even if such marks are included in the Original + Code or Modifications. + + IV. Inability to Comply Due to Contractual Obligation. + Prior to licensing the Original Code under this License, Netscape + has licensed third party code for use in Netscape's Branded Code. + To the extent that Netscape is limited contractually from making + such third party code available under this License, Netscape may + choose to reintegrate such code into Covered Code without being + required to distribute such code in Source Code form, even if + such code would otherwise be considered "Modifications" under + this License. + + V. Use of Modifications and Covered Code by Initial Developer. + V.1. In General. + The obligations of Section 3 apply to Netscape, except to + the extent specified in this Amendment, Section V.2 and V.3. + + V.2. Other Products. + Netscape may include Covered Code in products other than the + Netscape's Branded Code which are released by Netscape + during the two (2) years following the release date of the + Original Code, without such additional products becoming + subject to the terms of this License, and may license such + additional products on different terms from those contained + in this License. + + V.3. Alternative Licensing. + Netscape may license the Source Code of Netscape's Branded + Code, including Modifications incorporated therein, without + such Netscape Branded Code becoming subject to the terms of + this License, and may license such Netscape Branded Code on + different terms from those contained in this License. + + VI. Litigation. + Notwithstanding the limitations of Section 11 above, the + provisions regarding litigation in Section 11(a), (b) and (c) of + the License shall apply to all disputes relating to this License. + + EXHIBIT A-Netscape Public License. + + "The contents of this file are subject to the Netscape Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/NPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The Original Code is Mozilla Communicator client code, released + March 31, 1998. + + The Initial Developer of the Original Code is Netscape + Communications Corporation. Portions created by Netscape are + Copyright (C) 1998-1999 Netscape Communications Corporation. All + Rights Reserved. + + Contributor(s): ______________________________________. + + Alternatively, the contents of this file may be used under the + terms of the _____ license (the "[___] License"), in which case + the provisions of [______] License are applicable instead of + those above. If you wish to allow use of your version of this + file only under the terms of the [____] License and not to allow + others to use your version of this file under the NPL, indicate + your decision by deleting the provisions above and replace them + with the notice and other provisions required by the [___] + License. If you do not delete the provisions above, a recipient + may use your version of this file under either the NPL or the + [___] License." diff --git a/third_party/codesighs/Makefile.in b/third_party/codesighs/Makefile.in new file mode 100644 index 0000000..e243f2d --- /dev/null +++ b/third_party/codesighs/Makefile.in @@ -0,0 +1,75 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Garrett Arch Blythe +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +REQUIRES = $(NULL) + +CSRCS += \ + codesighs.c \ + maptsvdifftool.c \ + $(NULL) + +ifeq ($(OS_ARCH),WINNT) +CSRCS += \ + msmap2tsv.c \ + msdump2symdb.c \ + $(NULL) +else +CSRCS += \ + nm2tsv.c \ + $(NULL) +endif + +SIMPLE_PROGRAMS = $(CSRCS:.c=$(BIN_SUFFIX)) + +include $(topsrcdir)/config/config.mk + +ifeq ($(OS_ARCH),WINNT) +OS_LIBS += \ + imagehlp.lib \ + $(NULL) +endif + + +include $(topsrcdir)/config/rules.mk diff --git a/third_party/codesighs/README.chromium b/third_party/codesighs/README.chromium new file mode 100644 index 0000000..36d4079 --- /dev/null +++ b/third_party/codesighs/README.chromium @@ -0,0 +1,13 @@ +This is Chromium's copy of the Mozilla codesighs tool. + +Originally obtained from Mozilla's Mercurial repository +on 17 April 2009: + +http://hg.mozilla.org/mozilla-central/tools/codesighs + +The LICENSE from http://hg.mozilla.org/mozilla-central has been copied +here. + +There are no local changes to the code itself. + +A codesighs.gyp file has been added for building with Chromium. diff --git a/third_party/codesighs/autosummary.unix.bash b/third_party/codesighs/autosummary.unix.bash new file mode 100755 index 0000000..a35f92a --- /dev/null +++ b/third_party/codesighs/autosummary.unix.bash @@ -0,0 +1,250 @@ +#!/bin/bash +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is autosummary.linx.bash code, released +# Oct 10, 2002. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2002 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Garrett Arch Blythe, 10-October-2002 +# Simon Fraser <sfraser@netscape.com> +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Check for optional objdir +# +if [ "$1" = "-o" ]; then +OBJROOT="$2" +shift +shift +else +OBJROOT="./mozilla" +fi + +if [ "$1" = "-s" ]; then +SRCROOT="$2" +shift +shift +else +SRCROOT="./mozilla" +fi + +# +# A little help for my friends. +# +if [ "-h" == "$1" ];then + SHOWHELP="1" +fi +if [ "--help" == "$1" ];then + SHOWHELP="1" +fi +if [ "" == "$1" ]; then + SHOWHELP="1" +fi +if [ "" == "$2" ]; then + SHOWHELP="1" +fi +if [ "" == "$3" ]; then + SHOWHELP="1" +fi + + +# +# Show the help if required. +# +if [ $SHOWHELP ]; then + echo "usage: $0 <save_results> <old_results> <summary>" + echo " <save_results> is a file that will receive the results of this run." + echo " This file can be used in a future run as the old results." + echo " <old_results> is a results file from a previous run." + echo " It is used to diff with current results and come up with a summary" + echo " of changes." + echo " It is OK if the file does not exist, just supply the argument." + echo " <summary> is a file which will contain a human readable report." + echo " This file is most useful by providing more information than the" + echo " normally single digit output of this script." + echo "" + echo "Run this command from the parent directory of the mozilla tree." + echo "" + echo "This command will output two numbers to stdout that will represent" + echo " the total size of all code and data, and a delta from the prior." + echo " the old results." + echo "For much more detail on size drifts refer to the summary report." + echo "" + echo "This tool reports on all executables in the directory tree." + exit +fi + +# +# Stash our arguments away. +# +COPYSORTTSV="$1" +OLDTSVFILE="$2" +SUMMARYFILE="$3" + +OSTYPE=`uname -s` + +# +# On Mac OS X, use the --zerodrift option to maptsvdifftool +# +if [ $OSTYPE == "Darwin" ]; then +ZERODRIFT="--zerodrift" +else +ZERODRIFT="" +fi + +# +# Create our temporary directory. +# mktemp on Darwin doesn't support -d (suckage) +# +if [ $OSTYPE == "Darwin" ]; then +ZERODRIFT="--zerodrift" +MYTMPDIR=`mktemp ./codesighs.tmp.XXXXXXXX` +rm $MYTMPDIR +mkdir $MYTMPDIR +else +MYTMPDIR=`mktemp -d ./codesighs.tmp.XXXXXXXX` +fi + +# +# Find all relevant files. +# +ALLFILES="$MYTMPDIR/allfiles.list" + +if [ $OSTYPE == "Darwin" ] || [ $OSTYPE == "SunOS" ]; then +find $OBJROOT/dist/bin ! -type d > $ALLFILES +else +find $OBJROOT/dist/bin -not -type d > $ALLFILES +fi + +# Check whether we have 'eu-readelf' or 'readelf' available. +# If we do, it will give more accurate symbol sizes than nm. + +if [ $OSTYPE == "Darwin" ]; then + USE_READELF= +else +READELF_PROG=`which eu-readelf 2>/dev/null | grep /eu-readelf$` +if test "$READELF_PROG"; then + USE_READELF=1 +else + READELF_PROG=`which readelf 2>/dev/null | grep /readelf$` + if test "$READELF_PROG"; then + # Check whether we need -W + if readelf --help | grep "\--wide" >&/dev/null; then + READELF_PROG="readelf -W" + else + READELF_PROG="readelf" + fi + USE_READELF=1 + else + USE_READELF= + fi +fi +fi + +RAWTSVFILE="$MYTMPDIR/raw.tsv" +if test "$USE_READELF"; then +export READELF_PROG +xargs -n 1 $SRCROOT/tools/codesighs/readelf_wrap.pl < $ALLFILES > $RAWTSVFILE 2> /dev/null +else + +# +# Produce the cumulative nm output. +# We are very particular on what switches to use. +# nm --format=bsd --size-sort --print-file-name --demangle +# +# Darwin (Mac OS X) has a lame nm that we have to wrap in a perl +# script to get decent output. +# +NMRESULTS="$MYTMPDIR/nm.txt" +if [ $OSTYPE == "Darwin" ]; then +xargs -n 1 $SRCROOT/tools/codesighs/nm_wrap_osx.pl < $ALLFILES > $NMRESULTS 2> /dev/null +else +xargs -n 1 nm --format=bsd --size-sort --print-file-name --demangle < $ALLFILES > $NMRESULTS 2> /dev/null +fi + + +# +# Produce the TSV output. +# + +$OBJROOT/dist/bin/nm2tsv --input $NMRESULTS > $RAWTSVFILE + +fi # USE_READELF + +# +# Sort the TSV output for useful diffing and eyeballing in general. +# +sort -r $RAWTSVFILE > $COPYSORTTSV + + +# +# If a historical file was specified, diff it with our sorted tsv values. +# Run it through a tool to summaries the diffs to the module +# level report. +# Otherwise, generate the module level report from our new data. +# + +rm -f $SUMMARYFILE +DIFFFILE="$MYTMPDIR/diff.txt" +if [ -e $OLDTSVFILE ]; then + diff $OLDTSVFILE $COPYSORTTSV > $DIFFFILE + $OBJROOT/dist/bin/maptsvdifftool $ZERODRIFT --input $DIFFFILE >> $SUMMARYFILE +else + $OBJROOT/dist/bin/codesighs --modules --input $COPYSORTTSV >> $SUMMARYFILE +fi + + +# +# Output our numbers, that will let tinderbox specify everything all +# at once. +# First number is in fact the total size of all code and data in the map +# files parsed. +# Second number, if present, is growth/shrinkage. +# + +if [ $TINDERBOX_OUTPUT ]; then + echo -n "__codesize:" +fi +$OBJROOT/dist/bin/codesighs --totalonly --input $COPYSORTTSV + +if [ -e $DIFFFILE ]; then +if [ $TINDERBOX_OUTPUT ]; then + echo -n "__codesizeDiff:" +fi + $OBJROOT/dist/bin/maptsvdifftool $ZERODRIFT --summary --input $DIFFFILE +fi + +# +# Remove our temporary directory. +# +rm -rf $MYTMPDIR diff --git a/third_party/codesighs/autosummary.win.bash b/third_party/codesighs/autosummary.win.bash new file mode 100755 index 0000000..d501625 --- /dev/null +++ b/third_party/codesighs/autosummary.win.bash @@ -0,0 +1,211 @@ +#!/bin/bash +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is autosummary.win.bash code, released +# Oct 3, 2002. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2002 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Garrett Arch Blythe, 03-October-2002 +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Check for optional objdir +# +if [ "$1" = "-o" ]; then +OBJROOT="$2" +shift +shift +else +OBJROOT="./mozilla" +fi + +if [ "$1" = "-s" ]; then +SRCROOT="$2" +shift +shift +else +SRCROOT="./mozilla" +fi + +# +# A little help for my friends. +# +if [ "-h" == "$1" ];then + SHOWHELP="1" +fi +if [ "--help" == "$1" ];then + SHOWHELP="1" +fi +if [ "" == "$1" ]; then + SHOWHELP="1" +fi +if [ "" == "$2" ]; then + SHOWHELP="1" +fi +if [ "" == "$3" ]; then + SHOWHELP="1" +fi + + +# +# Show the help if required. +# +if [ $SHOWHELP ]; then + echo "usage: $0 <save_results> <old_results> <summary>" + echo " <save_results> is a file that will receive the results of this run." + echo " This file can be used in a future run as the old results." + echo " <old_results> is a results file from a previous run." + echo " It is used to diff with current results and come up with a summary" + echo " of changes." + echo " It is OK if the file does not exist, just supply the argument." + echo " <summary> is a file which will contain a human readable report." + echo " This file is most useful by providing more information than the" + echo " normally single digit output of this script." + echo "" + echo "Run this command from the parent directory of the mozilla tree." + echo "" + echo "This command will output two numbers to stdout that will represent" + echo " the total size of all code and data, and a delta from the prior." + echo " the old results." + echo "For much more detail on size drifts refer to the summary report." + echo "" + echo "This tool reports on all executables in the directory tree." + exit +fi + + +# +# Stash our arguments away. +# +COPYSORTTSV="$1" +OLDTSVFILE="$2" +SUMMARYFILE="$3" + + +# +# Create our temporary directory. +# +MYTMPDIR=`mktemp -d ./codesighs.tmp.XXXXXXXX` + + +# +# Find the types of files we are interested in. +# +ONEFINDPASS="$MYTMPDIR/onefind.list" +/usr/bin/find $OBJROOT -type f -name "*.obj" -or -name "*.map" | while read FNAME; do + cygpath -m $FNAME >> $ONEFINDPASS +done + + +# +# Find all object files. +# +ALLOBJSFILE="$MYTMPDIR/allobjs.list" +grep -i "\.obj$" < $ONEFINDPASS > $ALLOBJSFILE + + +# +# Get a dump of the symbols in every object file. +# +ALLOBJSYMSFILE="$MYTMPDIR/allobjsyms.list" +xargs -n 1 dumpbin.exe /symbols < $ALLOBJSFILE > $ALLOBJSYMSFILE 2> /dev/null + + +# +# Produce the symdb for the symbols in all object files. +# +SYMDBFILE="$MYTMPDIR/symdb.tsv" +$OBJROOT/dist/bin/msdump2symdb --input $ALLOBJSYMSFILE | /usr/bin/sort > $SYMDBFILE 2> /dev/null + + +# +# Find all map files. +# +ALLMAPSFILE="$MYTMPDIR/allmaps.list" +grep -i "\.map$" < $ONEFINDPASS > $ALLMAPSFILE + + +# +# Produce the TSV output. +# +RAWTSVFILE="$MYTMPDIR/raw.tsv" +$OBJROOT/dist/bin/msmap2tsv --symdb $SYMDBFILE --batch < $ALLMAPSFILE > $RAWTSVFILE 2> /dev/null + + +# +# Sort the TSV output for useful diffing and eyeballing in general. +# +/usr/bin/sort -r $RAWTSVFILE > $COPYSORTTSV + + +# +# If a historical file was specified, diff it with our sorted tsv values. +# Run it through a tool to summaries the diffs to the module +# level report. +# Otherwise, generate the module level report from our new data. +# +rm -f $SUMMARYFILE +DIFFFILE="$MYTMPDIR/diff.txt" +if [ -e $OLDTSVFILE ]; then + diff $OLDTSVFILE $COPYSORTTSV > $DIFFFILE + $OBJROOT/dist/bin/maptsvdifftool --negation --input $DIFFFILE | dos2unix >> $SUMMARYFILE +else + $OBJROOT/dist/bin/codesighs --modules --input $COPYSORTTSV | dos2unix >> $SUMMARYFILE +fi + + +# +# Output our numbers, that will let tinderbox specify everything all +# at once. +# First number is in fact the total size of all code and data in the map +# files parsed. +# Second number, if present, is growth/shrinkage. +# + +if [ $TINDERBOX_OUTPUT ]; then + echo -n "__codesize:" +fi +$OBJROOT/dist/bin/codesighs --totalonly --input $COPYSORTTSV | dos2unix + + +if [ -e $DIFFFILE ]; then +if [ $TINDERBOX_OUTPUT ]; then + echo -n "__codesizeDiff:" +fi + $OBJROOT/dist/bin/maptsvdifftool --negation --summary --input $DIFFFILE | dos2unix +fi + +# +# Remove our temporary directory. +# +rm -rf $MYTMPDIR diff --git a/third_party/codesighs/basesummary.unix.bash b/third_party/codesighs/basesummary.unix.bash new file mode 100755 index 0000000..34cd298 --- /dev/null +++ b/third_party/codesighs/basesummary.unix.bash @@ -0,0 +1,254 @@ +#!/bin/bash +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is basesummary.linx.bash code, released +# Nov 15, 2002. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2002 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Garrett Arch Blythe, 15-November-2002 +# Simon Fraser <sfraser@netscape.com> +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Check for optional objdir +# +if [ "$1" = "-o" ]; then +OBJROOT="$2" +shift +shift +else +OBJROOT="./mozilla" +fi + +if [ "$1" = "-s" ]; then +SRCROOT="$2" +shift +shift +else +SRCROOT="./mozilla" +fi + +OSTYPE=`uname -s` + +if [ $OSTYPE == "Darwin" ]; then +MANIFEST="$SRCROOT/embedding/config/basebrowser-mac-macho" +else +MANIFEST="$SRCROOT/embedding/config/basebrowser-unix" +fi + +# +# A little help for my friends. +# +if [ "-h" == "$1" ];then + SHOWHELP="1" +fi +if [ "--help" == "$1" ];then + SHOWHELP="1" +fi +if [ "" == "$1" ]; then + SHOWHELP="1" +fi +if [ "" == "$2" ]; then + SHOWHELP="1" +fi +if [ "" == "$3" ]; then + SHOWHELP="1" +fi + + +# +# Show the help if required. +# +if [ $SHOWHELP ]; then + echo "usage: $0 <save_results> <old_results> <summary>" + echo " <save_results> is a file that will receive the results of this run." + echo " This file can be used in a future run as the old results." + echo " <old_results> is a results file from a previous run." + echo " It is used to diff with current results and come up with a summary" + echo " of changes." + echo " It is OK if the file does not exist, just supply the argument." + echo " <summary> is a file which will contain a human readable report." + echo " This file is most useful by providing more information than the" + echo " normally single digit output of this script." + echo "" + echo "Run this command from the parent directory of the mozilla tree." + echo "" + echo "This command will output two numbers to stdout that will represent" + echo " the total size of all code and data, and a delta from the prior." + echo " the old results." + echo "For much more detail on size drifts refer to the summary report." + echo "" + echo "This tool reports on executables listed in the following file:" + echo "$MANIFEST" + exit +fi + + +# +# Stash our arguments away. +# +COPYSORTTSV="$1" +OLDTSVFILE="$2" +SUMMARYFILE="$3" + + +# +# On Mac OS X, use the --zerodrift option to maptsvdifftool +# +if [ $OSTYPE == "Darwin" ]; then +ZERODRIFT="--zerodrift" +else +ZERODRIFT="" +fi + + +# +# Create our temporary directory. +# mktemp on Darwin doesn't support -d (suckage) +# +if [ $OSTYPE == "Darwin" ]; then +MYTMPDIR=`mktemp ./codesighs.tmp.XXXXXXXX` +rm $MYTMPDIR +mkdir $MYTMPDIR +else +MYTMPDIR=`mktemp -d ./codesighs.tmp.XXXXXXXX` +fi + + +# Check whether we have 'eu-readelf' or 'readelf' available. +# If we do, it will give more accurate symbol sizes than nm. + +if [ $OSTYPE == "Darwin" ]; then + USE_READELF= +else +READELF_PROG=`which eu-readelf 2>/dev/null | grep /eu-readelf$` +if test "$READELF_PROG"; then + USE_READELF=1 +else + READELF_PROG=`which readelf 2>/dev/null | grep /readelf$` + if test "$READELF_PROG"; then + # Check whether we need -W + if readelf --help | grep "\--wide" >&/dev/null; then + READELF_PROG="readelf -W" + else + READELF_PROG="readelf" + fi + USE_READELF=1 + else + USE_READELF= + fi +fi +fi + +# +# Find all relevant files. +# +ALLFILES="$MYTMPDIR/allfiles.list" +grep -v '[\;\[]' < $MANIFEST | grep -v '^$' | sed "s|^|${OBJROOT}/dist/bin/|" > $ALLFILES + + +RAWTSVFILE="$MYTMPDIR/raw.tsv" + +if test "$USE_READELF"; then +export READELF_PROG +xargs -n 1 $SRCROOT/tools/codesighs/readelf_wrap.pl < $ALLFILES > $RAWTSVFILE +else + +# +# Produce the cumulative nm output. +# We are very particular on what switches to use. +# nm --format=bsd --size-sort --print-file-name --demangle +# +# Darwin (Mac OS X) has a lame nm that we have to wrap in a perl +# script to get decent output. +# +NMRESULTS="$MYTMPDIR/nm.txt" +if [ $OSTYPE == "Darwin" ]; then +xargs -n 1 $SRCROOT/tools/codesighs/nm_wrap_osx.pl < $ALLFILES > $NMRESULTS 2> /dev/null +else +xargs -n 1 nm --format=bsd --size-sort --print-file-name --demangle < $ALLFILES > $NMRESULTS 2> /dev/null +fi + +# +# Produce the TSV output. +# +$OBJROOT/dist/bin/nm2tsv --input $NMRESULTS > $RAWTSVFILE + +fi # USE_READELF + +# +# Sort the TSV output for useful diffing and eyeballing in general. +# +sort -r $RAWTSVFILE > $COPYSORTTSV + + +# +# If a historical file was specified, diff it with our sorted tsv values. +# Run it through a tool to summaries the diffs to the module +# level report. +# Otherwise, generate the module level report from our new data. +# +rm -f $SUMMARYFILE +DIFFFILE="$MYTMPDIR/diff.txt" +if [ -e $OLDTSVFILE ]; then + diff $OLDTSVFILE $COPYSORTTSV > $DIFFFILE + $OBJROOT/dist/bin/maptsvdifftool $ZERODRIFT --input $DIFFFILE >> $SUMMARYFILE +else + $OBJROOT/dist/bin/codesighs --modules --input $COPYSORTTSV >> $SUMMARYFILE +fi + + +# +# Output our numbers, that will let tinderbox specify everything all +# at once. +# First number is in fact the total size of all code and data in the map +# files parsed. +# Second number, if present, is growth/shrinkage. +# + +if [ $TINDERBOX_OUTPUT ]; then + echo -n "__codesize:" +fi +$OBJROOT/dist/bin/codesighs --totalonly --input $COPYSORTTSV + +if [ -e $DIFFFILE ]; then +if [ $TINDERBOX_OUTPUT ]; then + echo -n "__codesizeDiff:" +fi + $OBJROOT/dist/bin/maptsvdifftool $ZERODRIFT --summary --input $DIFFFILE +fi + +# +# Remove our temporary directory. +# +rm -rf $MYTMPDIR diff --git a/third_party/codesighs/basesummary.win.bash b/third_party/codesighs/basesummary.win.bash new file mode 100755 index 0000000..1e05859 --- /dev/null +++ b/third_party/codesighs/basesummary.win.bash @@ -0,0 +1,224 @@ +#!/bin/bash +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is basesummary.win.bash code, released +# Nov 15, 2002. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2002 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Garrett Arch Blythe, 15-November-2002 +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + + +# +# Check for optional objdir +# +if [ "$1" = "-o" ]; then +OBJROOT="$2" +shift +shift +else +OBJROOT="./mozilla" +fi + +if [ "$1" = "-s" ]; then +SRCROOT="$2" +shift +shift +else +SRCROOT="./mozilla" +fi + +MANIFEST="$SRCROOT/embedding/config/basebrowser-win" + +# +# A little help for my friends. +# +if [ "-h" == "$1" ];then + SHOWHELP="1" +fi +if [ "--help" == "$1" ];then + SHOWHELP="1" +fi +if [ "" == "$1" ]; then + SHOWHELP="1" +fi +if [ "" == "$2" ]; then + SHOWHELP="1" +fi +if [ "" == "$3" ]; then + SHOWHELP="1" +fi + + +# +# Show the help if required. +# +if [ $SHOWHELP ]; then + echo "usage: $0 <save_results> <old_results> <summary>" + echo " <save_results> is a file that will receive the results of this run." + echo " This file can be used in a future run as the old results." + echo " <old_results> is a results file from a previous run." + echo " It is used to diff with current results and come up with a summary" + echo " of changes." + echo " It is OK if the file does not exist, just supply the argument." + echo " <summary> is a file which will contain a human readable report." + echo " This file is most useful by providing more information than the" + echo " normally single digit output of this script." + echo "" + echo "Run this command from the parent directory of the mozilla tree." + echo "" + echo "This command will output two numbers to stdout that will represent" + echo " the total size of all code and data, and a delta from the prior." + echo " the old results." + echo "For much more detail on size drifts refer to the summary report." + echo "" + echo "This tool reports on executables listed in the following file:" + echo "$MANIFEST" + exit +fi + + +# +# Stash our arguments away. +# +COPYSORTTSV="$1" +OLDTSVFILE="$2" +SUMMARYFILE="$3" + + +# +# Create our temporary directory. +# +MYTMPDIR=`mktemp -d ./codesighs.tmp.XXXXXXXX` + + +# +# Find the types of files we are interested in. +# +ONEFINDPASS="$MYTMPDIR/onefind.list" +/usr/bin/find $OBJROOT -type f -name "*.obj" -or -name "*.map" | while read FNAME; do + cygpath -m $FNAME >> $ONEFINDPASS +done + + +# +# Find all object files. +# +ALLOBJSFILE="$MYTMPDIR/allobjs.list" +grep -i "\.obj$" < $ONEFINDPASS > $ALLOBJSFILE + + +# +# Get a dump of the symbols in every object file. +# +ALLOBJSYMSFILE="$MYTMPDIR/allobjsyms.list" +xargs -n 1 dumpbin.exe /symbols < $ALLOBJSFILE > $ALLOBJSYMSFILE 2> /dev/null + + +# +# Produce the symdb for the symbols in all object files. +# +SYMDBFILE="$MYTMPDIR/symdb.tsv" +$OBJROOT/dist/bin/msdump2symdb --input $ALLOBJSYMSFILE | /usr/bin/sort > $SYMDBFILE 2> /dev/null + + +# +# Find all map files. +# +ALLMAPSFILE="$MYTMPDIR/allmaps.list" +grep -i "\.map$" < $ONEFINDPASS > $ALLMAPSFILE + + +# +# Figure out which modules in specific we care about. +# The relevant set meaning that the map file name prefix must be found +# in the file mozilla/embedding/config/basebrowser-win. +# +RELEVANTSETFILE="$MYTMPDIR/relevant.set" +grep -v '\;' < $MANIFEST | sed 's/.*\\//' | grep '\.[eEdD][xXlL][eElL]' | sed 's/\.[eEdD][xXlL][eElL]//' > $RELEVANTSETFILE +RELEVANTARG=`xargs -n 1 echo --match-module < $RELEVANTSETFILE` + + +# +# Produce the TSV output. +# +RAWTSVFILE="$MYTMPDIR/raw.tsv" +$OBJROOT/dist/bin/msmap2tsv --symdb $SYMDBFILE --batch $RELEVANTARG < $ALLMAPSFILE > $RAWTSVFILE 2> /dev/null + + +# +# Sort the TSV output for useful diffing and eyeballing in general. +# +/usr/bin/sort -r $RAWTSVFILE > $COPYSORTTSV + + +# +# If a historical file was specified, diff it with our sorted tsv values. +# Run it through a tool to summaries the diffs to the module +# level report. +# Otherwise, generate the module level report from our new data. +# +rm -f $SUMMARYFILE +DIFFFILE="$MYTMPDIR/diff.txt" +if [ -e $OLDTSVFILE ]; then + diff $OLDTSVFILE $COPYSORTTSV > $DIFFFILE + $OBJROOT/dist/bin/maptsvdifftool --negation --input $DIFFFILE | dos2unix >> $SUMMARYFILE +else + $OBJROOT/dist/bin/codesighs --modules --input $COPYSORTTSV | dos2unix >> $SUMMARYFILE +fi + + +# +# Output our numbers, that will let tinderbox specify everything all +# at once. +# First number is in fact the total size of all code and data in the map +# files parsed. +# Second number, if present, is growth/shrinkage. +# + +if [ $TINDERBOX_OUTPUT ]; then + echo -n "__codesize:" +fi +$OBJROOT/dist/bin/codesighs --totalonly --input $COPYSORTTSV | dos2unix + +if [ -e $DIFFFILE ]; then +if [ $TINDERBOX_OUTPUT ]; then + echo -n "__codesizeDiff:" +fi + $OBJROOT/dist/bin/maptsvdifftool --negation --summary --input $DIFFFILE | dos2unix +fi + +# +# Remove our temporary directory. +# +rm -rf $MYTMPDIR diff --git a/third_party/codesighs/codesighs.c b/third_party/codesighs/codesighs.c new file mode 100644 index 0000000..f20fbb1 --- /dev/null +++ b/third_party/codesighs/codesighs.c @@ -0,0 +1,1075 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is codesighs.c code, released + * Oct 3, 2002. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Garrett Arch Blythe, 03-October-2002 + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <ctype.h> +#include <errno.h> + +#define ERROR_REPORT(num, val, msg) fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg)); +#define CLEANUP(ptr) do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0) + + +typedef struct __struct_Options +/* +** Options to control how we perform. +** +** mProgramName Used in help text. +** mInput File to read for input. +** Default is stdin. +** mInputName Name of the file. +** mOutput Output file, append. +** Default is stdout. +** mOutputName Name of the file. +** mHelp Whether or not help should be shown. +** mModules Output module by module information. +** mTotalOnly Only output one number, the total. +** mMinSize Ignore lines below this size. +** mMaxSize Ignore lines above this size. +** mMatchScopes For a line to be processed, it should match. +** mMachClasses For a line to be processed, it should match. +** mMatchModules For a line to be processed, it should match. +** mMatchSections For a line to be processed, it should match. +** mMatchObjects For a line to be processed, it should match. +** mMatchSymbols For a line to be processed, it should match. +*/ +{ + const char* mProgramName; + FILE* mInput; + char* mInputName; + FILE* mOutput; + char* mOutputName; + int mHelp; + int mModules; + int mTotalOnly; + unsigned long mMinSize; + unsigned long mMaxSize; + char** mMatchScopes; + unsigned mMatchScopeCount; + char** mMatchClasses; + unsigned mMatchClassCount; + char** mMatchModules; + unsigned mMatchModuleCount; + char** mMatchSections; + unsigned mMatchSectionCount; + char** mMatchObjects; + unsigned mMatchObjectCount; + char** mMatchSymbols; + unsigned mMatchSymbolCount; +} +Options; + + +typedef struct __struct_Switch +/* +** Command line options. +*/ +{ + const char* mLongName; + const char* mShortName; + int mHasValue; + const char* mValue; + const char* mDescription; +} +Switch; + +#define DESC_NEWLINE "\n\t\t" + +static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."}; +static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."}; +static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."}; +static Switch gModuleSwitch = {"--modules", "-m", 0, NULL, "Output individual module numbers as well."}; +static Switch gTotalSwitch = {"--totalonly", "-t", 0, NULL, "Output only one number." DESC_NEWLINE "The total overall size." DESC_NEWLINE "Overrides other output options."}; +static Switch gMinSize = {"--min-size", "-min", 1, NULL, "Only consider symbols equal to or greater than this size." DESC_NEWLINE "The default is 0x00000000."}; +static Switch gMaxSize = {"--max-size", "-max", 1, NULL, "Only consider symbols equal to or smaller than this size." DESC_NEWLINE "The default is 0xFFFFFFFF."}; +static Switch gMatchScope = {"--match-scope", "-msco", 1, NULL, "Only consider scopes that have a substring match." DESC_NEWLINE "Multiple uses allowed to specify a range of scopes," DESC_NEWLINE "though PUBLIC, STATIC, and UNDEF are your only choices."}; +static Switch gMatchClass = {"--match-class", "-mcla", 1, NULL, "Only consider classes that have a substring match." DESC_NEWLINE "Multiple uses allowed to specify a range of classes," DESC_NEWLINE "though CODE and DATA are your only choices."}; +static Switch gMatchModule = {"--match-module", "-mmod", 1, NULL, "Only consider modules that have a substring match." DESC_NEWLINE "Multiple uses allowed to specify an array of modules."}; +static Switch gMatchSection = {"--match-section", "-msec", 1, NULL, "Only consider sections that have a substring match." DESC_NEWLINE "Multiple uses allowed to specify an array of sections." DESC_NEWLINE "Section is considered symbol type."}; +static Switch gMatchObject = {"--match-object", "-mobj", 1, NULL, "Only consider objects that have a substring match." DESC_NEWLINE "Multiple uses allowed to specify an array of objects."}; +static Switch gMatchSymbol = {"--match-symbol", "-msym", 1, NULL, "Only consider symbols that have a substring match." DESC_NEWLINE "Multiple uses allowed to specify an array of symbols."}; + +static Switch* gSwitches[] = { + &gInputSwitch, + &gOutputSwitch, + &gModuleSwitch, + &gTotalSwitch, + &gMinSize, + &gMaxSize, + &gMatchClass, + &gMatchScope, + &gMatchModule, + &gMatchSection, + &gMatchObject, + &gMatchSymbol, + &gHelpSwitch +}; + + +typedef struct __struct_SizeStats +/* +** Track totals. +** +** mData Size of data. +** mCode Size of code. +*/ +{ + unsigned long mData; + unsigned long mCode; +} +SizeStats; + + +typedef struct __struct_ModuleStats +/* +** Track module level information. +** +** mModule Module name. +** mSize Size of module. +*/ +{ + char* mModule; + SizeStats mSize; +} +ModuleStats; + +typedef enum __enum_SegmentClass +{ + CODE, + DATA +} +SegmentClass; + + +static int moduleCompare(const void* in1, const void* in2) +/* +** qsort helper function. +*/ +{ + int retval = 0; + + const ModuleStats* one = (const ModuleStats*)in1; + const ModuleStats* two = (const ModuleStats*)in2; + unsigned long oneSize = one->mSize.mCode + one->mSize.mData; + unsigned long twoSize = two->mSize.mCode + two->mSize.mData; + + if(oneSize < twoSize) + { + retval = 1; + } + else if(oneSize > twoSize) + { + retval = -1; + } + + return retval; +} + + +void trimWhite(char* inString) +/* +** Remove any whitespace from the end of the string. +*/ +{ + int len = strlen(inString); + + while(len) + { + len--; + + if(isspace(*(inString + len))) + { + *(inString + len) = '\0'; + } + else + { + break; + } + } +} + + +int codesighs(Options* inOptions) +/* +** Output a simplistic report based on our options. +*/ +{ + int retval = 0; + char lineBuffer[0x1000]; + int scanRes = 0; + unsigned long size; + char segClass[0x10]; + char scope[0x10]; + char module[0x100]; + char segment[0x40]; + char object[0x100]; + char* symbol; + SizeStats overall; + ModuleStats* modules = NULL; + unsigned moduleCount = 0; + + memset(&overall, 0, sizeof(overall)); + + /* + ** Read the file line by line, regardless of number of fields. + ** We assume tab separated value formatting, at least 7 lead values: + ** size class scope module segment object symbol .... + */ + while(0 == retval && NULL != fgets(lineBuffer, sizeof(lineBuffer), inOptions->mInput)) + { + trimWhite(lineBuffer); + + scanRes = sscanf(lineBuffer, + "%x\t%s\t%s\t%s\t%s\t%s\t", + (unsigned*)&size, + segClass, + scope, + module, + segment, + object); + + if(6 == scanRes) + { + SegmentClass segmentClass = CODE; + + symbol = strchr(lineBuffer, '\t') + 1; + + /* + ** Qualify the segment class. + */ + if(0 == strcmp(segClass, "DATA")) + { + segmentClass = DATA; + } + else if(0 == strcmp(segClass, "CODE")) + { + segmentClass = CODE; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, segClass, "Unable to determine segment class."); + } + + if(0 == retval) + { + /* + ** Match any options required before continuing. + ** This is where you would want to add more restrictive totalling. + */ + + /* + ** Match size. + */ + if(size < inOptions->mMinSize) + { + continue; + } + if(size > inOptions->mMaxSize) + { + continue; + } + + /* + ** Match class. + */ + if(0 != inOptions->mMatchClassCount) + { + unsigned loop = 0; + + for(loop = 0; loop < inOptions->mMatchClassCount; loop++) + { + if(NULL != strstr(segClass, inOptions->mMatchClasses[loop])) + { + break; + } + } + + /* + ** If there was no match, we skip the line. + */ + if(loop == inOptions->mMatchClassCount) + { + continue; + } + } + + /* + ** Match scope. + */ + if(0 != inOptions->mMatchScopeCount) + { + unsigned loop = 0; + + for(loop = 0; loop < inOptions->mMatchScopeCount; loop++) + { + if(NULL != strstr(scope, inOptions->mMatchScopes[loop])) + { + break; + } + } + + /* + ** If there was no match, we skip the line. + */ + if(loop == inOptions->mMatchScopeCount) + { + continue; + } + } + + /* + ** Match modules. + */ + if(0 != inOptions->mMatchModuleCount) + { + unsigned loop = 0; + + for(loop = 0; loop < inOptions->mMatchModuleCount; loop++) + { + if(NULL != strstr(module, inOptions->mMatchModules[loop])) + { + break; + } + } + + /* + ** If there was no match, we skip the line. + */ + if(loop == inOptions->mMatchModuleCount) + { + continue; + } + } + + /* + ** Match sections. + */ + if(0 != inOptions->mMatchSectionCount) + { + unsigned loop = 0; + + for(loop = 0; loop < inOptions->mMatchSectionCount; loop++) + { + if(NULL != strstr(segment, inOptions->mMatchSections[loop])) + { + break; + } + } + + /* + ** If there was no match, we skip the line. + */ + if(loop == inOptions->mMatchSectionCount) + { + continue; + } + } + + /* + ** Match object. + */ + if(0 != inOptions->mMatchObjectCount) + { + unsigned loop = 0; + + for(loop = 0; loop < inOptions->mMatchObjectCount; loop++) + { + if(NULL != strstr(object, inOptions->mMatchObjects[loop])) + { + break; + } + } + + /* + ** If there was no match, we skip the line. + */ + if(loop == inOptions->mMatchObjectCount) + { + continue; + } + } + + /* + ** Match symbols. + */ + if(0 != inOptions->mMatchSymbolCount) + { + unsigned loop = 0; + + for(loop = 0; loop < inOptions->mMatchSymbolCount; loop++) + { + if(NULL != strstr(symbol, inOptions->mMatchSymbols[loop])) + { + break; + } + } + + /* + ** If there was no match, we skip the line. + */ + if(loop == inOptions->mMatchSymbolCount) + { + continue; + } + } + + /* + ** Update overall totals. + */ + if(CODE == segmentClass) + { + overall.mCode += size; + } + else if(DATA == segmentClass) + { + overall.mData += size; + } + + /* + ** See what else we should be tracking. + */ + if(0 == inOptions->mTotalOnly) + { + if(inOptions->mModules) + { + unsigned index = 0; + + /* + ** Find the module to modify. + */ + for(index = 0; index < moduleCount; index++) + { + if(0 == strcmp(modules[index].mModule, module)) + { + break; + } + } + + /* + ** If the index is the same as the count, we need to + ** add a new module. + */ + if(index == moduleCount) + { + void* moved = NULL; + + moved = realloc(modules, sizeof(ModuleStats) * (moduleCount + 1)); + if(NULL != moved) + { + modules = (ModuleStats*)moved; + moduleCount++; + + memset(modules + index, 0, sizeof(ModuleStats)); + modules[index].mModule = strdup(module); + if(NULL == modules[index].mModule) + { + retval = __LINE__; + ERROR_REPORT(retval, module, "Unable to duplicate string."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inOptions->mProgramName, "Unable to allocate module memory."); + } + } + + if(0 == retval) + { + if(CODE == segmentClass) + { + modules[index].mSize.mCode += size; + } + else if(DATA == segmentClass) + { + modules[index].mSize.mData += size; + } + } + } + } + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inOptions->mInputName, "Problem extracting values from file."); + } + } + + if(0 == retval && 0 != ferror(inOptions->mInput)) + { + retval = __LINE__; + ERROR_REPORT(retval, inOptions->mInputName, "Unable to read file."); + } + + /* + ** If all went well, time to report. + */ + if(0 == retval) + { + if(inOptions->mTotalOnly) + { + fprintf(inOptions->mOutput, "%u\n", (unsigned)(overall.mCode + overall.mData)); + } + else + { + fprintf(inOptions->mOutput, "Overall Size\n"); + fprintf(inOptions->mOutput, "\tTotal:\t%10u\n", (unsigned)(overall.mCode + overall.mData)); + fprintf(inOptions->mOutput, "\tCode:\t%10u\n", (unsigned)overall.mCode); + fprintf(inOptions->mOutput, "\tData:\t%10u\n", (unsigned)overall.mData); + } + + /* + ** Check options to see what else we should output. + */ + if(inOptions->mModules && moduleCount) + { + unsigned loop = 0; + + /* + ** Sort the modules by their size. + */ + qsort(modules, (size_t)moduleCount, sizeof(ModuleStats), moduleCompare); + + /* + ** Output each one. + ** Might as well clean up while we go too. + */ + for(loop = 0; loop < moduleCount; loop++) + { + fprintf(inOptions->mOutput, "\n"); + fprintf(inOptions->mOutput, "%s\n", modules[loop].mModule); + fprintf(inOptions->mOutput, "\tTotal:\t%10u\n", (unsigned)(modules[loop].mSize.mCode + modules[loop].mSize.mData)); + fprintf(inOptions->mOutput, "\tCode:\t%10u\n", (unsigned)modules[loop].mSize.mCode); + fprintf(inOptions->mOutput, "\tData:\t%10u\n", (unsigned)modules[loop].mSize.mData); + + CLEANUP(modules[loop].mModule); + } + + /* + ** Done with modules. + */ + CLEANUP(modules); + moduleCount = 0; + } + } + + return retval; +} + + +int initOptions(Options* outOptions, int inArgc, char** inArgv) +/* +** returns int 0 if successful. +*/ +{ + int retval = 0; + int loop = 0; + int switchLoop = 0; + int match = 0; + const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); + Switch* current = NULL; + + /* + ** Set any defaults. + */ + memset(outOptions, 0, sizeof(Options)); + outOptions->mProgramName = inArgv[0]; + outOptions->mInput = stdin; + outOptions->mInputName = strdup("stdin"); + outOptions->mOutput = stdout; + outOptions->mOutputName = strdup("stdout"); + outOptions->mMaxSize = 0xFFFFFFFFU; + + if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName) + { + retval = __LINE__; + ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup."); + } + + /* + ** Go through and attempt to do the right thing. + */ + for(loop = 1; loop < inArgc && 0 == retval; loop++) + { + match = 0; + current = NULL; + + for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++) + { + if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop])) + { + match = __LINE__; + } + else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop])) + { + match = __LINE__; + } + + if(match) + { + if(gSwitches[switchLoop]->mHasValue) + { + /* + ** Attempt to absorb next option to fullfill value. + */ + if(loop + 1 < inArgc) + { + loop++; + + current = gSwitches[switchLoop]; + current->mValue = inArgv[loop]; + } + } + else + { + current = gSwitches[switchLoop]; + } + + break; + } + } + + if(0 == match) + { + outOptions->mHelp = __LINE__; + retval = __LINE__; + ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch."); + } + else if(NULL == current) + { + outOptions->mHelp = __LINE__; + retval = __LINE__; + ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value."); + } + else + { + /* + ** Do something based on address/swtich. + */ + if(current == &gInputSwitch) + { + CLEANUP(outOptions->mInputName); + if(NULL != outOptions->mInput && stdin != outOptions->mInput) + { + fclose(outOptions->mInput); + outOptions->mInput = NULL; + } + + outOptions->mInput = fopen(current->mValue, "r"); + if(NULL == outOptions->mInput) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to open input file."); + } + else + { + outOptions->mInputName = strdup(current->mValue); + if(NULL == outOptions->mInputName) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to strdup."); + } + } + } + else if(current == &gOutputSwitch) + { + CLEANUP(outOptions->mOutputName); + if(NULL != outOptions->mOutput && stdout != outOptions->mOutput) + { + fclose(outOptions->mOutput); + outOptions->mOutput = NULL; + } + + outOptions->mOutput = fopen(current->mValue, "a"); + if(NULL == outOptions->mOutput) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to open output file."); + } + else + { + outOptions->mOutputName = strdup(current->mValue); + if(NULL == outOptions->mOutputName) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to strdup."); + } + } + } + else if(current == &gHelpSwitch) + { + outOptions->mHelp = __LINE__; + } + else if(current == &gModuleSwitch) + { + outOptions->mModules = __LINE__; + } + else if(current == &gTotalSwitch) + { + outOptions->mTotalOnly = __LINE__; + } + else if(current == &gMinSize) + { + unsigned long arg = 0; + char* endScan = NULL; + + errno = 0; + arg = strtoul(current->mValue, &endScan, 0); + if(0 == errno && endScan != current->mValue) + { + outOptions->mMinSize = arg; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to convert to a number."); + } + } + else if(current == &gMaxSize) + { + unsigned long arg = 0; + char* endScan = NULL; + + errno = 0; + arg = strtoul(current->mValue, &endScan, 0); + if(0 == errno && endScan != current->mValue) + { + outOptions->mMaxSize = arg; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to convert to a number."); + } + } + else if(current == &gMatchClass) + { + char* dupMatch = NULL; + + dupMatch = strdup(current->mValue); + if(NULL != dupMatch) + { + void* moved = NULL; + + moved = realloc(outOptions->mMatchClasses, sizeof(char*) * (outOptions->mMatchClassCount + 1)); + if(NULL != moved) + { + outOptions->mMatchClasses = (char**)moved; + outOptions->mMatchClasses[outOptions->mMatchClassCount] = dupMatch; + outOptions->mMatchClassCount++; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mLongName, "Unable to expand array."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to duplicate string."); + } + } + else if(current == &gMatchScope) + { + char* dupMatch = NULL; + + dupMatch = strdup(current->mValue); + if(NULL != dupMatch) + { + void* moved = NULL; + + moved = realloc(outOptions->mMatchScopes, sizeof(char*) * (outOptions->mMatchScopeCount + 1)); + if(NULL != moved) + { + outOptions->mMatchScopes = (char**)moved; + outOptions->mMatchScopes[outOptions->mMatchScopeCount] = dupMatch; + outOptions->mMatchScopeCount++; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mLongName, "Unable to expand array."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to duplicate string."); + } + } + else if(current == &gMatchModule) + { + char* dupMatch = NULL; + + dupMatch = strdup(current->mValue); + if(NULL != dupMatch) + { + void* moved = NULL; + + moved = realloc(outOptions->mMatchModules, sizeof(char*) * (outOptions->mMatchModuleCount + 1)); + if(NULL != moved) + { + outOptions->mMatchModules = (char**)moved; + outOptions->mMatchModules[outOptions->mMatchModuleCount] = dupMatch; + outOptions->mMatchModuleCount++; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mLongName, "Unable to expand array."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to duplicate string."); + } + } + else if(current == &gMatchSection) + { + char* dupMatch = NULL; + + dupMatch = strdup(current->mValue); + if(NULL != dupMatch) + { + void* moved = NULL; + + moved = realloc(outOptions->mMatchSections, sizeof(char*) * (outOptions->mMatchSectionCount + 1)); + if(NULL != moved) + { + outOptions->mMatchSections = (char**)moved; + outOptions->mMatchSections[outOptions->mMatchSectionCount] = dupMatch; + outOptions->mMatchSectionCount++; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mLongName, "Unable to expand array."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to duplicate string."); + } + } + else if(current == &gMatchObject) + { + char* dupMatch = NULL; + + dupMatch = strdup(current->mValue); + if(NULL != dupMatch) + { + void* moved = NULL; + + moved = realloc(outOptions->mMatchObjects, sizeof(char*) * (outOptions->mMatchObjectCount + 1)); + if(NULL != moved) + { + outOptions->mMatchObjects = (char**)moved; + outOptions->mMatchObjects[outOptions->mMatchObjectCount] = dupMatch; + outOptions->mMatchObjectCount++; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mLongName, "Unable to expand array."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to duplicate string."); + } + } + else if(current == &gMatchSymbol) + { + char* dupMatch = NULL; + + dupMatch = strdup(current->mValue); + if(NULL != dupMatch) + { + void* moved = NULL; + + moved = realloc(outOptions->mMatchSymbols, sizeof(char*) * (outOptions->mMatchSymbolCount + 1)); + if(NULL != moved) + { + outOptions->mMatchSymbols = (char**)moved; + outOptions->mMatchSymbols[outOptions->mMatchSymbolCount] = dupMatch; + outOptions->mMatchSymbolCount++; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mLongName, "Unable to expand array."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to duplicate string."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mLongName, "No handler for command line switch."); + } + } + } + + return retval; +} + + +void cleanOptions(Options* inOptions) +/* +** Clean up any open handles. +*/ +{ + unsigned loop = 0; + + CLEANUP(inOptions->mInputName); + if(NULL != inOptions->mInput && stdin != inOptions->mInput) + { + fclose(inOptions->mInput); + } + CLEANUP(inOptions->mOutputName); + if(NULL != inOptions->mOutput && stdout != inOptions->mOutput) + { + fclose(inOptions->mOutput); + } + + for(loop = 0; loop < inOptions->mMatchClassCount; loop++) + { + CLEANUP(inOptions->mMatchClasses[loop]); + } + CLEANUP(inOptions->mMatchClasses); + + for(loop = 0; loop < inOptions->mMatchScopeCount; loop++) + { + CLEANUP(inOptions->mMatchScopes[loop]); + } + CLEANUP(inOptions->mMatchScopes); + + for(loop = 0; loop < inOptions->mMatchModuleCount; loop++) + { + CLEANUP(inOptions->mMatchModules[loop]); + } + CLEANUP(inOptions->mMatchModules); + + for(loop = 0; loop < inOptions->mMatchSectionCount; loop++) + { + CLEANUP(inOptions->mMatchSections[loop]); + } + CLEANUP(inOptions->mMatchSections); + + for(loop = 0; loop < inOptions->mMatchObjectCount; loop++) + { + CLEANUP(inOptions->mMatchObjects[loop]); + } + CLEANUP(inOptions->mMatchObjects); + + for(loop = 0; loop < inOptions->mMatchSymbolCount; loop++) + { + CLEANUP(inOptions->mMatchSymbols[loop]); + } + CLEANUP(inOptions->mMatchSymbols); + + memset(inOptions, 0, sizeof(Options)); +} + + +void showHelp(Options* inOptions) +/* +** Show some simple help text on usage. +*/ +{ + int loop = 0; + const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); + const char* valueText = NULL; + + printf("usage:\t%s [arguments]\n", inOptions->mProgramName); + printf("\n"); + printf("arguments:\n"); + + for(loop = 0; loop < switchCount; loop++) + { + if(gSwitches[loop]->mHasValue) + { + valueText = " <value>"; + } + else + { + valueText = ""; + } + + printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText); + printf("\t %s%s", gSwitches[loop]->mShortName, valueText); + printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription); + } + + printf("This tool takes a tsv file and reports composite code and data sizes.\n"); +} + + +int main(int inArgc, char** inArgv) +{ + int retval = 0; + Options options; + + retval = initOptions(&options, inArgc, inArgv); + if(options.mHelp) + { + showHelp(&options); + } + else if(0 == retval) + { + retval = codesighs(&options); + } + + cleanOptions(&options); + return retval; +} + diff --git a/third_party/codesighs/codesighs.gyp b/third_party/codesighs/codesighs.gyp new file mode 100644 index 0000000..c3874f8 --- /dev/null +++ b/third_party/codesighs/codesighs.gyp @@ -0,0 +1,60 @@ +# Copyright (c) 2009 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'includes': [ + '../../build/common.gypi', + ], + 'targets': [ + { + 'target_name': 'codesighs', + 'type': 'executable', + 'sources': [ + 'codesighs.c', + ], + }, + { + 'target_name': 'maptsvdifftool', + 'type': 'executable', + 'sources': [ + 'maptsvdifftool.c', + ], + }, + ], + 'conditions': [ + ['OS=="win"', { + 'targets': [ + { + 'target_name': 'msmap2tsv', + 'type': 'executable', + 'sources': [ + 'msmap2tsv.c', + ], + 'link_settings': { + 'libraries': [ + '-lDbgHelp.lib', + ], + }, + }, + { + 'target_name': 'msdump2symdb', + 'type': 'executable', + 'sources': [ + 'msdump2symdb.c', + ], + }, + ], + }, { # else: OS != "windows" + 'targets': [ + { + 'target_name': 'nm2tsv', + 'type': 'executable', + 'sources': [ + 'nm2tsv.c', + ], + }, + ], + }], + ], +} diff --git a/third_party/codesighs/maptsvdifftool.c b/third_party/codesighs/maptsvdifftool.c new file mode 100644 index 0000000..8207060 --- /dev/null +++ b/third_party/codesighs/maptsvdifftool.c @@ -0,0 +1,1311 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is maptsvdifftool.c code, released + * Oct 3, 2002. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Garrett Arch Blythe, 03-October-2002 + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <ctype.h> + +#define ERROR_REPORT(num, val, msg) fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg)); +#define CLEANUP(ptr) do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0) + + +typedef struct __struct_Options +/* +** Options to control how we perform. +** +** mProgramName Used in help text. +** mInput File to read for input. +** Default is stdin. +** mInputName Name of the file. +** mOutput Output file, append. +** Default is stdout. +** mOutputName Name of the file. +** mHelp Whether or not help should be shown. +** mSummaryOnly Only output a signle line. +** mZeroDrift Output zero drift data. +** mNegation Perform negation heuristics on the symbol drifts. +*/ +{ + const char* mProgramName; + FILE* mInput; + char* mInputName; + FILE* mOutput; + char* mOutputName; + int mHelp; + int mSummaryOnly; + int mZeroDrift; + int mNegation; +} +Options; + + +typedef struct __struct_Switch +/* +** Command line options. +*/ +{ + const char* mLongName; + const char* mShortName; + int mHasValue; + const char* mValue; + const char* mDescription; +} +Switch; + +#define DESC_NEWLINE "\n\t\t" + +static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."}; +static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."}; +static Switch gSummarySwitch = {"--summary", "-s", 0, NULL, "Only output a single line." DESC_NEWLINE "The cumulative size changes." DESC_NEWLINE "Overrides all other output options."}; +static Switch gZeroDriftSwitch = {"--zerodrift", "-z", 0, NULL, "Output zero drift data." DESC_NEWLINE "Reports symbol changes even when there is no net drift."}; +static Switch gNegationSwitch = {"--negation", "-n", 0, NULL, "Use negation heuristics." DESC_NEWLINE "When symbol sizes are inferred by offset, order changes cause noise." DESC_NEWLINE "This helps see through the noise by eliminating equal and opposite drifts."}; +static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."}; + +static Switch* gSwitches[] = { + &gInputSwitch, + &gOutputSwitch, + &gSummarySwitch, + &gZeroDriftSwitch, + &gNegationSwitch, + &gHelpSwitch +}; + + +typedef struct __struct_SizeComposition +/* +** Used to keep which parts positive and negative resulted in the total. +*/ +{ + int mPositive; + int mNegative; +} +SizeComposition; + + +typedef struct __struct_SizeStats +/* +** Keep track of sizes. +** Use signed integers so that negatives are valid, in which case we shrunk. +*/ +{ + int mCode; + SizeComposition mCodeComposition; + + int mData; + SizeComposition mDataComposition; +} +SizeStats; + + +typedef enum __enum_SegmentClass +/* +** What type of data a segment holds. +*/ +{ + CODE, + DATA +} +SegmentClass; + + +typedef struct __struct_SymbolStats +/* +** Symbol level stats. +*/ +{ + char* mSymbol; + int mSize; +} +SymbolStats; + + +typedef struct __struct_ObjectStats +/* +** Object level stats. +*/ +{ + char* mObject; + int mSize; + SizeComposition mComposition; + SymbolStats* mSymbols; + unsigned mSymbolCount; +} +ObjectStats; + + +typedef struct __struct_SegmentStats +/* +** Segment level stats. +*/ +{ + char* mSegment; + SegmentClass mClass; + int mSize; + SizeComposition mComposition; + ObjectStats* mObjects; + unsigned mObjectCount; +} +SegmentStats; + + +typedef struct __struct_ModuleStats +/* +** Module level stats. +*/ +{ + char* mModule; + SizeStats mSize; + SegmentStats* mSegments; + unsigned mSegmentCount; +} +ModuleStats; + + +static int moduleCompare(const void* in1, const void* in2) +/* +** qsort helper. +*/ +{ + int retval = 0; + + ModuleStats* one = (ModuleStats*)in1; + ModuleStats* two = (ModuleStats*)in2; + + int oneSize = (one->mSize.mCode + one->mSize.mData); + int twoSize = (two->mSize.mCode + two->mSize.mData); + + if(oneSize < twoSize) + { + retval = 1; + } + else if(oneSize > twoSize) + { + retval = -1; + } + else + { + retval = strcmp(one->mModule, two->mModule); + if(0 > oneSize && 0 > twoSize) + { + retval *= -1; + } + } + + return retval; +} + + +static int segmentCompare(const void* in1, const void* in2) +/* +** qsort helper. +*/ +{ + int retval = 0; + + SegmentStats* one = (SegmentStats*)in1; + SegmentStats* two = (SegmentStats*)in2; + + if(one->mSize < two->mSize) + { + retval = 1; + } + else if(one->mSize > two->mSize) + { + retval = -1; + } + else + { + retval = strcmp(one->mSegment, two->mSegment); + if(0 > one->mSize && 0 > two->mSize) + { + retval *= -1; + } + } + + return retval; +} + + +static int objectCompare(const void* in1, const void* in2) +/* +** qsort helper. +*/ +{ + int retval = 0; + + ObjectStats* one = (ObjectStats*)in1; + ObjectStats* two = (ObjectStats*)in2; + + if(one->mSize < two->mSize) + { + retval = 1; + } + else if(one->mSize > two->mSize) + { + retval = -1; + } + else + { + retval = strcmp(one->mObject, two->mObject); + if(0 > one->mSize && 0 > two->mSize) + { + retval *= -1; + } + } + + return retval; +} + + +static int symbolCompare(const void* in1, const void* in2) +/* +** qsort helper. +*/ +{ + int retval = 0; + + SymbolStats* one = (SymbolStats*)in1; + SymbolStats* two = (SymbolStats*)in2; + + if(one->mSize < two->mSize) + { + retval = 1; + } + else if(one->mSize > two->mSize) + { + retval = -1; + } + else + { + retval = strcmp(one->mSymbol, two->mSymbol); + if(0 > one->mSize && 0 > two->mSize) + { + retval *= -1; + } + } + + return retval; +} + + +void trimWhite(char* inString) +/* +** Remove any whitespace from the end of the string. +*/ +{ + int len = strlen(inString); + + while(len) + { + len--; + + if(isspace(*(inString + len))) + { + *(inString + len) = '\0'; + } + else + { + break; + } + } +} + + +int difftool(Options* inOptions) +/* +** Read a diff file and spit out relevant information. +*/ +{ + int retval = 0; + char lineBuffer[0x500]; + SizeStats overall; + ModuleStats* modules = NULL; + unsigned moduleCount = 0; + unsigned moduleLoop = 0; + ModuleStats* theModule = NULL; + unsigned segmentLoop = 0; + SegmentStats* theSegment = NULL; + unsigned objectLoop = 0; + ObjectStats* theObject = NULL; + unsigned symbolLoop = 0; + SymbolStats* theSymbol = NULL; + unsigned allSymbolCount = 0; + + memset(&overall, 0, sizeof(overall)); + + /* + ** Read the entire diff file. + ** We're only interested in lines beginning with < or > + */ + while(0 == retval && NULL != fgets(lineBuffer, sizeof(lineBuffer), inOptions->mInput)) + { + trimWhite(lineBuffer); + + if(('<' == lineBuffer[0] || '>' == lineBuffer[0]) && ' ' == lineBuffer[1]) + { + int additive = 0; + char* theLine = &lineBuffer[2]; + int scanRes = 0; + int size; + char segClass[0x10]; + char scope[0x10]; + char module[0x100]; + char segment[0x40]; + char object[0x100]; + char* symbol = NULL; + + /* + ** Figure out if the line adds or subtracts from something. + */ + if('>' == lineBuffer[0]) + { + additive = __LINE__; + } + + + /* + ** Scan the line for information. + */ + scanRes = sscanf(theLine, + "%x\t%s\t%s\t%s\t%s\t%s\t", + (unsigned*)&size, + segClass, + scope, + module, + segment, + object); + + if(6 == scanRes) + { + SegmentClass segmentClass = DATA; + + symbol = strrchr(theLine, '\t') + 1; + + if(0 == strcmp(segClass, "CODE")) + { + segmentClass = CODE; + } + else if(0 == strcmp(segClass, "DATA")) + { + segmentClass = DATA; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, segClass, "Unable to determine segment class."); + } + + if(0 == retval) + { + unsigned moduleIndex = 0; + + /* + ** Find, in succession, the following things: + ** the module + ** the segment + ** the object + ** the symbol + ** Failure to find any one of these means to create it. + */ + + for(moduleIndex = 0; moduleIndex < moduleCount; moduleIndex++) + { + if(0 == strcmp(modules[moduleIndex].mModule, module)) + { + break; + } + } + + if(moduleIndex == moduleCount) + { + void* moved = NULL; + + moved = realloc(modules, sizeof(ModuleStats) * (1 + moduleCount)); + if(NULL != moved) + { + modules = (ModuleStats*)moved; + moduleCount++; + memset(modules + moduleIndex, 0, sizeof(ModuleStats)); + + modules[moduleIndex].mModule = strdup(module); + if(NULL == modules[moduleIndex].mModule) + { + retval = __LINE__; + ERROR_REPORT(retval, module, "Unable to duplicate string."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inOptions->mProgramName, "Unable to increase module array."); + } + } + + if(0 == retval) + { + unsigned segmentIndex = 0; + theModule = (modules + moduleIndex); + + for(segmentIndex = 0; segmentIndex < theModule->mSegmentCount; segmentIndex++) + { + if(0 == strcmp(segment, theModule->mSegments[segmentIndex].mSegment)) + { + break; + } + } + + if(segmentIndex == theModule->mSegmentCount) + { + void* moved = NULL; + + moved = realloc(theModule->mSegments, sizeof(SegmentStats) * (theModule->mSegmentCount + 1)); + if(NULL != moved) + { + theModule->mSegments = (SegmentStats*)moved; + theModule->mSegmentCount++; + memset(theModule->mSegments + segmentIndex, 0, sizeof(SegmentStats)); + + theModule->mSegments[segmentIndex].mClass = segmentClass; + theModule->mSegments[segmentIndex].mSegment = strdup(segment); + if(NULL == theModule->mSegments[segmentIndex].mSegment) + { + retval = __LINE__; + ERROR_REPORT(retval, segment, "Unable to duplicate string."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inOptions->mProgramName, "Unable to increase segment array."); + } + } + + if(0 == retval) + { + unsigned objectIndex = 0; + theSegment = (theModule->mSegments + segmentIndex); + + for(objectIndex = 0; objectIndex < theSegment->mObjectCount; objectIndex++) + { + if(0 == strcmp(object, theSegment->mObjects[objectIndex].mObject)) + { + break; + } + } + + if(objectIndex == theSegment->mObjectCount) + { + void* moved = NULL; + + moved = realloc(theSegment->mObjects, sizeof(ObjectStats) * (1 + theSegment->mObjectCount)); + if(NULL != moved) + { + theSegment->mObjects = (ObjectStats*)moved; + theSegment->mObjectCount++; + memset(theSegment->mObjects + objectIndex, 0, sizeof(ObjectStats)); + + theSegment->mObjects[objectIndex].mObject = strdup(object); + if(NULL == theSegment->mObjects[objectIndex].mObject) + { + retval = __LINE__; + ERROR_REPORT(retval, object, "Unable to duplicate string."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inOptions->mProgramName, "Unable to increase object array."); + } + } + + if(0 == retval) + { + unsigned symbolIndex = 0; + theObject = (theSegment->mObjects + objectIndex); + + for(symbolIndex = 0; symbolIndex < theObject->mSymbolCount; symbolIndex++) + { + if(0 == strcmp(symbol, theObject->mSymbols[symbolIndex].mSymbol)) + { + break; + } + } + + if(symbolIndex == theObject->mSymbolCount) + { + void* moved = NULL; + + moved = realloc(theObject->mSymbols, sizeof(SymbolStats) * (1 + theObject->mSymbolCount)); + if(NULL != moved) + { + theObject->mSymbols = (SymbolStats*)moved; + theObject->mSymbolCount++; + allSymbolCount++; + memset(theObject->mSymbols + symbolIndex, 0, sizeof(SymbolStats)); + + theObject->mSymbols[symbolIndex].mSymbol = strdup(symbol); + if(NULL == theObject->mSymbols[symbolIndex].mSymbol) + { + retval = __LINE__; + ERROR_REPORT(retval, symbol, "Unable to duplicate string."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inOptions->mProgramName, "Unable to increase symbol array."); + } + } + + if(0 == retval) + { + theSymbol = (theObject->mSymbols + symbolIndex); + + /* + ** Update our various totals. + */ + if(additive) + { + if(CODE == segmentClass) + { + overall.mCode += size; + theModule->mSize.mCode += size; + } + else if(DATA == segmentClass) + { + overall.mData += size; + theModule->mSize.mData += size; + } + + theSegment->mSize += size; + theObject->mSize += size; + theSymbol->mSize += size; + } + else + { + if(CODE == segmentClass) + { + overall.mCode -= size; + theModule->mSize.mCode -= size; + } + else if(DATA == segmentClass) + { + overall.mData -= size; + theModule->mSize.mData -= size; + } + + theSegment->mSize -= size; + theObject->mSize -= size; + theSymbol->mSize -= size; + } + } + } + } + } + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inOptions->mInputName, "Unable to scan line data."); + } + } + } + + if(0 == retval && 0 != ferror(inOptions->mInput)) + { + retval = __LINE__; + ERROR_REPORT(retval, inOptions->mInputName, "Unable to read file."); + } + + /* + ** Next, it is time to perform revisionist history of sorts. + ** If the negation switch is in play, we perfrom the following + ** aggressive steps: + ** + ** For each section, find size changes which have an equal and + ** opposite change, and set them both to zero. + ** However, you can only do this if the number of negating changes + ** is even, as if it is odd, then any one of the many could be + ** at fault for the actual change. + ** + ** This orginally exists to make the win32 codesighs reports more + ** readable/meaningful. + */ + if(0 == retval && 0 != inOptions->mNegation) + { + ObjectStats** objArray = NULL; + SymbolStats** symArray = NULL; + + /* + ** Create arrays big enough to hold all symbols. + ** As well as an array to keep the owning object at the same index. + ** We will keep the object around as we may need to modify the size. + */ + objArray = (ObjectStats**)malloc(allSymbolCount * sizeof(ObjectStats*)); + symArray = (SymbolStats**)malloc(allSymbolCount * sizeof(SymbolStats*)); + if(NULL == objArray || NULL == symArray) + { + retval = __LINE__; + ERROR_REPORT(retval, inOptions->mProgramName, "Unable to allocate negation array memory."); + } + else + { + unsigned arrayCount = 0; + unsigned arrayLoop = 0; + + /* + ** Go through and perform the steps on each section/segment. + */ + for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++) + { + theModule = modules + moduleLoop; + + for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++) + { + theSegment = theModule->mSegments + segmentLoop; + + /* + ** Collect all symbols under this section. + ** The symbols are spread out between all the objects, + ** so keep track of both independently at the + ** same index. + */ + arrayCount = 0; + + for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++) + { + theObject = theSegment->mObjects + objectLoop; + + for(symbolLoop = 0; symbolLoop < theObject->mSymbolCount; symbolLoop++) + { + theSymbol = theObject->mSymbols + symbolLoop; + + objArray[arrayCount] = theObject; + symArray[arrayCount] = theSymbol; + arrayCount++; + } + } + + /* + ** Now that we have a list of symbols, go through each + ** and see if there is a chance of negation. + */ + for(arrayLoop = 0; arrayLoop < arrayCount; arrayLoop++) + { + /* + ** If the item is NULL, it was already negated. + ** Don't do this for items with a zero size. + */ + if(NULL != symArray[arrayLoop] && 0 != symArray[arrayLoop]->mSize) + { + unsigned identicalValues = 0; + unsigned oppositeValues = 0; + unsigned lookLoop = 0; + const int lookingFor = symArray[arrayLoop]->mSize; + + /* + ** Count the number of items with this value. + ** Count the number of items with the opposite equal value. + ** If they are equal, go through and negate all sizes. + */ + for(lookLoop = arrayLoop; lookLoop < arrayCount; lookLoop++) + { + /* + ** Skip negated items. + ** Skip zero length items. + */ + if(NULL == symArray[lookLoop] || 0 == symArray[lookLoop]->mSize) + { + continue; + } + + if(lookingFor == symArray[lookLoop]->mSize) + { + identicalValues++; + } + else if((-1 * lookingFor) == symArray[lookLoop]->mSize) + { + oppositeValues++; + } + } + + if(0 != identicalValues && identicalValues == oppositeValues) + { + unsigned negationLoop = 0; + + for(negationLoop = arrayLoop; 0 != identicalValues || 0 != oppositeValues; negationLoop++) + { + /* + ** Skip negated items. + ** Skip zero length items. + */ + if(NULL == symArray[negationLoop] || 0 == symArray[negationLoop]->mSize) + { + continue; + } + + /* + ** Negate any size matches. + ** Reflect the change in the object as well. + ** Clear the symbol. + */ + if(lookingFor == symArray[negationLoop]->mSize) + { + objArray[negationLoop]->mSize -= lookingFor; + symArray[negationLoop]->mSize = 0; + symArray[negationLoop] = NULL; + + identicalValues--; + } + else if((-1 * lookingFor) == symArray[negationLoop]->mSize) + { + objArray[negationLoop]->mSize += lookingFor; + symArray[negationLoop]->mSize = 0; + symArray[negationLoop] = NULL; + + oppositeValues--; + } + } + } + } + } + } + } + } + + CLEANUP(objArray); + CLEANUP(symArray); + } + + + /* + ** If all went well, time to report. + */ + if(0 == retval) + { + /* + ** Loop through our data once more, so that the symbols can + ** propigate their changes upwards in a positive/negative + ** fashion. + ** This will help give the composite change more meaning. + */ + for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++) + { + theModule = modules + moduleLoop; + + /* + ** Skip if there is zero drift, or no net change. + */ + if(0 == inOptions->mZeroDrift && 0 == (theModule->mSize.mCode + theModule->mSize.mData)) + { + continue; + } + + for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++) + { + theSegment = theModule->mSegments + segmentLoop; + + /* + ** Skip if there is zero drift, or no net change. + */ + if(0 == inOptions->mZeroDrift && 0 == theSegment->mSize) + { + continue; + } + + for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++) + { + theObject = theSegment->mObjects + objectLoop; + + /* + ** Skip if there is zero drift, or no net change. + */ + if(0 == inOptions->mZeroDrift && 0 == theObject->mSize) + { + continue; + } + + for(symbolLoop = 0; symbolLoop < theObject->mSymbolCount; symbolLoop++) + { + theSymbol = theObject->mSymbols + symbolLoop; + + /* + ** Propagate the composition all the way to the top. + ** Sizes of zero change are skipped. + */ + if(0 < theSymbol->mSize) + { + theObject->mComposition.mPositive += theSymbol->mSize; + theSegment->mComposition.mPositive += theSymbol->mSize; + if(CODE == theSegment->mClass) + { + overall.mCodeComposition.mPositive += theSymbol->mSize; + theModule->mSize.mCodeComposition.mPositive += theSymbol->mSize; + } + else if(DATA == theSegment->mClass) + { + overall.mDataComposition.mPositive += theSymbol->mSize; + theModule->mSize.mDataComposition.mPositive += theSymbol->mSize; + } + } + else if(0 > theSymbol->mSize) + { + theObject->mComposition.mNegative += theSymbol->mSize; + theSegment->mComposition.mNegative += theSymbol->mSize; + if(CODE == theSegment->mClass) + { + overall.mCodeComposition.mNegative += theSymbol->mSize; + theModule->mSize.mCodeComposition.mNegative += theSymbol->mSize; + } + else if(DATA == theSegment->mClass) + { + overall.mDataComposition.mNegative += theSymbol->mSize; + theModule->mSize.mDataComposition.mNegative += theSymbol->mSize; + } + } + } + } + } + } + + + if(inOptions->mSummaryOnly) + { + fprintf(inOptions->mOutput, "%+d (%+d/%+d)\n", overall.mCode + overall.mData, overall.mCodeComposition.mPositive + overall.mDataComposition.mPositive, overall.mCodeComposition.mNegative + overall.mDataComposition.mNegative); + } + else + { + fprintf(inOptions->mOutput, "Overall Change in Size\n"); + fprintf(inOptions->mOutput, "\tTotal:\t%+11d (%+d/%+d)\n", overall.mCode + overall.mData, overall.mCodeComposition.mPositive + overall.mDataComposition.mPositive, overall.mCodeComposition.mNegative + overall.mDataComposition.mNegative); + fprintf(inOptions->mOutput, "\tCode:\t%+11d (%+d/%+d)\n", overall.mCode, overall.mCodeComposition.mPositive, overall.mCodeComposition.mNegative); + fprintf(inOptions->mOutput, "\tData:\t%+11d (%+d/%+d)\n", overall.mData, overall.mDataComposition.mPositive, overall.mDataComposition.mNegative); + } + + /* + ** Check what else we should output. + */ + if(0 == inOptions->mSummaryOnly && NULL != modules && moduleCount) + { + const char* segmentType = NULL; + + /* + ** We're going to sort everything. + */ + qsort(modules, moduleCount, sizeof(ModuleStats), moduleCompare); + for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++) + { + theModule = modules + moduleLoop; + + qsort(theModule->mSegments, theModule->mSegmentCount, sizeof(SegmentStats), segmentCompare); + + for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++) + { + theSegment = theModule->mSegments + segmentLoop; + + qsort(theSegment->mObjects, theSegment->mObjectCount, sizeof(ObjectStats), objectCompare); + + for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++) + { + theObject = theSegment->mObjects + objectLoop; + + qsort(theObject->mSymbols, theObject->mSymbolCount, sizeof(SymbolStats), symbolCompare); + } + } + } + + /* + ** Loop through for output. + */ + for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++) + { + theModule = modules + moduleLoop; + + /* + ** Skip if there is zero drift, or no net change. + */ + if(0 == inOptions->mZeroDrift && 0 == (theModule->mSize.mCode + theModule->mSize.mData)) + { + continue; + } + + fprintf(inOptions->mOutput, "\n"); + fprintf(inOptions->mOutput, "%s\n", theModule->mModule); + fprintf(inOptions->mOutput, "\tTotal:\t%+11d (%+d/%+d)\n", theModule->mSize.mCode + theModule->mSize.mData, theModule->mSize.mCodeComposition.mPositive + theModule->mSize.mDataComposition.mPositive, theModule->mSize.mCodeComposition.mNegative + theModule->mSize.mDataComposition.mNegative); + fprintf(inOptions->mOutput, "\tCode:\t%+11d (%+d/%+d)\n", theModule->mSize.mCode, theModule->mSize.mCodeComposition.mPositive, theModule->mSize.mCodeComposition.mNegative); + fprintf(inOptions->mOutput, "\tData:\t%+11d (%+d/%+d)\n", theModule->mSize.mData, theModule->mSize.mDataComposition.mPositive, theModule->mSize.mDataComposition.mNegative); + + for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++) + { + theSegment = theModule->mSegments + segmentLoop; + + /* + ** Skip if there is zero drift, or no net change. + */ + if(0 == inOptions->mZeroDrift && 0 == theSegment->mSize) + { + continue; + } + + if(CODE == theSegment->mClass) + { + segmentType = "CODE"; + } + else if(DATA == theSegment->mClass) + { + segmentType = "DATA"; + } + + fprintf(inOptions->mOutput, "\t%+11d (%+d/%+d)\t%s (%s)\n", theSegment->mSize, theSegment->mComposition.mPositive, theSegment->mComposition.mNegative, theSegment->mSegment, segmentType); + + for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++) + { + theObject = theSegment->mObjects + objectLoop; + + /* + ** Skip if there is zero drift, or no net change. + */ + if(0 == inOptions->mZeroDrift && 0 == theObject->mSize) + { + continue; + } + + fprintf(inOptions->mOutput, "\t\t%+11d (%+d/%+d)\t%s\n", theObject->mSize, theObject->mComposition.mPositive, theObject->mComposition.mNegative, theObject->mObject); + + for(symbolLoop = 0; symbolLoop < theObject->mSymbolCount; symbolLoop++) + { + theSymbol = theObject->mSymbols + symbolLoop; + + /* + ** Skip if there is zero drift, or no net change. + */ + if(0 == inOptions->mZeroDrift && 0 == theSymbol->mSize) + { + continue; + } + + fprintf(inOptions->mOutput, "\t\t\t%+11d\t%s\n", theSymbol->mSize, theSymbol->mSymbol); + } + } + } + } + } + } + + /* + ** Cleanup time. + */ + for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++) + { + theModule = modules + moduleLoop; + + for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++) + { + theSegment = theModule->mSegments + segmentLoop; + + for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++) + { + theObject = theSegment->mObjects + objectLoop; + + for(symbolLoop = 0; symbolLoop < theObject->mSymbolCount; symbolLoop++) + { + theSymbol = theObject->mSymbols + symbolLoop; + + CLEANUP(theSymbol->mSymbol); + } + + CLEANUP(theObject->mSymbols); + CLEANUP(theObject->mObject); + } + + CLEANUP(theSegment->mObjects); + CLEANUP(theSegment->mSegment); + } + + CLEANUP(theModule->mSegments); + CLEANUP(theModule->mModule); + } + CLEANUP(modules); + + return retval; +} + + +int initOptions(Options* outOptions, int inArgc, char** inArgv) +/* +** returns int 0 if successful. +*/ +{ + int retval = 0; + int loop = 0; + int switchLoop = 0; + int match = 0; + const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); + Switch* current = NULL; + + /* + ** Set any defaults. + */ + memset(outOptions, 0, sizeof(Options)); + outOptions->mProgramName = inArgv[0]; + outOptions->mInput = stdin; + outOptions->mInputName = strdup("stdin"); + outOptions->mOutput = stdout; + outOptions->mOutputName = strdup("stdout"); + + if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName) + { + retval = __LINE__; + ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup."); + } + + /* + ** Go through and attempt to do the right thing. + */ + for(loop = 1; loop < inArgc && 0 == retval; loop++) + { + match = 0; + current = NULL; + + for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++) + { + if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop])) + { + match = __LINE__; + } + else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop])) + { + match = __LINE__; + } + + if(match) + { + if(gSwitches[switchLoop]->mHasValue) + { + /* + ** Attempt to absorb next option to fullfill value. + */ + if(loop + 1 < inArgc) + { + loop++; + + current = gSwitches[switchLoop]; + current->mValue = inArgv[loop]; + } + } + else + { + current = gSwitches[switchLoop]; + } + + break; + } + } + + if(0 == match) + { + outOptions->mHelp = __LINE__; + retval = __LINE__; + ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch."); + } + else if(NULL == current) + { + outOptions->mHelp = __LINE__; + retval = __LINE__; + ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value."); + } + else + { + /* + ** Do something based on address/swtich. + */ + if(current == &gInputSwitch) + { + CLEANUP(outOptions->mInputName); + if(NULL != outOptions->mInput && stdin != outOptions->mInput) + { + fclose(outOptions->mInput); + outOptions->mInput = NULL; + } + + outOptions->mInput = fopen(current->mValue, "r"); + if(NULL == outOptions->mInput) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to open input file."); + } + else + { + outOptions->mInputName = strdup(current->mValue); + if(NULL == outOptions->mInputName) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to strdup."); + } + } + } + else if(current == &gOutputSwitch) + { + CLEANUP(outOptions->mOutputName); + if(NULL != outOptions->mOutput && stdout != outOptions->mOutput) + { + fclose(outOptions->mOutput); + outOptions->mOutput = NULL; + } + + outOptions->mOutput = fopen(current->mValue, "a"); + if(NULL == outOptions->mOutput) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to open output file."); + } + else + { + outOptions->mOutputName = strdup(current->mValue); + if(NULL == outOptions->mOutputName) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to strdup."); + } + } + } + else if(current == &gHelpSwitch) + { + outOptions->mHelp = __LINE__; + } + else if(current == &gSummarySwitch) + { + outOptions->mSummaryOnly = __LINE__; + } + else if(current == &gZeroDriftSwitch) + { + outOptions->mZeroDrift = __LINE__; + } + else if(current == &gNegationSwitch) + { + outOptions->mNegation = __LINE__; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mLongName, "No handler for command line switch."); + } + } + } + + return retval; +} + + +void cleanOptions(Options* inOptions) +/* +** Clean up any open handles. +*/ +{ + CLEANUP(inOptions->mInputName); + if(NULL != inOptions->mInput && stdin != inOptions->mInput) + { + fclose(inOptions->mInput); + } + CLEANUP(inOptions->mOutputName); + if(NULL != inOptions->mOutput && stdout != inOptions->mOutput) + { + fclose(inOptions->mOutput); + } + + memset(inOptions, 0, sizeof(Options)); +} + + +void showHelp(Options* inOptions) +/* +** Show some simple help text on usage. +*/ +{ + int loop = 0; + const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); + const char* valueText = NULL; + + printf("usage:\t%s [arguments]\n", inOptions->mProgramName); + printf("\n"); + printf("arguments:\n"); + + for(loop = 0; loop < switchCount; loop++) + { + if(gSwitches[loop]->mHasValue) + { + valueText = " <value>"; + } + else + { + valueText = ""; + } + + printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText); + printf("\t %s%s", gSwitches[loop]->mShortName, valueText); + printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription); + } + + printf("This tool takes the diff of two sorted tsv files to form a summary report\n"); + printf("of code and data size changes which is hoped to be human readable.\n"); +} + + +int main(int inArgc, char** inArgv) +{ + int retval = 0; + Options options; + + retval = initOptions(&options, inArgc, inArgv); + if(options.mHelp) + { + showHelp(&options); + } + else if(0 == retval) + { + retval = difftool(&options); + } + + cleanOptions(&options); + return retval; +} + diff --git a/third_party/codesighs/msdump2symdb.c b/third_party/codesighs/msdump2symdb.c new file mode 100644 index 0000000..705c3c4 --- /dev/null +++ b/third_party/codesighs/msdump2symdb.c @@ -0,0 +1,1090 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is msdump2symdb.c code, released + * Jan 16, 2003. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Garrett Arch Blythe, 16-January-2003 + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <ctype.h> +#include <errno.h> + +#define ERROR_REPORT(num, val, msg) fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg)); +#define CLEANUP(ptr) do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0) + + +typedef struct __struct_Options +/* +** Options to control how we perform. +** +** mProgramName Used in help text. +** mInput File to read for input. +** Default is stdin. +** mInputName Name of the file. +** mOutput Output file, append. +** Default is stdout. +** mOutputName Name of the file. +** mHelp Whether or not help should be shown. +*/ +{ + const char* mProgramName; + FILE* mInput; + char* mInputName; + FILE* mOutput; + char* mOutputName; + int mHelp; +} +Options; + + +typedef struct __struct_Switch +/* +** Command line options. +*/ +{ + const char* mLongName; + const char* mShortName; + int mHasValue; + const char* mValue; + const char* mDescription; +} +Switch; + +#define DESC_NEWLINE "\n\t\t" + +static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."}; +static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."}; +static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."}; + +static Switch* gSwitches[] = { + &gInputSwitch, + &gOutputSwitch, + &gHelpSwitch +}; + + +typedef struct __struct_MSDump_Symbol +/* +** Struct to hold infomration on a symbol. +** +** mSize Size of the symbol once all work is complete. +** mOffset Offset of the symbol in the section. +** mName Symbolic name. +*/ +{ + unsigned mSize; + unsigned mOffset; + char* mName; +} +MSDump_Symbol; + + +typedef struct __struct_MSDump_Section +/* +** Struct for holding information on a section. +** +** mLength Length of the section in bytes. +** mUsed Number of bytes used in the section thus far. +** Should eventually match mLength after work is done. +** mType Type of section, as string (.data, .text, et. al.) +** mSymbols Symbols found inside the section. +** mSymbolCount Number of symbols in array. +*/ +{ + unsigned mLength; + unsigned mUsed; + char* mType; + + MSDump_Symbol* mSymbols; + unsigned mSymbolCount; +} +MSDump_Section; + + +typedef struct __struct_MSDump_Object +/* +** Struct for holding object's data. +*/ +{ + char* mObject; + + MSDump_Section* mSections; + unsigned mSectionCount; +} +MSDump_Object; + + +typedef struct __struct_MSDump_ReadState +/* +** State flags while reading the input gives us hints on what to do. +** +** mSkipLines Number of lines to skip without parsing. +** mSectionDetails Section information next, like line length. +** mCurrentObject Object file we are dealing with. +*/ +{ + unsigned mSkipLines; + unsigned mSectionDetails; + MSDump_Object* mCurrentObject; +} +MSDump_ReadState; + + +typedef struct __struct_MSDump_Container +/* +** Umbrella container for all data encountered. +*/ +{ + MSDump_ReadState mReadState; + + MSDump_Object* mObjects; + unsigned mObjectCount; +} +MSDump_Container; + + +void trimWhite(char* inString) +/* +** Remove any whitespace from the end of the string. +*/ +{ + int len = strlen(inString); + + while(len) + { + len--; + + if(isspace(*(inString + len))) + { + *(inString + len) = '\0'; + } + else + { + break; + } + } +} + + +const char* skipWhite(const char* inString) +/* +** Return pointer to first non white space character. +*/ +{ + const char* retval = inString; + + while('\0' != *retval && isspace(*retval)) + { + retval++; + } + + return retval; +} + + +const char* skipNonWhite(const char* inString) +/* +** Return pointer to first white space character. +*/ +{ + const char* retval = inString; + + while('\0' != *retval && !isspace(*retval)) + { + retval++; + } + + return retval; +} + + +void slash2bs(char* inString) +/* +** Change any forward slash to a backslash. +*/ +{ + char* slash = inString; + + while(NULL != (slash = strchr(slash, '/'))) + { + *slash = '\\'; + slash++; + } +} + + +const char* skipToArg(const char* inString, unsigned inArgIndex) +/* +** Return pointer either to the arg or NULL. +** 1 indexed. +*/ +{ + const char* retval = NULL; + + while(0 != inArgIndex && '\0' != *inString) + { + inArgIndex--; + + inString = skipWhite(inString); + if(0 != inArgIndex) + { + inString = skipNonWhite(inString); + } + } + + if('\0' != *inString) + { + retval = inString; + } + + return retval; +} + + +const char* getLastArg(const char* inString) +/* +** Return pointer to last arg in string. +*/ +{ + const char* retval = NULL; + int length = 0; + int sawString = 0; + + length = strlen(inString); + while(0 != length) + { + length--; + + if(0 == sawString) + { + if(0 == isspace(inString[length])) + { + sawString = __LINE__; + } + } + else + { + if(0 != isspace(inString[length])) + { + retval = inString + length + 1; + } + } + } + + return retval; +} + + +int processLine(Options* inOptions, MSDump_Container* inContainer, const char* inLine) +/* +** Handle one line at a time. +** Looking for several different types of lines. +** Ignore all other lines. +** The container is the state machine. +** returns 0 on no error. +*/ +{ + int retval = 0; + + /* + ** Check to see if we were expecting section details. + */ + if(0 != inContainer->mReadState.mSectionDetails) + { + const char* length = NULL; + unsigned sectionIndex = 0; + + /* + ** Detail is a 1 based index.... + ** Reset. + */ + sectionIndex = inContainer->mReadState.mSectionDetails - 1; + inContainer->mReadState.mSectionDetails = 0; + + if(0 == strncmp(" Section length", inLine, 18)) + { + const char* sectionLength = NULL; + unsigned numericLength = 0; + char* endScan = NULL; + + sectionLength = skipWhite(inLine + 18); + + errno = 0; + numericLength = strtoul(sectionLength, &endScan, 16); + if(0 == errno && endScan != sectionLength) + { + inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mLength = numericLength; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inLine, "Cannot scan for section length."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inLine, "Cannot parse section line."); + } + } + /* + ** Check for switching object file symbols. + */ + else if(0 == strncmp("Dump of file ", inLine, 13)) + { + const char* dupMe = inLine + 13; + char* dup = NULL; + + dup = strdup(dupMe); + if(NULL != dup) + { + void* growth = NULL; + + trimWhite(dup); + slash2bs(dup); + + + growth = realloc(inContainer->mObjects, (inContainer->mObjectCount + 1) * sizeof(MSDump_Object)); + if(NULL != growth) + { + unsigned int index = inContainer->mObjectCount; + + inContainer->mObjectCount++; + inContainer->mObjects = growth; + memset(inContainer->mObjects + index, 0, sizeof(MSDump_Object)); + + inContainer->mObjects[index].mObject = dup; + + /* + ** Reset the read state for this new object. + */ + memset(&inContainer->mReadState, 0, sizeof(MSDump_ReadState)); + + /* + ** Record our current object file. + */ + inContainer->mReadState.mCurrentObject = inContainer->mObjects + index; + + /* + ** We can skip a few lines. + */ + inContainer->mReadState.mSkipLines = 4; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, dup, "Unable to grow object array."); + free(dup); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, dupMe, "Unable to copy string."); + + } + } + /* + ** Check for a symbol dump or a section header. + */ + else if(isxdigit(*inLine) && isxdigit(*(inLine + 1)) && isxdigit(*(inLine + 2))) + { + const char* sectionString = NULL; + + /* + ** Determine the section for this line. + ** Ignore DEBUG sections. + */ + sectionString = skipToArg(inLine, 3); + if(NULL != sectionString) + { + if(0 != strncmp(sectionString, "DEBUG", 5) && 0 != strncmp(sectionString, "ABS", 3) && 0 != strncmp(sectionString, "UNDEF", 5)) + { + /* + ** MUST start with "SECT" + */ + if(0 == strncmp(sectionString, "SECT", 4)) + { + unsigned sectionIndex1 = 0; + + char *endScan = NULL; + + sectionString += 4; + + /* + ** Convert the remaining string to an index. + ** It will be 1 based. + */ + errno = 0; + sectionIndex1 = strtoul(sectionString, &endScan, 16); + if(0 == errno && endScan != sectionString && 0 != sectionIndex1) + { + unsigned sectionIndex = sectionIndex1 - 1; + + /* + ** Is this a new section? Assumed to be ascending. + ** Or is this a symbol in the section? + */ + if(sectionIndex1 > inContainer->mReadState.mCurrentObject->mSectionCount) + { + const char* typeArg = NULL; + + /* + ** New Section, figure out the type. + */ + typeArg = skipToArg(sectionString, 5); + if(NULL != typeArg) + { + char* typeDup = NULL; + + /* + ** Skip the leading period before duping. + */ + if('.' == *typeArg) + { + typeArg++; + } + typeDup = strdup(typeArg); + + if(NULL != typeDup) + { + void* moved = NULL; + char* nonWhite = NULL; + + /* + ** Terminate the duplicate after the section type. + */ + nonWhite = (char*)skipNonWhite(typeDup); + if(NULL != nonWhite) + { + *nonWhite = '\0'; + } + + /* + ** Create more space for the section in the object... + */ + moved = realloc(inContainer->mReadState.mCurrentObject->mSections, sizeof(MSDump_Section) * sectionIndex1); + if(NULL != moved) + { + unsigned oldCount = inContainer->mReadState.mCurrentObject->mSectionCount; + + inContainer->mReadState.mCurrentObject->mSections = (MSDump_Section*)moved; + inContainer->mReadState.mCurrentObject->mSectionCount = sectionIndex1; + memset(&inContainer->mReadState.mCurrentObject->mSections[oldCount], 0, sizeof(MSDump_Section) * (sectionIndex1 - oldCount)); + + /* + ** Other section details. + */ + inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mType = typeDup; + + + /* + ** Mark it so that we look for the length on the next line. + ** This happens on next entry into the read state. + */ + inContainer->mReadState.mSectionDetails = sectionIndex1; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inLine, "Unable to grow for new section."); + free(typeDup); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, typeArg, "Unable to duplicate type."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inLine, "Unable to determine section type."); + } + + } + else + { + const char* offsetArg = NULL; + const char* classArg = NULL; + unsigned classWords = 1; + const char* symbolArg = NULL; + + /* + ** This is an section we've seen before, and must list a symbol. + ** Figure out the things we want to know about the symbol, e.g. size. + ** We will ignore particular classes of symbols. + */ + + offsetArg = skipToArg(inLine, 2); + + classArg = skipToArg(offsetArg, 4); + if(0 == strncmp(classArg, "()", 2)) + { + classArg = skipToArg(classArg, 2); + } + if(0 == strncmp(classArg, ".bf or.ef", 9)) + { + classWords = 2; + } + + symbolArg = skipToArg(classArg, 3 + (classWords - 1)); + + /* + ** Skip particular lines/items. + */ + if( + 0 != strncmp(classArg, "Label", 5) && + 0 != strncmp(symbolArg, ".bf", 3) && + 0 != strncmp(symbolArg, ".lf", 3) && + 0 != strncmp(symbolArg, ".ef", 3) + ) + { + char* endOffsetArg = NULL; + unsigned offset = 0; + + /* + ** Convert the offset to something meaninful (size). + */ + errno = 0; + offset = strtoul(offsetArg, &endOffsetArg, 16); + if(0 == errno && endOffsetArg != offsetArg) + { + void* moved = NULL; + + /* + ** Increase the size of the symbol array in the section. + ** Assumed symbols are unique within each section. + */ + moved = realloc(inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols, sizeof(MSDump_Symbol) * (inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbolCount + 1)); + if(NULL != moved) + { + unsigned symIndex = 0; + + /* + ** Record symbol details. + ** Assumed symbols are encountered in order for their section (size calc depends on it). + */ + symIndex = inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbolCount; + inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbolCount++; + inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols = (MSDump_Symbol*)moved; + memset(&inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols[symIndex], 0, sizeof(MSDump_Symbol)); + + inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols[symIndex].mOffset = offset; + + /* + ** We could allocate smarter here if it ever mattered. + */ + inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols[symIndex].mName = strdup(symbolArg); + if(NULL != inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols[symIndex].mName) + { + char* trim = NULL; + + trim = (char*)skipNonWhite(inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols[symIndex].mName); + if(NULL != trim) + { + *trim = '\0'; + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inLine, "Unable to duplicate symbol name."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inLine, "Unable to grow symbol array for section."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inLine, "Unable to convert offset to a number."); + } + } + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inLine, "Unable to determine section index."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inLine, "No match for section prefix."); + } + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inLine, "Unable to scan for section."); + } + } + + return retval; +} + + +void dumpCleanup(MSDump_Container* inContainer) +/* +** Attempt to be nice and free up what we have allocated. +*/ +{ + unsigned objectLoop = 0; + unsigned sectionLoop = 0; + unsigned symbolLoop = 0; + + for(objectLoop = 0; objectLoop < inContainer->mObjectCount; objectLoop++) + { + for(sectionLoop = 0; sectionLoop < inContainer->mObjects[objectLoop].mSectionCount; sectionLoop++) + { + for(symbolLoop = 0; symbolLoop < inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbolCount; symbolLoop++) + { + CLEANUP(inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mName); + } + inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbolCount = 0; + CLEANUP(inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols); + CLEANUP(inContainer->mObjects[objectLoop].mSections[sectionLoop].mType); + } + inContainer->mObjects[objectLoop].mSectionCount = 0; + CLEANUP(inContainer->mObjects[objectLoop].mSections); + } + CLEANUP(inContainer->mObjects); + inContainer->mObjectCount = 0; +} + + +int qsortSymOffset(const void* in1, const void* in2) +/* +** qsort callback to sort the symbols by their offset. +*/ +{ + MSDump_Symbol* sym1 = (MSDump_Symbol*)in1; + MSDump_Symbol* sym2 = (MSDump_Symbol*)in2; + int retval = 0; + + if(sym1->mOffset < sym2->mOffset) + { + retval = 1; + } + else if(sym1->mOffset > sym2->mOffset) + { + retval = -1; + } + + return retval; +} + + +int calcContainer(Options* inOptions, MSDump_Container* inContainer) +/* +** Resposible for doing any size calculations based on the offsets known. +** After this calculation, each sections mUsed will match mSize. +** After this calculation, all symbols should know how big they are. +*/ +{ + int retval = 0; + unsigned objectLoop = 0; + unsigned sectionLoop = 0; + unsigned symbolLoop = 0; + + + /* + ** Need to sort all symbols by their offsets. + */ + for(objectLoop = 0; 0 == retval && objectLoop < inContainer->mObjectCount; objectLoop++) + { + for(sectionLoop = 0; 0 == retval && sectionLoop < inContainer->mObjects[objectLoop].mSectionCount; sectionLoop++) + { + qsort( + inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols, + inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbolCount, + sizeof(MSDump_Symbol), + qsortSymOffset + ); + } + } + + + /* + ** Need to go through all symbols and calculate their size. + */ + for(objectLoop = 0; 0 == retval && objectLoop < inContainer->mObjectCount; objectLoop++) + { + for(sectionLoop = 0; 0 == retval && sectionLoop < inContainer->mObjects[objectLoop].mSectionCount; sectionLoop++) + { + for(symbolLoop = 0; 0 == retval && symbolLoop < inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbolCount; symbolLoop++) + { + inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mSize = + inContainer->mObjects[objectLoop].mSections[sectionLoop].mLength - + inContainer->mObjects[objectLoop].mSections[sectionLoop].mUsed - + inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mOffset; + + inContainer->mObjects[objectLoop].mSections[sectionLoop].mUsed += + inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mSize; + } + } + } + + + return retval; +} + + +int reportContainer(Options* inOptions, MSDump_Container* inContainer) +/* +** Display all symbols and their data. +** We'll use a tsv format. +*/ +{ + int retval = 0; + unsigned objectLoop = 0; + unsigned sectionLoop = 0; + unsigned symbolLoop = 0; + int printRes = 0; + + for(objectLoop = 0; 0 == retval && objectLoop < inContainer->mObjectCount; objectLoop++) + { + for(sectionLoop = 0; 0 == retval && sectionLoop < inContainer->mObjects[objectLoop].mSectionCount; sectionLoop++) + { + for(symbolLoop = 0; 0 == retval && symbolLoop < inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbolCount; symbolLoop++) + { + printRes = fprintf(inOptions->mOutput, "%s\t%s\t%.8X\t%s\n", + inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mName, + inContainer->mObjects[objectLoop].mSections[sectionLoop].mType, + inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mSize, + inContainer->mObjects[objectLoop].mObject + ); + + if(0 > printRes) + { + retval = __LINE__; + ERROR_REPORT(retval, inOptions->mOutputName, "Unable to write to file."); + } + } + } + } + + return retval; +} + + +int dump2symdb(Options* inOptions) +/* +** Convert the input into the output, respecting the options. +** Returns 0 on success. +*/ +{ + int retval = 0; + char lineBuffer[0x800]; + MSDump_Container container; + + memset(&container, 0, sizeof(container)); + + /* + ** Read the file line by line. + */ + while(0 == retval && NULL != fgets(lineBuffer, sizeof(lineBuffer), inOptions->mInput)) + { + if(0 != container.mReadState.mSkipLines) + { + container.mReadState.mSkipLines--; + continue; + } + retval = processLine(inOptions, &container, lineBuffer); + } + + /* + ** Perform whatever calculations desired. + */ + if(0 == retval) + { + retval = calcContainer(inOptions, &container); + } + + /* + ** Output what we know. + */ + if(0 == retval) + { + retval = reportContainer(inOptions, &container); + } + + /* + ** Cleanup what we've done. + */ + dumpCleanup(&container); + + return retval; +} + + +int initOptions(Options* outOptions, int inArgc, char** inArgv) +/* +** returns int 0 if successful. +*/ +{ + int retval = 0; + int loop = 0; + int switchLoop = 0; + int match = 0; + const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); + Switch* current = NULL; + + /* + ** Set any defaults. + */ + memset(outOptions, 0, sizeof(Options)); + outOptions->mProgramName = inArgv[0]; + outOptions->mInput = stdin; + outOptions->mInputName = strdup("stdin"); + outOptions->mOutput = stdout; + outOptions->mOutputName = strdup("stdout"); + + if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName) + { + retval = __LINE__; + ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup."); + } + + /* + ** Go through and attempt to do the right thing. + */ + for(loop = 1; loop < inArgc && 0 == retval; loop++) + { + match = 0; + current = NULL; + + for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++) + { + if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop])) + { + match = __LINE__; + } + else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop])) + { + match = __LINE__; + } + + if(match) + { + if(gSwitches[switchLoop]->mHasValue) + { + /* + ** Attempt to absorb next option to fullfill value. + */ + if(loop + 1 < inArgc) + { + loop++; + + current = gSwitches[switchLoop]; + current->mValue = inArgv[loop]; + } + } + else + { + current = gSwitches[switchLoop]; + } + + break; + } + } + + if(0 == match) + { + outOptions->mHelp = __LINE__; + retval = __LINE__; + ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch."); + } + else if(NULL == current) + { + outOptions->mHelp = __LINE__; + retval = __LINE__; + ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value."); + } + else + { + /* + ** Do something based on address/swtich. + */ + if(current == &gInputSwitch) + { + CLEANUP(outOptions->mInputName); + if(NULL != outOptions->mInput && stdin != outOptions->mInput) + { + fclose(outOptions->mInput); + outOptions->mInput = NULL; + } + + outOptions->mInput = fopen(current->mValue, "r"); + if(NULL == outOptions->mInput) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to open input file."); + } + else + { + outOptions->mInputName = strdup(current->mValue); + if(NULL == outOptions->mInputName) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to strdup."); + } + } + } + else if(current == &gOutputSwitch) + { + CLEANUP(outOptions->mOutputName); + if(NULL != outOptions->mOutput && stdout != outOptions->mOutput) + { + fclose(outOptions->mOutput); + outOptions->mOutput = NULL; + } + + outOptions->mOutput = fopen(current->mValue, "a"); + if(NULL == outOptions->mOutput) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to open output file."); + } + else + { + outOptions->mOutputName = strdup(current->mValue); + if(NULL == outOptions->mOutputName) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to strdup."); + } + } + } + else if(current == &gHelpSwitch) + { + outOptions->mHelp = __LINE__; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mLongName, "No handler for command line switch."); + } + } + } + + return retval; +} + + +void cleanOptions(Options* inOptions) +/* +** Clean up any open handles. +*/ +{ + CLEANUP(inOptions->mInputName); + if(NULL != inOptions->mInput && stdin != inOptions->mInput) + { + fclose(inOptions->mInput); + } + CLEANUP(inOptions->mOutputName); + if(NULL != inOptions->mOutput && stdout != inOptions->mOutput) + { + fclose(inOptions->mOutput); + } + + memset(inOptions, 0, sizeof(Options)); +} + + +void showHelp(Options* inOptions) +/* +** Show some simple help text on usage. +*/ +{ + int loop = 0; + const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); + const char* valueText = NULL; + + printf("usage:\t%s [arguments]\n", inOptions->mProgramName); + printf("\n"); + printf("arguments:\n"); + + for(loop = 0; loop < switchCount; loop++) + { + if(gSwitches[loop]->mHasValue) + { + valueText = " <value>"; + } + else + { + valueText = ""; + } + + printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText); + printf("\t %s%s", gSwitches[loop]->mShortName, valueText); + printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription); + } + + printf("This tool takes the output of \"dumpbin /symbols\" to produce a simple\n"); + printf("tsv db file of symbols and their respective attributes, like size.\n"); +} + + +int main(int inArgc, char** inArgv) +{ + int retval = 0; + Options options; + + retval = initOptions(&options, inArgc, inArgv); + if(options.mHelp) + { + showHelp(&options); + } + else if(0 == retval) + { + retval = dump2symdb(&options); + } + + cleanOptions(&options); + return retval; +} + diff --git a/third_party/codesighs/msmap.h b/third_party/codesighs/msmap.h new file mode 100644 index 0000000..3c528de --- /dev/null +++ b/third_party/codesighs/msmap.h @@ -0,0 +1,149 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is msmap.h code, released + * Oct 3, 2002. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Garrett Arch Blythe, 03-October-2002 + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#if !defined __msmap_H +#define __msmap_H + + +#if defined(__cplusplus) +extern "C" { +#endif +#if 0 +} +#endif + + +/* +** Used to numerically represent addresses. +*/ +typedef unsigned long address; + + +typedef enum __enum_MSMap_SymbolScope +/* +** Symbol scope. +*/ +{ + PUBLIC, + STATIC, + UNDEFINED +} +MSMap_SymbolScope; + + +typedef enum __enum_MSMap_SegmentClass +/* +** Segment class. +*/ +{ + CODE, + DATA +} +MSMap_SegmentClass; + + +typedef struct __struct_MSMap_Segment +/* +** Information about a segment. +*/ +{ + address mPrefix; + address mOffset; + address mLength; + address mUsed; + char* mSegment; + MSMap_SegmentClass mClass; +} +MSMap_Segment; + + +typedef struct __struct_MSMap_Symbol +/* +** Information about a symbol. +*/ +{ + address mPrefix; + address mOffset; + char* mSymbol; + address mRVABase; + char* mObject; + MSMap_SymbolScope mScope; + unsigned mSymDBSize; + MSMap_Segment* mSection; +} +MSMap_Symbol; + + +typedef struct __struct_MSMap_Module +/* +** Top level container of the map data. +*/ +{ + char* mModule; + time_t mTimestamp; + address mPreferredLoadAddress; + MSMap_Segment* mSegments; + unsigned mSegmentCount; + unsigned mSegmentCapacity; + address mEntryPrefix; + address mEntryOffset; + MSMap_Symbol* mSymbols; + unsigned mSymbolCount; + unsigned mSymbolCapacity; +} +MSMap_Module; + + +/* +** How much to grow our arrays by. +*/ +#define MSMAP_SEGMENT_GROWBY 0x10 +#define MSMAP_SYMBOL_GROWBY 0x100 + + +#if 0 +{ +#endif +#if defined(__cplusplus) +} /* extern "C" */ +#endif + + +#endif /* __msmap_H */ diff --git a/third_party/codesighs/msmap2tsv.c b/third_party/codesighs/msmap2tsv.c new file mode 100644 index 0000000..9158702 --- /dev/null +++ b/third_party/codesighs/msmap2tsv.c @@ -0,0 +1,2237 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is msmap2tsv.c code, released + * Oct 3, 2002. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Garrett Arch Blythe, 03-October-2002 + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <ctype.h> + +#include "msmap.h" + +#if defined(_WIN32) +#include <windows.h> +#include <imagehlp.h> + +#define F_DEMANGLE 1 +#define DEMANGLE_STATE_NORMAL 0 +#define DEMANGLE_STATE_QDECODE 1 +#define DEMANGLE_STATE_PROLOGUE_1 2 +#define DEMANGLE_STATE_HAVE_TYPE 3 +#define DEMANGLE_STATE_DEC_LENGTH 4 +#define DEMANGLE_STATE_HEX_LENGTH 5 +#define DEMANGLE_STATE_PROLOGUE_SECONDARY 6 +#define DEMANGLE_STATE_DOLLAR_1 7 +#define DEMANGLE_STATE_DOLLAR_2 8 +#define DEMANGLE_STATE_START 9 +#define DEMANGLE_STATE_STOP 10 +#define DEMANGLE_SAFE_CHAR(eval) (isprint(eval) ? eval : ' ') + +#else +#define F_DEMANGLE 0 +#endif /* WIN32 */ + + +#define ERROR_REPORT(num, val, msg) fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg)); +#define CLEANUP(ptr) do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0) + + +typedef struct __struct_SymDB_Size +/* +** The size of the symbol. +** The size is nested withing a symbols structures to produce a fast +** lookup path. +** The objects are listed in case the client of the symdb needs to +** match the object name in the scenario where multiple symbol +** sizes are present. +** +** mSize The size of the symbol in these objects. +** mObjects A list of objects containing said symbol. +** mObjectCount Number of objects. +*/ +{ + unsigned mSize; + char** mObjects; + unsigned mObjectCount; +} +SymDB_Size; + + +typedef struct __struct_SymDB_Section +/* +** Each section for a symbol has a list of sizes. +** Should there be exactly one size for the symbol, then that +** is the size that should be accepted. +** If there is more than one size, then a match on the object +** should be attempted, held withing each size. +** +** mName The section name. +** mSizes The varoius sizes of the symbol in this section. +** mSizeCount The number of available sizes. +*/ +{ + char* mName; + SymDB_Size* mSizes; + unsigned mSizeCount; +} +SymDB_Section; + + +typedef struct __struct_SymDB_Symbol +/* +** Each symbol has at least one section. +** The section indicates what type of symbol a client may be looking for. +** If there is no match on the section, then the client should not trust +** the symbdb. +** +** mName The mangled name of the symbol. +** mSections Various sections this symbol belongs to. +** mSectionCount The number of sections. +*/ +{ + char* mName; + SymDB_Section* mSections; + unsigned mSectionCount; +} +SymDB_Symbol; + + +#define SYMDB_SYMBOL_GROWBY 0x1000 /* how many sybols to allocate at a time */ + + +typedef struct __struct_SymDB_Container +/* +** The symbol DB container object. +** The goal of the symbol DB is to have exactly one SymDB_Symbol for each +** mangled name, no matter how ever many identical mangled names there +** are in the input. +** The input is already expected to be well sorted, futher this leads to +** the ability to binary search for symbol name matches. +** +** mSymbols The symbols. +** mSymbolCount The number of symbols in the DB. +** mSymbolCapacity The number of symbols we can hold (before realloc). +*/ +{ + SymDB_Symbol* mSymbols; + unsigned mSymbolCount; + unsigned mSymbolCapacity; +} +SymDB_Container; + + +typedef struct __struct_Options +/* +** Options to control how we perform. +** +** mProgramName Used in help text. +** mInput File to read for input. +** Default is stdin. +** mInputName Name of the file. +** mOutput Output file, append. +** Default is stdout. +** mOutputName Name of the file. +** mHelp Whether or not help should be shown. +** mMatchModules Array of strings which the module name should match. +** mMatchModuleCount Number of items in array. +** mSymDBName Symbol DB filename. +** mBatchMode Batch mode. +** When in batch mode, the input file contains a list of +** map files to process. +** Normally the input file is a single map file itself. +*/ +{ + const char* mProgramName; + FILE* mInput; + char* mInputName; + FILE* mOutput; + char* mOutputName; + int mHelp; + char** mMatchModules; + unsigned mMatchModuleCount; + char* mSymDBName; + SymDB_Container* mSymDB; + int mBatchMode; +} +Options; + + +typedef struct __struct_Switch +/* +** Command line options. +*/ +{ + const char* mLongName; + const char* mShortName; + int mHasValue; + const char* mValue; + const char* mDescription; +} +Switch; + +#define DESC_NEWLINE "\n\t\t" + +static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."}; +static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."}; +static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."}; +static Switch gMatchModuleSwitch = {"--match-module", "-mm", 1, NULL, "Specify a valid module name." DESC_NEWLINE "Multiple specifications allowed." DESC_NEWLINE "If a module name does not match one of the names specified then no output will occur."}; +static Switch gSymDBSwitch = {"--symdb", "-sdb", 1, NULL, "Specify a symbol tsv db input file." DESC_NEWLINE "Such a symdb is produced using the tool msdump2symdb." DESC_NEWLINE "This allows better symbol size approximations." DESC_NEWLINE "The symdb file must be pre-sorted."}; +static Switch gBatchModeSwitch = {"--batch", "-b", 0, NULL, "Runs in batch mode." DESC_NEWLINE "The input file contains a list of map files." DESC_NEWLINE "Normally the input file is a map file itself." DESC_NEWLINE "This eliminates reprocessing the symdb for multiple map files."}; + +static Switch* gSwitches[] = { + &gInputSwitch, + &gOutputSwitch, + &gMatchModuleSwitch, + &gSymDBSwitch, + &gBatchModeSwitch, + &gHelpSwitch +}; + + +typedef struct __struct_MSMap_ReadState +/* +** Keep track of what state we are while reading input. +** This gives the input context in which we absorb the datum. +*/ +{ + int mHasModule; + + int mHasTimestamp; + + int mHasPreferredLoadAddress; + + int mHasSegmentData; + int mSegmentDataSkippedLine; + + int mHasPublicSymbolData; + int mHasPublicSymbolDataSkippedLines; + + int mHasEntryPoint; + + int mFoundStaticSymbols; +} +MSMap_ReadState; + + +char* skipWhite(char* inScan) +/* +** Skip whitespace. +*/ +{ + char* retval = inScan; + + while(isspace(*retval)) + { + retval++; + } + + return retval; +} + +void trimWhite(char* inString) +/* +** Remove any whitespace from the end of the string. +*/ +{ + int len = strlen(inString); + + while(len) + { + len--; + + if(isspace(*(inString + len))) + { + *(inString + len) = '\0'; + } + else + { + break; + } + } +} + + +char* lastWord(char* inString) +/* +** Finds and returns the last word in a string. +** It is assumed no whitespace is at the end of the string. +*/ +{ + int mod = 0; + int len = strlen(inString); + + while(len) + { + len--; + if(isspace(*(inString + len))) + { + mod = 1; + break; + } + } + + return inString + len + mod; +} + + +MSMap_Segment* getSymbolSection(MSMap_Module* inModule, MSMap_Symbol* inoutSymbol) +/* +** Perform a lookup for the section of the symbol. +** The function could cache the value. +*/ +{ + MSMap_Segment* retval = NULL; + + if(NULL != inoutSymbol->mSection) + { + /* + ** Use cached value. + */ + retval = inoutSymbol->mSection; + } + else + { + unsigned secLoop = 0; + + /* + ** Go through sections in module to find the match for the symbol. + */ + for(secLoop = 0; secLoop < inModule->mSegmentCount; secLoop++) + { + if(inoutSymbol->mPrefix == inModule->mSegments[secLoop].mPrefix) + { + if(inoutSymbol->mOffset >= inModule->mSegments[secLoop].mOffset) + { + if(inoutSymbol->mOffset < (inModule->mSegments[secLoop].mOffset + inModule->mSegments[secLoop].mLength)) + { + /* + ** We have the section. + */ + retval = &inModule->mSegments[secLoop]; + break; + } + } + } + } + + /* + ** Cache the value for next time. + */ + inoutSymbol->mSection = retval; + } + + return retval; +} + + +int readSymDB(const char* inDBName, SymDB_Container** outDB) +/* +** Intialize the symbol DB. +** Only call if the symbol DB should be initialized. +*/ +{ + int retval = 0; + + /* + ** Initialize out arguments. + */ + if(NULL != outDB) + { + *outDB = NULL; + } + + if(NULL != outDB && NULL != inDBName) + { + FILE* symDB = NULL; + + symDB = fopen(inDBName, "r"); + if(NULL != symDB) + { + *outDB = (SymDB_Container*)calloc(1, sizeof(SymDB_Container)); + if(NULL != *outDB) + { + char lineBuf[0x400]; + char* symbol = NULL; + char* section = NULL; + char* object = NULL; + char* length = NULL; + unsigned lengthNum = 0; + char* endLength = NULL; + + /* + ** Read the file line by line. + */ + while(0 == retval && NULL != fgets(lineBuf, sizeof(lineBuf), symDB)) + { + trimWhite(lineBuf); + + /* + ** Each line has four arguments. tab separated values (tsv). + ** Symbol + ** Section + ** Length + ** Object + */ + + symbol = skipWhite(lineBuf); + if(NULL == symbol) + { + retval = __LINE__; + ERROR_REPORT(retval, inDBName, "File does not appear to be a symbol DB."); + break; + } + + section = strchr(symbol, '\t'); + if(NULL == section) + { + retval = __LINE__; + ERROR_REPORT(retval, inDBName, "File does not appear to be a symbol DB."); + break; + } + *section = '\0'; + section++; + + length = strchr(section, '\t'); + if(NULL == length) + { + retval = __LINE__; + ERROR_REPORT(retval, inDBName, "File does not appear to be a symbol DB."); + break; + } + *length = '\0'; + length++; + + object = strchr(length, '\t'); + if(NULL == object) + { + retval = __LINE__; + ERROR_REPORT(retval, inDBName, "File does not appear to be a symbol DB."); + break; + } + *object = '\0'; + object++; + + /* + ** Convert the length into a number. + */ + errno = 0; + lengthNum = strtoul(length, &endLength, 16); + if(0 == errno && endLength != length) + { + SymDB_Symbol* dbSymbol = NULL; + SymDB_Section* dbSection = NULL; + SymDB_Size* dbSize = NULL; + char* dbObject = NULL; + void* moved = NULL; + + /* + ** Are we looking at the same symbol as last line? + ** This assumes the symdb is pre sorted!!! + */ + if(0 != (*outDB)->mSymbolCount) + { + unsigned index = (*outDB)->mSymbolCount - 1; + + if(0 == strcmp((*outDB)->mSymbols[index].mName, symbol)) + { + dbSymbol = &(*outDB)->mSymbols[index]; + } + } + + /* + ** May need to create symbol. + */ + if(NULL == dbSymbol) + { + /* + ** Could be time to grow the symbol pool. + */ + if((*outDB)->mSymbolCount >= (*outDB)->mSymbolCapacity) + { + moved = realloc((*outDB)->mSymbols, sizeof(SymDB_Symbol) * ((*outDB)->mSymbolCapacity + SYMDB_SYMBOL_GROWBY)); + if(NULL != moved) + { + (*outDB)->mSymbols = (SymDB_Symbol*)moved; + memset(&(*outDB)->mSymbols[(*outDB)->mSymbolCapacity], 0, sizeof(SymDB_Symbol) * SYMDB_SYMBOL_GROWBY); + (*outDB)->mSymbolCapacity += SYMDB_SYMBOL_GROWBY; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inDBName, "Unable to grow symbol DB symbol array."); + break; + } + } + + if((*outDB)->mSymbolCount < (*outDB)->mSymbolCapacity) + { + dbSymbol = &(*outDB)->mSymbols[(*outDB)->mSymbolCount]; + (*outDB)->mSymbolCount++; + + dbSymbol->mName = strdup(symbol); + if(NULL == dbSymbol->mName) + { + retval = __LINE__; + ERROR_REPORT(retval, symbol, "Unable to duplicate string."); + break; + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, symbol, "Unable to grow symbol DB for symbol."); + break; + } + } + + /* + ** Assume we have the symbol. + ** + ** Is this the same section as the last section in the symbol? + ** This assumes the symdb was presorted!!!! + */ + if(0 != dbSymbol->mSectionCount) + { + unsigned index = dbSymbol->mSectionCount - 1; + + if(0 == strcmp(dbSymbol->mSections[index].mName, section)) + { + dbSection = &dbSymbol->mSections[index]; + } + } + + /* + ** May need to create the section. + */ + if(NULL == dbSection) + { + moved = realloc(dbSymbol->mSections, sizeof(SymDB_Section) * (dbSymbol->mSectionCount + 1)); + if(NULL != moved) + { + dbSymbol->mSections = (SymDB_Section*)moved; + dbSection = &dbSymbol->mSections[dbSymbol->mSectionCount]; + dbSymbol->mSectionCount++; + + memset(dbSection, 0, sizeof(SymDB_Section)); + + dbSection->mName = strdup(section); + if(NULL == dbSection->mName) + { + retval = __LINE__; + ERROR_REPORT(retval, section, "Unable to duplicate string."); + break; + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, section, "Unable to grow symbol sections for symbol DB."); + break; + } + } + + /* + ** Assume we have the section. + ** + ** Is this the same size as the last size? + ** This assumes the symdb was presorted!!! + */ + if(0 != dbSection->mSizeCount) + { + unsigned index = dbSection->mSizeCount - 1; + + if(dbSection->mSizes[index].mSize == lengthNum) + { + dbSize = &dbSection->mSizes[index]; + } + } + + /* + ** May need to create the size in question. + */ + if(NULL == dbSize) + { + moved = realloc(dbSection->mSizes, sizeof(SymDB_Size) * (dbSection->mSizeCount + 1)); + if(NULL != moved) + { + dbSection->mSizes = (SymDB_Size*)moved; + dbSize = &dbSection->mSizes[dbSection->mSizeCount]; + dbSection->mSizeCount++; + + memset(dbSize, 0, sizeof(SymDB_Size)); + + dbSize->mSize = lengthNum; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, length, "Unable to grow symbol section sizes for symbol DB."); + break; + } + } + + /* + ** Assume we have the size. + ** + ** We assume a one to one correllation between size and object. + ** Always try to add the new object name. + ** As the symdb is assumed to be sorted, the object names should also be in order. + */ + moved = realloc(dbSize->mObjects, sizeof(char*) * (dbSize->mObjectCount + 1)); + if(NULL != moved) + { + dbObject = strdup(object); + + dbSize->mObjects = (char**)moved; + dbSize->mObjects[dbSize->mObjectCount] = dbObject; + dbSize->mObjectCount++; + + if(NULL == dbObject) + { + retval = __LINE__; + ERROR_REPORT(retval, object, "Unable to duplicate string."); + break; + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, object, "Unable to grow symbol section size objects for symbol DB."); + break; + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, length, "Unable to convert symbol DB length into a number."); + break; + } + } + + if(0 == retval && 0 != ferror(symDB)) + { + retval = __LINE__; + ERROR_REPORT(retval, inDBName, "Unable to read file."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inDBName, "Unable to allocate symbol DB."); + } + + fclose(symDB); + symDB = NULL; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inDBName, "Unable to open symbol DB."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, "(NULL)", "Invalid arguments."); + } + + return retval; +} + + +void cleanSymDB(SymDB_Container** inDB) +/* +** Free it all up. +*/ +{ + if(NULL != inDB && NULL != *inDB) + { + unsigned symLoop = 0; + unsigned secLoop = 0; + unsigned sizLoop = 0; + unsigned objLoop = 0; + + for(symLoop = 0; symLoop < (*inDB)->mSymbolCount; symLoop++) + { + for(secLoop = 0; secLoop < (*inDB)->mSymbols[symLoop].mSectionCount; secLoop++) + { + for(sizLoop = 0; sizLoop < (*inDB)->mSymbols[symLoop].mSections[secLoop].mSizeCount; sizLoop++) + { + for(objLoop = 0; objLoop < (*inDB)->mSymbols[symLoop].mSections[secLoop].mSizes[sizLoop].mObjectCount; objLoop++) + { + CLEANUP((*inDB)->mSymbols[symLoop].mSections[secLoop].mSizes[sizLoop].mObjects[objLoop]); + } + CLEANUP((*inDB)->mSymbols[symLoop].mSections[secLoop].mSizes[sizLoop].mObjects); + } + CLEANUP((*inDB)->mSymbols[symLoop].mSections[secLoop].mName); + CLEANUP((*inDB)->mSymbols[symLoop].mSections[secLoop].mSizes); + } + CLEANUP((*inDB)->mSymbols[symLoop].mName); + CLEANUP((*inDB)->mSymbols[symLoop].mSections); + } + CLEANUP((*inDB)->mSymbols); + CLEANUP(*inDB); + } +} + + +int symDBLookup(const void* inKey, const void* inItem) +/* +** bsearch utility routine to find the symbol in the symdb. +*/ +{ + int retval = 0; + const char* key = (const char*)inKey; + const SymDB_Symbol* symbol = (const SymDB_Symbol*)inItem; + + retval = strcmp(key, symbol->mName); + + return retval; +} + + +int fillSymbolSizeFromDB(Options* inOptions, MSMap_Module* inModule, MSMap_Symbol* inoutSymbol, const char* inMangledName) +/* +** If we have a symbol DB, attempt to determine the real size of the symbol +** up front. +** This helps us later in the game to avoid performing size guesses by +** offset. +*/ +{ + int retval = 0; + + /* + ** May need to initialize symdb. + */ + if(NULL == inOptions->mSymDB && NULL != inOptions->mSymDBName) + { + retval = readSymDB(inOptions->mSymDBName, &inOptions->mSymDB); + } + + /* + ** Optional + */ + if(0 == retval && NULL != inOptions->mSymDB) + { + void* match = NULL; + + /* + ** Find the symbol. + */ + match = bsearch(inMangledName, inOptions->mSymDB->mSymbols, inOptions->mSymDB->mSymbolCount, sizeof(SymDB_Symbol), symDBLookup); + if(NULL != match) + { + SymDB_Symbol* symbol = (SymDB_Symbol*)match; + unsigned symDBSize = 0; + MSMap_Segment* mapSection = NULL; + + /* + ** We found the symbol. + ** + ** See if it has the section in question. + */ + mapSection = getSymbolSection(inModule, inoutSymbol); + if(NULL != mapSection) + { + unsigned secLoop = 0; + + for(secLoop = 0; secLoop < symbol->mSectionCount; secLoop++) + { + if(0 == strcmp(mapSection->mSegment, symbol->mSections[secLoop].mName)) + { + SymDB_Section* section = &symbol->mSections[secLoop]; + + /* + ** We have a section match. + ** Should there be a single size for the symbol, + ** then we just default to that. + ** If more than one size, we have to do an + ** object match search. + ** Should there be no object match, we do nothign. + */ + if(1 == section->mSizeCount) + { + symDBSize = section->mSizes[0].mSize; + } + else + { + char* mapObject = NULL; + + /* + ** Figure out the map object file name. + ** Skip any colon. + ** If it doesn't have a .obj in it, not worth continuing. + */ + mapObject = strrchr(inoutSymbol->mObject, ':'); + if(NULL == mapObject) + { + mapObject = inoutSymbol->mObject; + } + else + { + mapObject++; /* colon */ + } + + if(NULL != strstr(mapObject, ".obj")) + { + unsigned sizLoop = 0; + unsigned objLoop = 0; + SymDB_Size* size = NULL; + + for(sizLoop = 0; sizLoop < section->mSizeCount; sizLoop++) + { + size = §ion->mSizes[sizLoop]; + + for(objLoop = 0; objLoop < size->mObjectCount; objLoop++) + { + if(NULL != strstr(size->mObjects[objLoop], mapObject)) + { + /* + ** As we matched the object, in a particular section, + ** we'll go with this as the number. + */ + symDBSize = size->mSize; + break; + } + } + + /* + ** If the object loop broke early, we break too. + */ + if(objLoop < size->mObjectCount) + { + break; + } + } + } + } + + break; + } + } + } + + /* + ** Put the size in. + */ + inoutSymbol->mSymDBSize = symDBSize; + } + } + + return retval; +} + + +char* symdup(const char* inSymbol) +/* +** Attempts to demangle the symbol if appropriate. +** Otherwise acts like strdup. +*/ +{ + char* retval = NULL; + +#if F_DEMANGLE + { + int isImport = 0; + + if(0 == strncmp("__imp_", inSymbol, 6)) + { + isImport = __LINE__; + inSymbol += 6; + } + + if('?' == inSymbol[0]) + { + char demangleBuf[0x200]; + DWORD demangleRes = 0; + + demangleRes = UnDecorateSymbolName(inSymbol, demangleBuf, sizeof(demangleBuf), UNDNAME_COMPLETE); + if(0 != demangleRes) + { + if (strcmp(demangleBuf, "`string'") == 0) + { + + /* attempt manual demangling of string prefix.. */ + + /* first make sure we have enough space for the + updated string - the demangled string will + always be shorter than strlen(inSymbol) and the + prologue will always be longer than the + "string: " that we tack on the front of the string + */ + char *curresult = retval = malloc(strlen(inSymbol) + 11); + const char *curchar = inSymbol; + + int state = DEMANGLE_STATE_START; + + /* the hex state is for stuff like ?$EA which + really means hex value 0x40 */ + char hex_state = 0; + char string_is_unicode = 0; + + /* sometimes we get a null-termination before the + final @ sign - in that case, remember that + we've seen the whole string */ + int have_null_char = 0; + + /* stick our user-readable prefix on */ + strcpy(curresult, "string: \""); + curresult += 9; + + while (*curchar) { + + // process current state + switch (state) { + + /* the Prologue states are divided up so + that someday we can try to decode + the random letters in between the '@' + signs. Also, some strings only have 2 + prologue '@' signs, so we have to + figure out how to distinguish between + them at some point. */ + case DEMANGLE_STATE_START: + if (*curchar == '@') + state = DEMANGLE_STATE_PROLOGUE_1; + /* ignore all other states */ + break; + + case DEMANGLE_STATE_PROLOGUE_1: + switch (*curchar) { + case '0': + string_is_unicode=0; + state = DEMANGLE_STATE_HAVE_TYPE; + break; + case '1': + string_is_unicode=1; + state = DEMANGLE_STATE_HAVE_TYPE; + break; + + /* ignore all other characters */ + } + break; + + case DEMANGLE_STATE_HAVE_TYPE: + if (*curchar >= '0' && *curchar <= '9') { + state = DEMANGLE_STATE_DEC_LENGTH; + } else if (*curchar >= 'A' && *curchar <= 'Z') { + state = DEMANGLE_STATE_HEX_LENGTH; + } + case DEMANGLE_STATE_DEC_LENGTH: + /* decimal lengths don't have the 2nd + field + */ + if (*curchar == '@') + state = DEMANGLE_STATE_NORMAL; + break; + + case DEMANGLE_STATE_HEX_LENGTH: + /* hex lengths have a 2nd field + (though I have no idea what it is for) + */ + if (*curchar == '@') + state = DEMANGLE_STATE_PROLOGUE_SECONDARY; + break; + + case DEMANGLE_STATE_PROLOGUE_SECONDARY: + if (*curchar == '@') + state = DEMANGLE_STATE_NORMAL; + break; + + case DEMANGLE_STATE_NORMAL: + switch (*curchar) { + case '?': + state = DEMANGLE_STATE_QDECODE; + break; + case '@': + state = DEMANGLE_STATE_STOP; + break; + default: + *curresult++ = DEMANGLE_SAFE_CHAR(*curchar); + state = DEMANGLE_STATE_NORMAL; + break; + } + break; + + /* found a '?' */ + case DEMANGLE_STATE_QDECODE: + state = DEMANGLE_STATE_NORMAL; + + /* there are certain shortcuts, like + "?3" means ":" + */ + switch (*curchar) { + case '1': + *curresult++ = '/'; + break; + case '2': + *curresult++ = '\\'; + break; + case '3': + *curresult++ = ':'; + break; + case '4': + *curresult++ = '.'; + break; + case '5': + *curresult++ = ' '; + break; + case '6': + *curresult++ = '\\'; + *curresult++ = 'n'; + break; + case '8': + *curresult++ = '\''; + break; + case '9': + *curresult++ = '-'; + break; + + /* any other arbitrary ASCII value can + be stored by prefixing it with ?$ + */ + case '$': + state = DEMANGLE_STATE_DOLLAR_1; + } + break; + + case DEMANGLE_STATE_DOLLAR_1: + /* first digit of ?$ notation. All digits + are hex, represented starting with the + capital leter 'A' such that 'A' means 0x0, + 'B' means 0x1, 'K' means 0xA + */ + hex_state = (*curchar - 'A') * 0x10; + state = DEMANGLE_STATE_DOLLAR_2; + break; + + case DEMANGLE_STATE_DOLLAR_2: + /* same mechanism as above */ + hex_state += (*curchar - 'A'); + if (hex_state) { + *curresult++ = DEMANGLE_SAFE_CHAR(hex_state); + have_null_char = 0; + } + else { + have_null_char = 1; + } + + state = DEMANGLE_STATE_NORMAL; + break; + + case DEMANGLE_STATE_STOP: + break; + } + + curchar++; + } + + /* add the appropriate termination depending + if we completed the string or not */ + if (!have_null_char) + strcpy(curresult, "...\""); + else + strcpy(curresult, "\""); + } else { + retval = strdup(demangleBuf); + } + } + else + { + /* + ** fall back to normal. + */ + retval = strdup(inSymbol); + } + } + else if('_' == inSymbol[0]) + { + retval = strdup(inSymbol + 1); + } + else + { + retval = strdup(inSymbol); + } + + /* + ** May need to rewrite the symbol if an import. + */ + if(NULL != retval && isImport) + { + const char importPrefix[] = "__declspec(dllimport) "; + char importBuf[0x200]; + int printRes = 0; + + printRes = _snprintf(importBuf, sizeof(importBuf), "%s%s", importPrefix, retval); + free(retval); + retval = NULL; + + if(printRes > 0) + { + retval = strdup(importBuf); + } + } + } +#else /* F_DEMANGLE */ + retval = strdup(inSymbol); +#endif /* F_DEMANGLE */ + + return retval; +} + + +int readmap(Options* inOptions, MSMap_Module* inModule) +/* +** Read the input line by line, adding it to the module. +*/ +{ + int retval = 0; + char lineBuffer[0x400]; + char* current = NULL; + MSMap_ReadState fsm; + int len = 0; + int forceContinue = 0; + + memset(&fsm, 0, sizeof(fsm)); + + /* + ** Read the map file line by line. + ** We keep a simple state machine to determine what we're looking at. + */ + while(0 == retval && NULL != fgets(lineBuffer, sizeof(lineBuffer), inOptions->mInput)) + { + if(forceContinue) + { + /* + ** Used to skip anticipated blank lines. + */ + forceContinue--; + continue; + } + + current = skipWhite(lineBuffer); + trimWhite(current); + + len = strlen(current); + + if(fsm.mHasModule) + { + if(fsm.mHasTimestamp) + { + if(fsm.mHasPreferredLoadAddress) + { + if(fsm.mHasSegmentData) + { + if(fsm.mHasPublicSymbolData) + { + if(fsm.mHasEntryPoint) + { + if(fsm.mFoundStaticSymbols) + { + /* + ** A blank line means we've reached the end of all static symbols. + */ + if(len) + { + /* + ** We're adding a new symbol. + ** Make sure we have room for it. + */ + if(inModule->mSymbolCapacity == inModule->mSymbolCount) + { + void* moved = NULL; + + moved = realloc(inModule->mSymbols, sizeof(MSMap_Symbol) * (inModule->mSymbolCapacity + MSMAP_SYMBOL_GROWBY)); + if(NULL != moved) + { + inModule->mSymbolCapacity += MSMAP_SYMBOL_GROWBY; + inModule->mSymbols = (MSMap_Symbol*)moved; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inModule->mModule, "Unable to grow symbols."); + } + } + + if(0 == retval && inModule->mSymbolCapacity > inModule->mSymbolCount) + { + MSMap_Symbol* theSymbol = NULL; + unsigned index = 0; + int scanRes = 0; + char symbolBuf[0x200]; + + index = inModule->mSymbolCount; + inModule->mSymbolCount++; + theSymbol = (inModule->mSymbols + index); + + memset(theSymbol, 0, sizeof(MSMap_Symbol)); + theSymbol->mScope = STATIC; + + scanRes = sscanf(current, "%x:%x %s %x", (unsigned*)&(theSymbol->mPrefix), (unsigned*)&(theSymbol->mOffset), symbolBuf, (unsigned*)&(theSymbol->mRVABase)); + if(4 == scanRes) + { + theSymbol->mSymbol = symdup(symbolBuf); + + if(0 == retval) + { + if(NULL != theSymbol->mSymbol) + { + char *last = lastWord(current); + + theSymbol->mObject = strdup(last); + if(NULL == theSymbol->mObject) + { + retval = __LINE__; + ERROR_REPORT(retval, last, "Unable to copy object name."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, symbolBuf, "Unable to copy symbol name."); + } + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inModule->mModule, "Unable to scan static symbols."); + } + } + } + else + { + /* + ** All done. + */ + break; + } + } + else + { + /* + ** Static symbols are optional. + ** If no static symbols we're done. + ** Otherwise, set the flag such that it will work more. + */ + if(0 == strcmp(current, "Static symbols")) + { + fsm.mFoundStaticSymbols = __LINE__; + forceContinue = 1; + } + else + { + /* + ** All done. + */ + break; + } + } + } + else + { + int scanRes = 0; + + scanRes = sscanf(current, "entry point at %x:%x", (unsigned*)&(inModule->mEntryPrefix), (unsigned*)&(inModule->mEntryOffset)); + if(2 == scanRes) + { + fsm.mHasEntryPoint = __LINE__; + forceContinue = 1; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current, "Unable to obtain entry point."); + } + } + } + else + { + /* + ** Skip the N lines of public symbol data (column headers). + */ + if(2 <= fsm.mHasPublicSymbolDataSkippedLines) + { + /* + ** A blank line indicates end of public symbols. + */ + if(len) + { + /* + ** We're adding a new symbol. + ** Make sure we have room for it. + */ + if(inModule->mSymbolCapacity == inModule->mSymbolCount) + { + void* moved = NULL; + + moved = realloc(inModule->mSymbols, sizeof(MSMap_Symbol) * (inModule->mSymbolCapacity + MSMAP_SYMBOL_GROWBY)); + if(NULL != moved) + { + inModule->mSymbolCapacity += MSMAP_SYMBOL_GROWBY; + inModule->mSymbols = (MSMap_Symbol*)moved; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inModule->mModule, "Unable to grow symbols."); + } + } + + if(0 == retval && inModule->mSymbolCapacity > inModule->mSymbolCount) + { + MSMap_Symbol* theSymbol = NULL; + unsigned index = 0; + int scanRes = 0; + char symbolBuf[0x200]; + + index = inModule->mSymbolCount; + inModule->mSymbolCount++; + theSymbol = (inModule->mSymbols + index); + + memset(theSymbol, 0, sizeof(MSMap_Symbol)); + theSymbol->mScope = PUBLIC; + + scanRes = sscanf(current, "%x:%x %s %x", (unsigned*)&(theSymbol->mPrefix), (unsigned*)&(theSymbol->mOffset), symbolBuf, (unsigned *)&(theSymbol->mRVABase)); + if(4 == scanRes) + { + theSymbol->mSymbol = symdup(symbolBuf); + + if(NULL != theSymbol->mSymbol) + { + char *last = lastWord(current); + + theSymbol->mObject = strdup(last); + if(NULL != theSymbol->mObject) + { + /* + ** Finally, attempt to lookup the actual size of the symbol + ** if there is a symbol DB available. + */ + retval = fillSymbolSizeFromDB(inOptions, inModule, theSymbol, symbolBuf); + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, last, "Unable to copy object name."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, symbolBuf, "Unable to copy symbol name."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inModule->mModule, "Unable to scan public symbols."); + } + } + } + else + { + fsm.mHasPublicSymbolData = __LINE__; + } + } + else + { + fsm.mHasPublicSymbolDataSkippedLines++; + } + } + } + else + { + /* + ** Skip the first line of segment data (column headers). + ** Mark that we've begun grabbing segement data. + */ + if(fsm.mSegmentDataSkippedLine) + { + /* + ** A blank line means end of the segment data. + */ + if(len) + { + /* + ** We're adding a new segment. + ** Make sure we have room for it. + */ + if(inModule->mSegmentCapacity == inModule->mSegmentCount) + { + void* moved = NULL; + + moved = realloc(inModule->mSegments, sizeof(MSMap_Segment) * (inModule->mSegmentCapacity + MSMAP_SEGMENT_GROWBY)); + if(NULL != moved) + { + inModule->mSegmentCapacity += MSMAP_SEGMENT_GROWBY; + inModule->mSegments = (MSMap_Segment*)moved; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inModule->mModule, "Unable to grow segments."); + } + } + + if(0 == retval && inModule->mSegmentCapacity > inModule->mSegmentCount) + { + MSMap_Segment* theSegment = NULL; + unsigned index = 0; + char classBuf[0x10]; + char nameBuf[0x20]; + int scanRes = 0; + + index = inModule->mSegmentCount; + inModule->mSegmentCount++; + theSegment = (inModule->mSegments + index); + + memset(theSegment, 0, sizeof(MSMap_Segment)); + + scanRes = sscanf(current, "%x:%x %xH %s %s", (unsigned*)&(theSegment->mPrefix), (unsigned*)&(theSegment->mOffset), (unsigned*)&(theSegment->mLength), nameBuf, classBuf); + if(5 == scanRes) + { + if('.' == nameBuf[0]) + { + theSegment->mSegment = strdup(&nameBuf[1]); + } + else + { + theSegment->mSegment = strdup(nameBuf); + } + + if(NULL != theSegment->mSegment) + { + if(0 == strcmp("DATA", classBuf)) + { + theSegment->mClass = DATA; + } + else if(0 == strcmp("CODE", classBuf)) + { + theSegment->mClass = CODE; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, classBuf, "Unrecognized segment class."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, nameBuf, "Unable to copy segment name."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, inModule->mModule, "Unable to scan segments."); + } + } + } + else + { + fsm.mHasSegmentData = __LINE__; + } + } + else + { + fsm.mSegmentDataSkippedLine = __LINE__; + } + } + } + else + { + int scanRes = 0; + + /* + ** The PLA has a particular format. + */ + scanRes = sscanf(current, "Preferred load address is %x", (unsigned*)&(inModule->mPreferredLoadAddress)); + if(1 == scanRes) + { + fsm.mHasPreferredLoadAddress = __LINE__; + forceContinue = 1; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current, "Unable to obtain preferred load address."); + } + } + } + else + { + int scanRes = 0; + + /* + ** The timestamp has a particular format. + */ + scanRes = sscanf(current, "Timestamp is %x", (unsigned*)&(inModule->mTimestamp)); + if(1 == scanRes) + { + fsm.mHasTimestamp = __LINE__; + forceContinue = 1; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current, "Unable to obtain timestamp."); + } + } + } + else + { + /* + ** The module is on a line by itself. + */ + inModule->mModule = strdup(current); + if(NULL != inModule->mModule) + { + fsm.mHasModule = __LINE__; + forceContinue = 1; + + if(0 != inOptions->mMatchModuleCount) + { + unsigned matchLoop = 0; + + /* + ** If this module name doesn't match, then bail. + ** Compare in a case sensitive manner, exact match only. + */ + for(matchLoop = 0; matchLoop < inOptions->mMatchModuleCount; matchLoop++) + { + if(0 == strcmp(inModule->mModule, inOptions->mMatchModules[matchLoop])) + { + break; + } + } + + if(matchLoop == inOptions->mMatchModuleCount) + { + /* + ** A match did not occur, bail out of read loop. + ** No error, however. + */ + break; + } + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current, "Unable to obtain module."); + } + } + } + + if(0 == retval && 0 != ferror(inOptions->mInput)) + { + retval = __LINE__; + ERROR_REPORT(retval, inOptions->mInputName, "Unable to read file."); + } + + return retval; +} + + +static int qsortRVABase(const void* in1, const void* in2) +/* +** qsort callback to sort the symbols by their RVABase. +*/ +{ + MSMap_Symbol* sym1 = (MSMap_Symbol*)in1; + MSMap_Symbol* sym2 = (MSMap_Symbol*)in2; + int retval = 0; + + if(sym1->mRVABase < sym2->mRVABase) + { + retval = -1; + } + else if(sym1->mRVABase > sym2->mRVABase) + { + retval = 1; + } + + return retval; +} + + +static int tsvout(Options* inOptions, unsigned inSize, MSMap_SegmentClass inClass, MSMap_SymbolScope inScope, const char* inModule, const char* inSegment, const char* inObject, const char* inSymbol) +/* +** Output a line of map information separated by tabs. +** Some items (const char*), if not present, will receive a default value. +*/ +{ + int retval = 0; + + /* + ** No need to output on no size. + ** This can happen with zero sized segments, + ** or an imported symbol which has multiple names (one will count). + */ + if(0 != inSize) + { + char objectBuf[0x100]; + const char* symScope = NULL; + const char* segClass = NULL; + const char* undefined = "UNDEF"; + + /* + ** Fill in unspecified values. + */ + if(NULL == inObject) + { + sprintf(objectBuf, "%s:%s:%s", undefined, inModule, inSegment); + inObject = objectBuf; + } + if(NULL == inSymbol) + { + inSymbol = inObject; + } + + /* + ** Convert some enumerations to text. + */ + switch(inClass) + { + case CODE: + segClass = "CODE"; + break; + case DATA: + segClass = "DATA"; + break; + default: + retval = __LINE__; + ERROR_REPORT(retval, "", "Unable to determine class for output."); + break; + } + + switch(inScope) + { + case PUBLIC: + symScope = "PUBLIC"; + break; + case STATIC: + symScope = "STATIC"; + break; + case UNDEFINED: + symScope = undefined; + break; + default: + retval = __LINE__; + ERROR_REPORT(retval, "", "Unable to determine scope for symbol."); + break; + } + + if(0 == retval) + { + int printRes = 0; + + printRes = fprintf(inOptions->mOutput, + "%.8X\t%s\t%s\t%s\t%s\t%s\t%s\n", + inSize, + segClass, + symScope, + inModule, + inSegment, + inObject, + inSymbol + ); + + if(0 > printRes) + { + retval = __LINE__; + ERROR_REPORT(retval, inOptions->mOutputName, "Unable to output tsv data."); + } + } + } + + return retval; +} + + +void cleanModule(MSMap_Module* inModule) +{ + unsigned loop = 0; + + for(loop = 0; loop < inModule->mSymbolCount; loop++) + { + CLEANUP(inModule->mSymbols[loop].mObject); + CLEANUP(inModule->mSymbols[loop].mSymbol); + } + CLEANUP(inModule->mSymbols); + + for(loop = 0; loop < inModule->mSegmentCount; loop++) + { + CLEANUP(inModule->mSegments[loop].mSegment); + } + CLEANUP(inModule->mSegments); + + CLEANUP(inModule->mModule); + + memset(inModule, 0, sizeof(MSMap_Module)); +} + + +int map2tsv(Options* inOptions) +/* +** Read all input. +** Output tab separated value data. +*/ +{ + int retval = 0; + MSMap_Module module; + + memset(&module, 0, sizeof(module)); + + /* + ** Read in the map file. + */ + retval = readmap(inOptions, &module); + if(0 == retval) + { + unsigned symLoop = 0; + MSMap_Symbol* symbol = NULL; + unsigned secLoop = 0; + MSMap_Segment* section = NULL; + unsigned size = 0; + unsigned dbSize = 0; + unsigned offsetSize = 0; + unsigned endOffset = 0; + + /* + ** Quick sort the symbols via RVABase. + */ + qsort(module.mSymbols, module.mSymbolCount, sizeof(MSMap_Symbol), qsortRVABase); + + /* + ** Go through all the symbols (in order by sort). + ** Output their sizes. + */ + for(symLoop = 0; 0 == retval && symLoop < module.mSymbolCount; symLoop++) + { + symbol = &module.mSymbols[symLoop]; + section = getSymbolSection(&module, symbol); + if (!section) + continue; + + /* + ** Use the symbol DB size if available. + */ + dbSize = symbol->mSymDBSize; + + /* + ** Guess using offsets. + ** Is there a next symbol available? If so, its start offset is the end of this symbol. + ** Otherwise, our section offset + length is the end of this symbol. + ** + ** The trick is, the DB size can not go beyond the offset size, for sanity. + */ + + /* + ** Try next symbol, but only if in same section. + ** If still not, use the end of the segment. + ** This implies we were the last symbol in the segment. + */ + if((symLoop + 1) < module.mSymbolCount) + { + MSMap_Symbol* nextSymbol = NULL; + MSMap_Segment* nextSection = NULL; + + nextSymbol = &module.mSymbols[symLoop + 1]; + nextSection = getSymbolSection(&module, nextSymbol); + + if(section == nextSection) + { + endOffset = nextSymbol->mOffset; + } + else + { + endOffset = section->mOffset + section->mLength; + } + } + else + { + endOffset = section->mOffset + section->mLength; + } + + /* + ** Can now guess at size. + */ + offsetSize = endOffset - symbol->mOffset; + + /* + ** Now, determine which size to use. + ** This is really a sanity check as well. + */ + size = offsetSize; + if(0 != dbSize) + { + if(dbSize < offsetSize) + { + size = dbSize; + } + } + + /* + ** Output the symbol with the size. + */ + retval = tsvout(inOptions, + size, + section->mClass, + symbol->mScope, + module.mModule, + section->mSegment, + symbol->mObject, + symbol->mSymbol + ); + + /* + ** Make sure we mark this amount of space as used in the section. + */ + section->mUsed += size; + } + + /* + ** Go through the sections, and those whose length is longer than the + ** amount of space used, output dummy filler values. + */ + for(secLoop = 0; 0 == retval && secLoop < module.mSegmentCount; secLoop++) + { + section = &module.mSegments[secLoop]; + + if(section && section->mUsed < section->mLength) + { + retval = tsvout(inOptions, + section->mLength - section->mUsed, + section->mClass, + UNDEFINED, + module.mModule, + section->mSegment, + NULL, + NULL + ); + } + } + } + + /* + ** Cleanup. + */ + cleanModule(&module); + + return retval; +} + + +int initOptions(Options* outOptions, int inArgc, char** inArgv) +/* +** returns int 0 if successful. +*/ +{ + int retval = 0; + int loop = 0; + int switchLoop = 0; + int match = 0; + const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); + Switch* current = NULL; + + /* + ** Set any defaults. + */ + memset(outOptions, 0, sizeof(Options)); + outOptions->mProgramName = inArgv[0]; + outOptions->mInput = stdin; + outOptions->mInputName = strdup("stdin"); + outOptions->mOutput = stdout; + outOptions->mOutputName = strdup("stdout"); + + if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName) + { + retval = __LINE__; + ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup."); + } + + /* + ** Go through and attempt to do the right thing. + */ + for(loop = 1; loop < inArgc && 0 == retval; loop++) + { + match = 0; + current = NULL; + + for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++) + { + if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop])) + { + match = __LINE__; + } + else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop])) + { + match = __LINE__; + } + + if(match) + { + if(gSwitches[switchLoop]->mHasValue) + { + /* + ** Attempt to absorb next option to fullfill value. + */ + if(loop + 1 < inArgc) + { + loop++; + + current = gSwitches[switchLoop]; + current->mValue = inArgv[loop]; + } + } + else + { + current = gSwitches[switchLoop]; + } + + break; + } + } + + if(0 == match) + { + outOptions->mHelp = __LINE__; + retval = __LINE__; + ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch."); + } + else if(NULL == current) + { + outOptions->mHelp = __LINE__; + retval = __LINE__; + ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value."); + } + else + { + /* + ** Do something based on address/swtich. + */ + if(current == &gInputSwitch) + { + CLEANUP(outOptions->mInputName); + if(NULL != outOptions->mInput && stdin != outOptions->mInput) + { + fclose(outOptions->mInput); + outOptions->mInput = NULL; + } + + outOptions->mInput = fopen(current->mValue, "r"); + if(NULL == outOptions->mInput) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to open input file."); + } + else + { + outOptions->mInputName = strdup(current->mValue); + if(NULL == outOptions->mInputName) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to strdup."); + } + } + } + else if(current == &gOutputSwitch) + { + CLEANUP(outOptions->mOutputName); + if(NULL != outOptions->mOutput && stdout != outOptions->mOutput) + { + fclose(outOptions->mOutput); + outOptions->mOutput = NULL; + } + + outOptions->mOutput = fopen(current->mValue, "a"); + if(NULL == outOptions->mOutput) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to open output file."); + } + else + { + outOptions->mOutputName = strdup(current->mValue); + if(NULL == outOptions->mOutputName) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to strdup."); + } + } + } + else if(current == &gHelpSwitch) + { + outOptions->mHelp = __LINE__; + } + else if(current == &gMatchModuleSwitch) + { + void* moved = NULL; + + /* + ** Add the value to the list of allowed module names. + */ + moved = realloc(outOptions->mMatchModules, sizeof(char*) * (outOptions->mMatchModuleCount + 1)); + if(NULL != moved) + { + outOptions->mMatchModules = (char**)moved; + outOptions->mMatchModules[outOptions->mMatchModuleCount] = strdup(current->mValue); + if(NULL != outOptions->mMatchModules[outOptions->mMatchModuleCount]) + { + outOptions->mMatchModuleCount++; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to duplicate string."); + } + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to allocate space for string."); + } + } + else if(current == &gSymDBSwitch) + { + CLEANUP(outOptions->mSymDBName); + outOptions->mSymDBName = strdup(current->mValue); + if(NULL == outOptions->mSymDBName) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to duplicate symbol db name."); + } + } + else if(current == &gBatchModeSwitch) + { + outOptions->mBatchMode = __LINE__; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mLongName, "No handler for command line switch."); + } + } + } + + return retval; +} + + +void cleanOptions(Options* inOptions) +/* +** Clean up any open handles, et. al. +*/ +{ + CLEANUP(inOptions->mInputName); + if(NULL != inOptions->mInput && stdin != inOptions->mInput) + { + fclose(inOptions->mInput); + } + CLEANUP(inOptions->mOutputName); + if(NULL != inOptions->mOutput && stdout != inOptions->mOutput) + { + fclose(inOptions->mOutput); + } + while(0 != inOptions->mMatchModuleCount) + { + inOptions->mMatchModuleCount--; + CLEANUP(inOptions->mMatchModules[inOptions->mMatchModuleCount]); + } + CLEANUP(inOptions->mMatchModules); + + cleanSymDB(&inOptions->mSymDB); + + memset(inOptions, 0, sizeof(Options)); +} + + +void showHelp(Options* inOptions) +/* +** Show some simple help text on usage. +*/ +{ + int loop = 0; + const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); + const char* valueText = NULL; + + printf("usage:\t%s [arguments]\n", inOptions->mProgramName); + printf("\n"); + printf("arguments:\n"); + + for(loop = 0; loop < switchCount; loop++) + { + if(gSwitches[loop]->mHasValue) + { + valueText = " <value>"; + } + else + { + valueText = ""; + } + + printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText); + printf("\t %s%s", gSwitches[loop]->mShortName, valueText); + printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription); + } + + printf("This tool normalizes MS linker .map files for use by other tools.\n"); +} + + +int batchMode(Options* inOptions) +/* +** Batch mode means that the input file is actually a list of map files. +** We simply swap out our input file names while we do this. +*/ +{ + int retval = 0; + char lineBuf[0x400]; + FILE* realInput = NULL; + char* realInputName = NULL; + FILE* mapFile = NULL; + int finalRes = 0; + + realInput = inOptions->mInput; + realInputName = inOptions->mInputName; + + while(0 == retval && NULL != fgets(lineBuf, sizeof(lineBuf), realInput)) + { + trimWhite(lineBuf); + + /* + ** Skip/allow blank lines. + */ + if('\0' == lineBuf[0]) + { + continue; + } + + /* + ** Override what we believe to be the input for this line. + */ + inOptions->mInputName = lineBuf; + inOptions->mInput = fopen(lineBuf, "r"); + if(NULL != inOptions->mInput) + { + int mapRes = 0; + + /* + ** Do it. + */ + mapRes = map2tsv(inOptions); + + /* + ** We report the first error that we encounter, but we continue. + ** This is batch mode after all. + */ + if(0 == finalRes) + { + finalRes = mapRes; + } + + /* + ** Close the input file. + */ + fclose(inOptions->mInput); + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, lineBuf, "Unable to open map file."); + break; + } + } + + if(0 == retval && 0 != ferror(realInput)) + { + retval = __LINE__; + ERROR_REPORT(retval, realInputName, "Unable to read file."); + } + + /* + ** Restore what we've swapped. + */ + inOptions->mInput = realInput; + inOptions->mInputName = realInputName; + + /* + ** Report first map file error if there were no other operational + ** problems. + */ + if(0 == retval) + { + retval = finalRes; + } + + return retval; +} + + +int main(int inArgc, char** inArgv) +{ + int retval = 0; + Options options; + + retval = initOptions(&options, inArgc, inArgv); + if(options.mHelp) + { + showHelp(&options); + } + else if(0 == retval) + { + if(options.mBatchMode) + { + retval = batchMode(&options); + } + else + { + retval = map2tsv(&options); + } + } + + cleanOptions(&options); + return retval; +} + diff --git a/third_party/codesighs/nm2tsv.c b/third_party/codesighs/nm2tsv.c new file mode 100644 index 0000000..07b4563 --- /dev/null +++ b/third_party/codesighs/nm2tsv.c @@ -0,0 +1,505 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is nm2tsv.c code, released + * Oct 10, 2002. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Garrett Arch Blythe, 10-October-2002 + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <ctype.h> + + +#define ERROR_REPORT(num, val, msg) fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg)); +#define CLEANUP(ptr) do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0) + + +typedef struct __struct_Options +/* +** Options to control how we perform. +** +** mProgramName Used in help text. +** mInput File to read for input. +** Default is stdin. +** mInputName Name of the file. +** mOutput Output file, append. +** Default is stdout. +** mOutputName Name of the file. +** mHelp Whether or not help should be shown. +*/ +{ + const char* mProgramName; + FILE* mInput; + char* mInputName; + FILE* mOutput; + char* mOutputName; + int mHelp; +} +Options; + + +typedef struct __struct_Switch +/* +** Command line options. +*/ +{ + const char* mLongName; + const char* mShortName; + int mHasValue; + const char* mValue; + const char* mDescription; +} +Switch; + +#define DESC_NEWLINE "\n\t\t" + +static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."}; +static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."}; +static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."}; + +static Switch* gSwitches[] = { + &gInputSwitch, + &gOutputSwitch, + &gHelpSwitch +}; + + +char* scanWhite(char* inScan) +/* +** Scan for whitespace. +*/ +{ + char* retval = inScan; + + while('\0' != *retval && 0 == isspace(*retval)) + { + retval++; + } + + return retval; +} + + +void trimWhite(char* inString) +/* +** Remove any whitespace from the end of the string. +*/ +{ + int len = strlen(inString); + + while(len) + { + len--; + + if(isspace(*(inString + len))) + { + *(inString + len) = '\0'; + } + else + { + break; + } + } +} + + +int nm2tsv(Options* inOptions) +/* +** Read all input. +** Output tab separated value data. +** +** We expect our data to be in a particular format. +** nm --format=bsd --size-sort --print-file-name --demangle +*/ +{ + int retval = 0; + char lineBuffer[4096]; /* yes, the are some very large symbols */ + char* module = NULL; + char* size = NULL; + char* type = NULL; + char* symbol = NULL; + + /* + ** Read in the nm file. + */ + while(0 == retval && NULL != fgets(lineBuffer, sizeof(lineBuffer), inOptions->mInput)) + { + trimWhite(lineBuffer); + + /* + ** Find the various pieces of information we'll be looking for. + */ + size = strchr(lineBuffer, ':'); + if(NULL != size) + { + *size = '\0'; + size++; + + module = strrchr(lineBuffer, '/'); + if(NULL == module) + { + module = lineBuffer; + } + else + { + *module = '\0'; + module++; + } + + type = scanWhite(size); + *type = '\0'; + type++; + + symbol = type + 1; + *symbol = '\0'; + symbol++; + + /* + ** Skip certain types. + */ + switch(*type) + { + case '-': + continue; + break; + default: + break; + } + + /* + ** Simply output the data with a little more interpretation. + ** First is size. + */ + fprintf(inOptions->mOutput, "%s\t", size); + + /* + ** Type, CODE or DATA + */ + switch(toupper(*type)) + { + case 'T': /* text (code) */ + case 'W': /* weak symbol ??? */ + fprintf(inOptions->mOutput, "CODE\t"); + break; + default: + fprintf(inOptions->mOutput, "DATA\t"); + break; + } + + /* + ** Scope, PUBLIC, STATIC, or UNDEF + */ + if(islower(*type)) + { + fprintf(inOptions->mOutput, "STATIC\t"); + } + else + { + switch(*type) + { + case '?': + fprintf(inOptions->mOutput, "UNDEF\t"); + break; + default: + fprintf(inOptions->mOutput, "PUBLIC\t"); + break; + } + } + + /* + ** Module name, segment. + */ + fprintf(inOptions->mOutput, "%s\t", module); + fprintf(inOptions->mOutput, "%c\t", toupper(*type)); + + /* + ** Origin + */ + fprintf(inOptions->mOutput, "UNDEF:%s:%c\t", module, toupper(*type)); + + /* + ** Symbol is last. + */ + fprintf(inOptions->mOutput, "%s\n", symbol); + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, lineBuffer, "Malformed input line."); + } + } + + if(0 == retval && 0 != ferror(inOptions->mInput)) + { + retval = __LINE__; + ERROR_REPORT(retval, inOptions->mInputName, "Unable to read file."); + } + + return retval; +} + + +int initOptions(Options* outOptions, int inArgc, char** inArgv) +/* +** returns int 0 if successful. +*/ +{ + int retval = 0; + int loop = 0; + int switchLoop = 0; + int match = 0; + const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); + Switch* current = NULL; + + /* + ** Set any defaults. + */ + memset(outOptions, 0, sizeof(Options)); + outOptions->mProgramName = inArgv[0]; + outOptions->mInput = stdin; + outOptions->mInputName = strdup("stdin"); + outOptions->mOutput = stdout; + outOptions->mOutputName = strdup("stdout"); + + if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName) + { + retval = __LINE__; + ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup."); + } + + /* + ** Go through and attempt to do the right thing. + */ + for(loop = 1; loop < inArgc && 0 == retval; loop++) + { + match = 0; + current = NULL; + + for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++) + { + if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop])) + { + match = __LINE__; + } + else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop])) + { + match = __LINE__; + } + + if(match) + { + if(gSwitches[switchLoop]->mHasValue) + { + /* + ** Attempt to absorb next option to fullfill value. + */ + if(loop + 1 < inArgc) + { + loop++; + + current = gSwitches[switchLoop]; + current->mValue = inArgv[loop]; + } + } + else + { + current = gSwitches[switchLoop]; + } + + break; + } + } + + if(0 == match) + { + outOptions->mHelp = __LINE__; + retval = __LINE__; + ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch."); + } + else if(NULL == current) + { + outOptions->mHelp = __LINE__; + retval = __LINE__; + ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value."); + } + else + { + /* + ** Do something based on address/swtich. + */ + if(current == &gInputSwitch) + { + CLEANUP(outOptions->mInputName); + if(NULL != outOptions->mInput && stdin != outOptions->mInput) + { + fclose(outOptions->mInput); + outOptions->mInput = NULL; + } + + outOptions->mInput = fopen(current->mValue, "r"); + if(NULL == outOptions->mInput) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to open input file."); + } + else + { + outOptions->mInputName = strdup(current->mValue); + if(NULL == outOptions->mInputName) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to strdup."); + } + } + } + else if(current == &gOutputSwitch) + { + CLEANUP(outOptions->mOutputName); + if(NULL != outOptions->mOutput && stdout != outOptions->mOutput) + { + fclose(outOptions->mOutput); + outOptions->mOutput = NULL; + } + + outOptions->mOutput = fopen(current->mValue, "a"); + if(NULL == outOptions->mOutput) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to open output file."); + } + else + { + outOptions->mOutputName = strdup(current->mValue); + if(NULL == outOptions->mOutputName) + { + retval = __LINE__; + ERROR_REPORT(retval, current->mValue, "Unable to strdup."); + } + } + } + else if(current == &gHelpSwitch) + { + outOptions->mHelp = __LINE__; + } + else + { + retval = __LINE__; + ERROR_REPORT(retval, current->mLongName, "No handler for command line switch."); + } + } + } + + return retval; +} + + +void cleanOptions(Options* inOptions) +/* +** Clean up any open handles. +*/ +{ + CLEANUP(inOptions->mInputName); + if(NULL != inOptions->mInput && stdin != inOptions->mInput) + { + fclose(inOptions->mInput); + } + CLEANUP(inOptions->mOutputName); + if(NULL != inOptions->mOutput && stdout != inOptions->mOutput) + { + fclose(inOptions->mOutput); + } + + memset(inOptions, 0, sizeof(Options)); +} + + +void showHelp(Options* inOptions) +/* +** Show some simple help text on usage. +*/ +{ + int loop = 0; + const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); + const char* valueText = NULL; + + printf("usage:\t%s [arguments]\n", inOptions->mProgramName); + printf("\n"); + printf("arguments:\n"); + + for(loop = 0; loop < switchCount; loop++) + { + if(gSwitches[loop]->mHasValue) + { + valueText = " <value>"; + } + else + { + valueText = ""; + } + + printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText); + printf("\t %s%s", gSwitches[loop]->mShortName, valueText); + printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription); + } + + printf("This tool normalizes nm output for use by other tools.\n"); + printf("GNU nm is assumed for symbol type determination.\n"); + printf("i.e. Use this tool to parse the output of:\n"); + printf("\t/usr/bin/nm --format=bsd --size-sort --print-file-name --demangle <exefile>\n"); +} + + +int main(int inArgc, char** inArgv) +{ + int retval = 0; + Options options; + + retval = initOptions(&options, inArgc, inArgv); + if(options.mHelp) + { + showHelp(&options); + } + else if(0 == retval) + { + retval = nm2tsv(&options); + } + + cleanOptions(&options); + return retval; +} + diff --git a/third_party/codesighs/nm_wrap_osx.pl b/third_party/codesighs/nm_wrap_osx.pl new file mode 100755 index 0000000..cf1181f --- /dev/null +++ b/third_party/codesighs/nm_wrap_osx.pl @@ -0,0 +1,105 @@ +#!/usr/bin/perl -w +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is autosummary.linx.bash code, released +# Oct 10, 2002. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2002 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Simon Fraser <sfraser@netscape.com> +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +use strict; + +# +# A wrapper for nm that produces output listing symbol size. +# +my($prev_addr) = 0; +my($prev_module) = ""; +my($prev_kind) = ""; +my($prev_symbol) = ""; + +open(NM_OUTPUT, "nm -fnol $ARGV[0] | c++filt |") or die "nm failed to run on $ARGV[0]\n"; +while (<NM_OUTPUT>) +{ + my($line) = $_; + chomp($line); + + if ($line =~ /^([^:]+):\s*([0-9a-f]{8}) (\w) (.+)$/) + { + my($module) = $1; + my($addr) = $2; + my($kind) = $3; + my($symbol) = $4; + + #Skip absolute addresses, there should be only a few + if ('a' eq lc $kind) { + if ('trampoline_size' ne $symbol) { + warn "Encountered unknown absolutely addressed symbol '$symbol' in $module"; + } + next; + } + + # we expect the input to have been piped through c++filt to + # demangle symbols. For some reason, it doesn't always demangle + # all of them, so push still-mangled symbols back through c++filt again. + if ($symbol =~ /^(_[_Z].+)/) + { + # warn "Trying again to unmangle $1\n"; + $symbol = `c++filt '$1'`; + chomp($symbol); + # warn "Unmangling again to $symbol\n"; + } + + my($prev_size) = hex($addr) - hex($prev_addr); + # print "Outputting line $line\n"; + + # always print one behind, because only now do we know its size + if ($prev_module ne "") { + printf "%s:%08x %s %s\n", $prev_module, $prev_size, $prev_kind, $prev_symbol; + } + + $prev_addr = $addr; + $prev_module = $module; + $prev_kind = $kind; + $prev_symbol = $symbol; + } + else + { + # warn " Discaring line $line\n"; + } +} + +# we don't know how big the last symbol is, so always show 4. +if ($prev_module ne "") { + printf "%s:%08x %s %s\n", $prev_module, 4, $prev_kind, $prev_symbol; +} diff --git a/third_party/codesighs/readelf_wrap.pl b/third_party/codesighs/readelf_wrap.pl new file mode 100755 index 0000000..7e2b15e --- /dev/null +++ b/third_party/codesighs/readelf_wrap.pl @@ -0,0 +1,192 @@ +#!/usr/bin/perl -w +# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*- +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is readelf_wrap.pl. +# +# The Initial Developer of the Original Code is +# IBM Corporation. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Brian Ryner <bryner@brianryner.com> +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +use strict; + +# Section fields (the full list of values is in <elf.h>) +my $SECT_NUM = 0; # section index +my $SECT_NAME = 1; # section name +my $SECT_TYPE = 2; # section type +my $SECT_ADDR = 3; # section virtual address +my $SECT_OFF = 4; # section offset in file +my $SECT_SIZE = 5; # size of section +my $SECT_ES = 6; # section entry size +my $SECT_FLG = 7; # section flags +my $SECT_LK = 8; # link to another section +my $SECT_INF = 9; # additional section info +my $SECT_AL = 10; # section alignment + + +# Symbol fields (note: the full list of possible values for each field +# is given in <elf.h>) + +my $SYM_NUM = 0; # unique index of the symbol +my $SYM_VALUE = 1; # value of the symbol +my $SYM_SIZE = 2; # size of the symbol +my $SYM_TYPE = 3; # type (NOTYPE, OBJECT, FUNC, SECTION, FILE, ...) +my $SYM_BIND = 4; # binding/scope (LOCAL, GLOBAL, WEAK, ...) +my $SYM_VIS = 5; # visibility (DEFAULT, INTERNAL, HIDDEN, PROTECTED) +my $SYM_NDX = 6; # index of section the symbol is in +my $SYM_NAME = 7; # name of the symbol +my $SYM_FILE = 8; # (not part of readelf) file for symbol + +# Tell readelf to print out the list of sections and then the symbols +die "Usage: $^X <binary>\n" unless ($#ARGV >= 0); +my $readelf = $ENV{'READELF_PROG'}; +if (!$readelf) { + $readelf = 'readelf'; +} +open(READELF_OUTPUT, "$readelf -Ss $ARGV[0] 2>/dev/null | c++filt |") or die "readelf failed to run on $ARGV[0]\n"; + +my @section_list; +my @symbol_list; +my ($module) = ($ARGV[0] =~ /([^\/]+)$/); +my $in_symbols = 0; + +while (<READELF_OUTPUT>) { + + if (!$in_symbols) { + if (/^ *\[ *(\d+)\]/) { + my @section; + + # note that we strip off the leading '.' of section names for + # readability + if (! (@section = (/^ *\[ *(\d+)\] \.([\w\.\-]+) *(\w+) *(.{8}) (.{6}[0-9a-fA-F]*) (.{6}[0-9a-fA-F]*) *(\d+) ([a-zA-Z]+ +| +[a-zA-Z]+|) *(\d+) *(\w+) *(\d+)/))) { + # capture the 'null' section which has no name, so that the + # array indices are the same as the section indices. + + @section = ($1, '', 'NULL', '00000000', '000000', '000000', + '00', '', '0', '0', '0'); + } + + push (@section_list, \@section); + } elsif (/^Symbol table/) { + $in_symbols = 1; + } + } else { + + my @sym; + + if (@sym = /^\s*(\d+): (\w+)\s*(\d+)\s*(\w+)\s*(\w+)\s*(\w+)\s*(\w+) (.*)/) + { + # Filter out types of symbols that we don't care about: + # - anything that's not of type OBJECT or FUNC + # - any undefined symbols (ndx = UND[EF]) + # - any 0-size symbols + + if (($sym[$SYM_TYPE] !~ /^(OBJECT|FUNC)$/) || + $sym[$SYM_NDX] eq 'UND' || $sym[$SYM_NDX] eq 'UNDEF' + || $sym[$SYM_SIZE] eq '0') { + next; + } + push (@symbol_list, \@sym); + } + elsif (/^Symbol table .*'\.symtab'/) { + # We've been using .dynsym up to this point, but if we have .symtab + # available, it will have everything in .dynsym and more. + # So, reset our symbol list. + + @symbol_list = (); + } + } +} + +close(READELF_OUTPUT); + +# spit them out in codesighs TSV format +my $sym; +my @section_sizes; +$#section_sizes = $#section_list; +foreach (@section_sizes) { $_ = 0; } + +foreach $sym (@symbol_list) { + # size + printf "%08x\t", $sym->[$SYM_SIZE]; + + # code or data + if ($sym->[$SYM_TYPE] eq 'FUNC') { + print "CODE\t"; + } else { # OBJECT + print "DATA\t"; + } + + # scope + if ($sym->[$SYM_BIND] eq 'LOCAL') { + print "STATIC\t"; + } elsif ($sym->[$SYM_BIND] =~ /(GLOBAL|WEAK)/) { + print "PUBLIC\t"; + } else { + print "UNDEF\t"; + } + + # module name + + print "$module\t"; + + # section + my $section = $section_list[$sym->[$SYM_NDX]]->[$SECT_NAME]; + print "$section\t"; + + # should be the object file, but for now just module/section + print "UNDEF:$module:$section\t"; + + # now the symbol name + print $sym->[$SYM_NAME]."\n"; + + # update our cumulative section sizes + $section_sizes[$section_list[$sym->[$SYM_NDX]]->[$SECT_NUM]] += $sym->[$SYM_SIZE]; +} + +# Output extra entries to make the sum of the symbol sizes equal the +# section size. + +my $section; +foreach $section (@section_list) { + + my $diff = hex($section->[$SECT_SIZE]) - $section_sizes[$section->[$SECT_NUM]]; + if ($diff > 0) { + my $sectname = $section->[$SECT_NAME]; + if ($section->[$SECT_NAME] =~ /^(rodata|data|text|bss)/) { + printf "%08x", $diff; + print "\tDATA\tSTATIC\t$module\t$sectname\tUNDEF:$module:$sectname\t.nosyms.$sectname\n"; +# } else { +# print "ignoring $diff bytes of empty space in $sectname section\n"; + } + } +} |