From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mga05.intel.com (mga05.intel.com [192.55.52.43]) by mx.groups.io with SMTP id smtpd.web12.3507.1648303517267006489 for ; Sat, 26 Mar 2022 07:05:17 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="unable to parse pub key" header.i=@intel.com header.s=intel header.b=HtuRmWY0; spf=pass (domain: intel.com, ip: 192.55.52.43, mailfrom: bob.c.feng@intel.com) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1648303517; x=1679839517; h=from:to:cc:subject:date:message-id:references: in-reply-to:content-transfer-encoding:mime-version; bh=WLiQam+cPPoc9I6Kn2R/rhYkTFdIdJlskAHAqLP9boM=; b=HtuRmWY02JkCYFFXNPtuxja7bhGzd1/qyEnI2ljIZ8tBIFLcDBsJsOgY kdjmawGcrugcBE3I9CfFv00MPfxZ+FSlMMx4NmI7DPauMUFb6NKhlTqc8 5JFEJ3KSSc6WRyr+cIgi3PmkDFgX08841kkZXYUkmLN+7MEpyYyUzChPn Bp9zyMNsug+2SLVio+UIV+TmPGwmM00Y4LfKgyWgo75/YGkGOPAZPGpKg jrwR+eN0TAcYJ0wSPpC15Xkxp17kStE5gMPoNqiARSnfuFz/gdi4ElpvX 2OR2GMaAO5c9f7INhreGlDzcAbpuhjzMBK1TiydIxiZ+mBhIf/h8uM6i+ Q==; X-IronPort-AV: E=McAfee;i="6200,9189,10298"; a="345220973" X-IronPort-AV: E=Sophos;i="5.90,213,1643702400"; d="scan'208";a="345220973" Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by fmsmga105.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 26 Mar 2022 07:05:17 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.90,213,1643702400"; d="scan'208";a="638488568" Received: from orsmsx603.amr.corp.intel.com ([10.22.229.16]) by FMSMGA003.fm.intel.com with ESMTP; 26 Mar 2022 07:05:16 -0700 Received: from orsmsx609.amr.corp.intel.com (10.22.229.22) by ORSMSX603.amr.corp.intel.com (10.22.229.16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2308.27; Sat, 26 Mar 2022 07:05:16 -0700 Received: from orsmsx609.amr.corp.intel.com (10.22.229.22) by ORSMSX609.amr.corp.intel.com (10.22.229.22) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2308.27; Sat, 26 Mar 2022 07:05:15 -0700 Received: from ORSEDG602.ED.cps.intel.com (10.7.248.7) by orsmsx609.amr.corp.intel.com (10.22.229.22) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2308.27 via Frontend Transport; Sat, 26 Mar 2022 07:05:15 -0700 Received: from NAM12-BN8-obe.outbound.protection.outlook.com (104.47.55.177) by edgegateway.intel.com (134.134.137.103) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2308.21; Sat, 26 Mar 2022 07:05:15 -0700 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=FTTZUq4mJ4SZZbR8coIOAmvUW0NO61YVhaNpQjSK0F2BrHGQKphIOsb/vIIvfRzlviLf0nu98UEPBHnvjZy3LNUb0uRnxkqNu6HXjwpilNzU/CU3JWD1Y9QRu9RKwKyRX43ehAGSV3fj0cOlM2qARHRSuKyh+3zY+JljunsdyjamAalyZNFG4AB8xBJH507QzDzyfED2jFmFukxZI7Qwwq06i/LwCOynAk1PQ4LRKWP+F6cLebzv1UYh29wNbm0b301saJxS0DigORHn/91F1NhEZjddQchffgOB8loX4lV7Efea0OzExnAoUB5x0pqzmaVnegNkGOZzM5IniS+q3A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=+EJ2RD9Xw+r6SlqBzukHzYCSYTpxmIAuLqyCIS9wBhY=; b=h7EHtMPT0PenLnz5AeYIwoxto1bOcxZmxbTjm/xSd/HlGrvNhzYsEbFTdtdc7koSU7nAsWZ4etMfFlboKNK6zEl2hmQ8LKS53D/pw8SKNG3WuIj6O7/mbQaECUtxGffu8yTriD6ZvZTKjuXRGe8g2bpmJ4szlkzjGQW3Rt4xbxp7d0uduc1C5g7G4ZcADX4+FcAU4lUgBSE8IGxykQH9PP+i/OQzQwwe4wzv2KE5rP24Fmw7b9I4J9ln0c8tYiyIhFJdaeGcSgNaYy4j+Hggg5NiFWiRcLq7hGfOOotUb/JOOH/jsav+5wNSdprREdmu0+BLhZ01sccjGCouABUxbw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=intel.com; dmarc=pass action=none header.from=intel.com; dkim=pass header.d=intel.com; arc=none Received: from PH7PR11MB5863.namprd11.prod.outlook.com (2603:10b6:510:135::11) by DM5PR11MB1372.namprd11.prod.outlook.com (2603:10b6:3:11::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5102.22; Sat, 26 Mar 2022 14:05:12 +0000 Received: from PH7PR11MB5863.namprd11.prod.outlook.com ([fe80::9cca:b173:d2c9:1d21]) by PH7PR11MB5863.namprd11.prod.outlook.com ([fe80::9cca:b173:d2c9:1d21%3]) with mapi id 15.20.5102.019; Sat, 26 Mar 2022 14:05:12 +0000 From: "Bob Feng" To: Rebecca Cran , "devel@edk2.groups.io" , Leif Lindholm , "Kinney, Michael D" , "Wu, Hao A" , "Gao, Liming" , "Chen, Christine" CC: Andrew Fish Subject: Re: [PATCH v2 2/3] BaseTools: Scripts/efi_gdb.py: Add gdb EFI commands and pretty Print Thread-Topic: [PATCH v2 2/3] BaseTools: Scripts/efi_gdb.py: Add gdb EFI commands and pretty Print Thread-Index: AQHYPWFFoc6qBmfotkifk2/HeNQH2qzRui2g Date: Sat, 26 Mar 2022 14:05:11 +0000 Message-ID: References: <20220321202048.13567-1-quic_rcran@quicinc.com> <20220321202048.13567-3-quic_rcran@quicinc.com> In-Reply-To: <20220321202048.13567-3-quic_rcran@quicinc.com> Accept-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=intel.com; x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 2aceafd6-6a5b-4cdb-596a-08da0f31a4eb x-ms-traffictypediagnostic: DM5PR11MB1372:EE_ x-ld-processed: 46c98d88-e344-4ed4-8496-4ed7712e255d,ExtAddr x-microsoft-antispam-prvs: x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: jSKods03ulZcukJcCuSriwoCs/xyeSf6m4OuEFTyzO3c3rnDjtTyO1Fnqp5PGk2U7wjsGOmgX2Eb484A7JLOOQjsAfFzyXxs2aXrc51qBdhE6kUX7h3Rvp3yWWks+yuk3FfYlKoEqe/vseNKICC50nqegurxWJsAeHfJIN/5yi3+kHVYhtQUbITwczvm9TAopS/aRo21SlBT5YH0DHxhoRnJnKOG6/wvuvNj1JHX2Z5PQceX7pvrQjcKTNojR6jnnDCtx23HYFtTeCSwR9xWbxt6kfaa84+oySfPZLPG79Wly7v5GOWZgoyY833LdYNbUW7w38j8hcXvOSwcl4NAoRWGopOPEtt1RhQfV+2PaBATwBWzQZtPR9EgkBv87IbzPeHqnFeT1B6R7Q8IBgAECBLBf78oo6QQAgX12plUjSfozDs2ZvDfkWYxA0j9wKKmfYPq3Zh01EFY2IxeOkNw2m0DhtH0SyL2Jth2spOwvIk4+x7d6mOIyr90Tcbjnkwiu3q/PtiOnsofmfL0Eb8o88miSCdwQIry12q8Nb/0TpMqtslvoNVaLA+Zj57P8Pnu2k9VlMRsfQz29cNgZ9PFrYye8OW0tLzoGYf06QmE99Ut9As2j65zeGQ4tALewHnMiN5b7F7hwb0t0OHuwe5awBKow/44PSTSpTKS5nVEWt8XZgRXNhqVfnRftPCi/mypRLd1XYNLop0pEe2tGX0yRGC+An51YE8dWKDB+QOQuqIsuD3pYNWK3ZgjgAjI48remKoZntIeVq3rFaA3ZDe+29/bc8LnY3a+2SnGFyb+eRZvk9FExhNpzsBVP1wjQlYT x-forefront-antispam-report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PH7PR11MB5863.namprd11.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230001)(366004)(71200400001)(76116006)(921005)(82960400001)(122000001)(8676002)(4326008)(38100700002)(66946007)(66476007)(66446008)(64756008)(66556008)(2906002)(5660300002)(6636002)(110136005)(30864003)(316002)(86362001)(52536014)(33656002)(8936002)(38070700005)(55016003)(26005)(186003)(508600001)(83380400001)(966005)(9686003)(6506007)(7696005)(53546011)(579004)(559001);DIR:OUT;SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?us-ascii?Q?2HWozziqjU9caryJJVK6V3stM449BjIRuG09TjAD9g3zZ64iGQhBvrwLAtyE?= =?us-ascii?Q?YJrvUNEWv1916tjcV0gcBfFWBL9Ari7tBynpMXDeAQ6O39ha2lGLbVjETyn4?= =?us-ascii?Q?U1M+shSUYbbsQAimDtinL4yFPKj1S8pVmJzV1FP0ovdUlJBAsMiXnlK0N2Gu?= =?us-ascii?Q?ew7QxlI309L6QrkR7q9FZZTPMw4nlTDDC4Y77o2uJBHV0zNL2IHWEYJAPEup?= =?us-ascii?Q?MMfMQe7hdrd6EYOMooO+aAXLgPj+q8LAQvKBLujJjVSR7F9vb7POYuoBDVrv?= =?us-ascii?Q?lFx9Stc0h7+oZ9hyr7cGSddORENKhe1nzSPRQZjJ2IlPn+b7Gsvh89R1yP4y?= =?us-ascii?Q?acg2opFjoZVMqiweRVuaqn6izSWqGqFVKswXSxqF0wxp7v//yGuVfMdWexEu?= =?us-ascii?Q?9MzhMTYehkCDTIb/z8c7mr/GOJNOZL8Qs5Ow6t3tplmcvqtG8GVssOYqzuEf?= =?us-ascii?Q?ZLmnggokCASjXUP+jR4COisTwzEsJKj12Y3UkB4D4c/sLPPbVz7s3x3kO4Bh?= =?us-ascii?Q?lz90Tz8CJ9DCYxmPDY/zYYGZbDvMlqWxwnxNveTa9EtQ30jAKOm7ZAkJe/0h?= =?us-ascii?Q?L9Y1z5Vt7jIylMlialoIH8z61O3avzKW+17kOEs/e1lAV9UsU+6lmFediFye?= =?us-ascii?Q?Mmt/2P54SUINHjsnDK5XKopDAF1ysxs8RYgoZNZspbhU9ce/EmqSE91Nzrfg?= =?us-ascii?Q?S4LiBiz59NPrugbFd9gZOnhxj69pEfLJIhEKoN3j26y8tsGAPHR6oJD0WgAG?= =?us-ascii?Q?wooHX+CN+w/drANfGqvTxWNv3xYHhjHyJVCCAsOuMlRtk9AYjGZo9NPA7vqH?= =?us-ascii?Q?PGg/vdAXPJ9a7N2kCiC5qU4dWIAGc+At+6GumY7rvBac6NxKiL1zEkX81qWG?= =?us-ascii?Q?BBUbH98kMnOzKPuQYiOdRRKSC1kPItmS/3+DtaxkwPbzo64jjoqPkxRErHd0?= =?us-ascii?Q?Gcw/+eGy+SIM757ZudOvrxn+PCdqdfEuQCv0w938Oqp8e6M8SkWsIRf+HPHf?= =?us-ascii?Q?dhM65e7e42OoullFSJC8BElw9PpZbq/l5gWAugc+6h7V/J85a2rDSc4ekRXR?= =?us-ascii?Q?YTC3njDPp+rss0KrF2UuAX+TwXLronefCSc+YxWPd17PbqFSclyF7AD/fHoF?= =?us-ascii?Q?7iD4s3j4VbM/WFA5hzMlRgp/0qFEG/87iLUoXB5DKXc+vEVyeJZ1YCO2IOlF?= =?us-ascii?Q?jKIka7FIfQPqCRQh0ee8X/tUtmmsgY6gNt7GRGeaRTMVVMRclH5eVSbJD3HH?= =?us-ascii?Q?pFMxI+leL3hAapv7sK+J1gVc0cM7YyEPeDyuxIeHRUZKUiOwbV2ZbBwV1Apb?= =?us-ascii?Q?NaGe1ztU1eiyYvOH4Bgwis3jshTkeohAvu3xsQStTbl2kbxcJs9qYWEUFRYb?= =?us-ascii?Q?snp/PlH5ftZYD8nQ+ZBhINb4oLJ+MurZuXQN1yZff+j3jUWXUtlsCpXOS+dZ?= =?us-ascii?Q?YwKNmGMctdP4wNPjc6YnIUR6p+cC6W+dfJsxZYhf0528gXc3LgZGNm1toEqE?= =?us-ascii?Q?rHsZFVc83gB07oIepefAHZBaiOzRS9VIPz0uoyKt6Y1hQJ5m2p0lsNu/vCo7?= =?us-ascii?Q?xCO9FLCmWPP9sTC7x/+ae22DUBMeUlBJG9a1hxUkpHDbRmWQZsmh4oR2/c/g?= =?us-ascii?Q?MSnYDC8VjNRijfmke2VcSPCxPT140p//KWk4z34ZRl2UVFgrkBUxHMA3l7XH?= =?us-ascii?Q?Ad2QvbIr4Md6MKBI4pS5/HGDJdHFIPZqupkMuV34jBr9jIQ7ncmL5J5Qgsup?= =?us-ascii?Q?tFHaSvofZw=3D=3D?= MIME-Version: 1.0 X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: PH7PR11MB5863.namprd11.prod.outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 2aceafd6-6a5b-4cdb-596a-08da0f31a4eb X-MS-Exchange-CrossTenant-originalarrivaltime: 26 Mar 2022 14:05:11.9630 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 46c98d88-e344-4ed4-8496-4ed7712e255d X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: y3BfsN1OOl2FTer3fQsdWEf4d56AyS3eF5pZdjrB/UnnbJFTV/mU5w0PWvPdfzbeK55QvgrN4hs2EzWdBcscAg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM5PR11MB1372 Return-Path: bob.c.feng@intel.com X-OriginatorOrg: intel.com Content-Language: en-US Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable Reviewed-by: Bob Feng -----Original Message----- From: Rebecca Cran =20 Sent: Tuesday, March 22, 2022 4:21 AM To: devel@edk2.groups.io; Leif Lindholm ; Kinney= , Michael D ; Wu, Hao A ; F= eng, Bob C ; Gao, Liming ; = Chen, Christine Cc: Rebecca Cran ; Andrew Fish Subject: [PATCH v2 2/3] BaseTools: Scripts/efi_gdb.py: Add gdb EFI commands= and pretty Print https://bugzilla.tianocore.org/show_bug.cgi?id=3D3500 Use efi_debugging.py Python Classes to implement EFI gdb commands: (gdb) help efi Commands for debugging EFI. efi List of efi subcommands: efi devicepath -- Display an EFI device path. efi guid -- Display info about EFI GUID's. efi hob -- Dump EFI HOBs. Type 'hob -h' for more info. efi symbols -- Load Symbols for EFI. Type 'efi_symbols -h' for more info. efi table -- Dump EFI System Tables. Type 'table -h' for more info. This module is coded against a generic gdb remote serial stub. It should wo= rk with QEMU, JTAG debugger, or a generic EFI gdb remote serial stub. No modifications of EFI is required to load symbols. Example usage: OvmfPkg/build.sh qemu -gdb tcp::9000 gdb -ex "target remote localhost:9000" -ex "source efi_gdb.py" Cc: Leif Lindholm Cc: Michael D Kinney Cc: Hao A Wu Cc: Bob Feng Cc: Liming Gao Cc: Yuwei Chen Signed-off-by: Rebecca Cran --- BaseTools/Scripts/efi_gdb.py | 918 ++++++++++++++++++++ 1 file changed, 918 insertions(+) diff --git a/BaseTools/Scripts/efi_gdb.py b/BaseTools/Scripts/efi_gdb.py ne= w file mode 100755 index 000000000000..e9bae8e9b913 --- /dev/null +++ b/BaseTools/Scripts/efi_gdb.py @@ -0,0 +1,918 @@ +#!/usr/bin/python3 +''' +Copyright 2021 (c) Apple Inc. All rights reserved. +SPDX-License-Identifier: BSD-2-Clause-Patent + +EFI gdb commands based on efi_debugging classes. + +Example usage: +OvmfPkg/build.sh qemu -gdb tcp::9000 +gdb -ex "target remote localhost:9000" -ex "source efi_gdb.py" + +(gdb) help efi +Commands for debugging EFI. efi + +List of efi subcommands: + +efi devicepath -- Display an EFI device path. +efi guid -- Display info about EFI GUID's. +efi hob -- Dump EFI HOBs. Type 'hob -h' for more info. +efi symbols -- Load Symbols for EFI. Type 'efi_symbols -h' for more info. +efi table -- Dump EFI System Tables. Type 'table -h' for more info. + +This module is coded against a generic gdb remote serial stub. It=20 +should work with QEMU, JTAG debugger, or a generic EFI gdb remote serial s= tub. + +If you are debugging with QEMU or a JTAG hardware debugger you can=20 +insert a CpuDeadLoop(); in your code, attach with gdb, and then `p=20 +Index=3D1` to step past. If you have a debug stub in EFI you can use CpuBr= eakpoint();. +''' + +from gdb.printing import RegexpCollectionPrettyPrinter from=20 +gdb.printing import register_pretty_printer import gdb import os import=20 +sys import uuid import optparse import shlex + +# gdb will not import from the same path as this script. +# so lets fix that for gdb... +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from efi_debugging import PeTeImage, patch_ctypes # noqa: E402 +from efi_debugging import EfiHob, GuidNames, EfiStatusClass # noqa: E402 +from efi_debugging import EfiBootMode, EfiDevicePath # noqa: E402 +from efi_debugging import EfiConfigurationTable, EfiTpl # noqa: E402 + + +class GdbFileObject(object): + '''Provide a file like object required by efi_debugging''' + + def __init__(self): + self.inferior =3D gdb.selected_inferior() + self.offset =3D 0 + + def tell(self): + return self.offset + + def read(self, size=3D-1): + if size =3D=3D -1: + # arbitrary default size + size =3D 0x1000000 + + try: + data =3D self.inferior.read_memory(self.offset, size) + except MemoryError: + data =3D bytearray(size) + assert False + if len(data) !=3D size: + raise MemoryError( + f'gdb could not read memory 0x{size:x}' + + f' bytes from 0x{self.offset:08x}') + else: + # convert memoryview object to a bytestring. + return data.tobytes() + + def readable(self): + return True + + def seek(self, offset, whence=3D0): + if whence =3D=3D 0: + self.offset =3D offset + elif whence =3D=3D 1: + self.offset +=3D offset + else: + # whence =3D=3D 2 is seek from end + raise NotImplementedError + + def seekable(self): + return True + + def write(self, data): + self.inferior.write_memory(self.offset, data) + return len(data) + + def writable(self): + return True + + def truncate(self, size=3DNone): + raise NotImplementedError + + def flush(self): + raise NotImplementedError + + def fileno(self): + raise NotImplementedError + + +class EfiSymbols: + """Class to manage EFI Symbols""" + + loaded =3D {} + stride =3D None + range =3D None + verbose =3D False + + def __init__(self, file=3DNone): + EfiSymbols.file =3D file if file else GdbFileObject() + + @ classmethod + def __str__(cls): + return ''.join(f'{value}\n' for value in cls.loaded.values()) + + @ classmethod + def configure_search(cls, stride, range=3DNone, verbose=3DFalse): + cls.stride =3D stride + cls.range =3D range + cls.verbose =3D verbose + + @ classmethod + def clear(cls): + cls.loaded =3D {} + + @ classmethod + def add_symbols_for_pecoff(cls, pecoff): + '''Tell lldb the location of the .text and .data sections.''' + + if pecoff.TextAddress in cls.loaded: + return 'Already Loaded: ' + try: + res =3D 'Loading Symbols Failed:' + res =3D gdb.execute('add-symbol-file ' + pecoff.CodeViewPdb + + ' ' + hex(pecoff.TextAddress) + + ' -s .data ' + hex(pecoff.DataAddress), + False, True) + + cls.loaded[pecoff.TextAddress] =3D pecoff + if cls.verbose: + print(f'\n{res:s}\n') + return '' + except gdb.error: + return res + + @ classmethod + def address_to_symbols(cls, address, reprobe=3DFalse): + ''' + Given an address search backwards for a PE/COFF (or TE) header + and load symbols. Return a status string. + ''' + if not isinstance(address, int): + address =3D int(address) + + pecoff =3D cls.address_in_loaded_pecoff(address) + if not reprobe and pecoff is not None: + # skip the probe of the remote + return f'{pecoff} is already loaded' + + pecoff =3D PeTeImage(cls.file, None) + if pecoff.pcToPeCoff(address, cls.stride, cls.range): + res =3D cls.add_symbols_for_pecoff(pecoff) + return f'{res}{pecoff}' + else: + return f'0x{address:08x} not in a PE/COFF (or TE) image' + + @ classmethod + def address_in_loaded_pecoff(cls, address): + if not isinstance(address, int): + address =3D int(address) + + for value in cls.loaded.values(): + if (address >=3D value.LoadAddress and + address <=3D value.EndLoadAddress): + return value + + return None + + @ classmethod + def unload_symbols(cls, address): + if not isinstance(address, int): + address =3D int(address) + + pecoff =3D cls.address_in_loaded_pecoff(address) + try: + res =3D 'Unloading Symbols Failed:' + res =3D gdb.execute( + f'remove-symbol-file -a {hex(pecoff.TextAddress):s}', + False, True) + del cls.loaded[pecoff.LoadAddress] + return res + except gdb.error: + return res + + +class CHAR16_PrettyPrinter(object): + + def __init__(self, val): + self.val =3D val + + def to_string(self): + if int(self.val) < 0x20: + return f"L'\\x{int(self.val):02x}'" + else: + return f"L'{chr(self.val):s}'" + + +class EFI_TPL_PrettyPrinter(object): + + def __init__(self, val): + self.val =3D val + + def to_string(self): + return str(EfiTpl(int(self.val))) + + +class EFI_STATUS_PrettyPrinter(object): + + def __init__(self, val): + self.val =3D val + + def to_string(self): + status =3D int(self.val) + return f'{str(EfiStatusClass(status)):s} (0x{status:08x})' + + +class EFI_BOOT_MODE_PrettyPrinter(object): + + def __init__(self, val): + self.val =3D val + + def to_string(self): + return str(EfiBootMode(int(self.val))) + + +class EFI_GUID_PrettyPrinter(object): + """Print 'EFI_GUID' as 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'""" + + def __init__(self, val): + self.val =3D val + + def to_string(self): + # if we could get a byte like object of *(unsigned char (*)[16]) + # then we could just use uuid.UUID() to convert + Data1 =3D int(self.val['Data1']) + Data2 =3D int(self.val['Data2']) + Data3 =3D int(self.val['Data3']) + Data4 =3D self.val['Data4'] + guid =3D f'{Data1:08X}-{Data2:04X}-' + guid +=3D f'{Data3:04X}-' + guid +=3D f'{int(Data4[0]):02X}{int(Data4[1]):02X}-' + guid +=3D f'{int(Data4[2]):02X}{int(Data4[3]):02X}' + guid +=3D f'{int(Data4[4]):02X}{int(Data4[5]):02X}' + guid +=3D f'{int(Data4[6]):02X}{int(Data4[7]):02X}' + return str(GuidNames(guid)) + + +def build_pretty_printer(): + # Turn off via: disable pretty-printer global EFI + pp =3D RegexpCollectionPrettyPrinter("EFI") + # you can also tell gdb `x/sh
` to print CHAR16 string + pp.add_printer('CHAR16', '^CHAR16$', CHAR16_PrettyPrinter) + pp.add_printer('EFI_BOOT_MODE', '^EFI_BOOT_MODE$', + EFI_BOOT_MODE_PrettyPrinter) + pp.add_printer('EFI_GUID', '^EFI_GUID$', EFI_GUID_PrettyPrinter) + pp.add_printer('EFI_STATUS', '^EFI_STATUS$', EFI_STATUS_PrettyPrinter) + pp.add_printer('EFI_TPL', '^EFI_TPL$', EFI_TPL_PrettyPrinter) + return pp + + +class EfiDevicePathCmd (gdb.Command): + """Display an EFI device path. Type 'efi devicepath -h' for more info"= "" + + def __init__(self): + super(EfiDevicePathCmd, self).__init__( + "efi devicepath", gdb.COMMAND_NONE) + + self.file =3D GdbFileObject() + + def create_options(self, arg, from_tty): + usage =3D "usage: %prog [options] [arg]" + description =3D ( + "Command that can load EFI PE/COFF and TE image symbols. ") + + self.parser =3D optparse.OptionParser( + description=3Ddescription, + prog=3D'efi devicepath', + usage=3Dusage, + add_help_option=3DFalse) + + self.parser.add_option( + '-v', + '--verbose', + action=3D'store_true', + dest=3D'verbose', + help=3D'hex dump extra data', + default=3DFalse) + + self.parser.add_option( + '-n', + '--node', + action=3D'store_true', + dest=3D'node', + help=3D'dump a single device path node', + default=3DFalse) + + self.parser.add_option( + '-h', + '--help', + action=3D'store_true', + dest=3D'help', + help=3D'Show help for the command', + default=3DFalse) + + return self.parser.parse_args(shlex.split(arg)) + + def invoke(self, arg, from_tty): + '''gdb command to dump EFI device paths''' + + try: + (options, _) =3D self.create_options(arg, from_tty) + if options.help: + self.parser.print_help() + return + + dev_addr =3D int(gdb.parse_and_eval(arg)) + except ValueError: + print("Invalid argument!") + return + + if options.node: + print(EfiDevicePath( + self.file).device_path_node_str(dev_addr, + options.verbose)) + else: + device_path =3D EfiDevicePath(self.file, dev_addr, options.ver= bose) + if device_path.valid(): + print(device_path) + + +class EfiGuidCmd (gdb.Command): + """Display info about EFI GUID's. Type 'efi guid -h' for more info""" + + def __init__(self): + super(EfiGuidCmd, self).__init__("efi guid", + gdb.COMMAND_NONE, + gdb.COMPLETE_EXPRESSION) + self.file =3D GdbFileObject() + + def create_options(self, arg, from_tty): + usage =3D "usage: %prog [options] [arg]" + description =3D ( + "Show EFI_GUID values and the C name of the EFI_GUID variables= " + "in the C code. If symbols are loaded the Guid.xref file" + "can be processed and the complete GUID database can be shown.= " + "This command also suports generating new GUID's, and showing" + "the value used to initialize the C variable.") + + self.parser =3D optparse.OptionParser( + description=3Ddescription, + prog=3D'efi guid', + usage=3Dusage, + add_help_option=3DFalse) + + self.parser.add_option( + '-n', + '--new', + action=3D'store_true', + dest=3D'new', + help=3D'Generate a new GUID', + default=3DFalse) + + self.parser.add_option( + '-v', + '--verbose', + action=3D'store_true', + dest=3D'verbose', + help=3D'Also display GUID C structure values', + default=3DFalse) + + self.parser.add_option( + '-h', + '--help', + action=3D'store_true', + dest=3D'help', + help=3D'Show help for the command', + default=3DFalse) + + return self.parser.parse_args(shlex.split(arg)) + + def invoke(self, arg, from_tty): + '''gdb command to dump EFI System Tables''' + + try: + (options, args) =3D self.create_options(arg, from_tty) + if options.help: + self.parser.print_help() + return + if len(args) >=3D 1: + # guid { 0x414e6bdd, 0xe47b, 0x47cc, + # { 0xb2, 0x44, 0xbb, 0x61, 0x02, 0x0c,0xf5, 0x16 }= } + # this generates multiple args + guid =3D ' '.join(args) + except ValueError: + print('bad arguments!') + return + + if options.new: + guid =3D uuid.uuid4() + print(str(guid).upper()) + print(GuidNames.to_c_guid(guid)) + return + + if len(args) > 0: + if GuidNames.is_guid_str(arg): + # guid 05AD34BA-6F02-4214-952E-4DA0398E2BB9 + key =3D guid.upper() + name =3D GuidNames.to_name(key) + elif GuidNames.is_c_guid(arg): + # guid { 0x414e6bdd, 0xe47b, 0x47cc, + # { 0xb2, 0x44, 0xbb, 0x61, 0x02, 0x0c,0xf5, 0x16 }= } + key =3D GuidNames.from_c_guid(arg) + name =3D GuidNames.to_name(key) + else: + # guid gEfiDxeServicesTableGuid + name =3D guid + try: + key =3D GuidNames.to_guid(name) + name =3D GuidNames.to_name(key) + except ValueError: + return + + extra =3D f'{GuidNames.to_c_guid(key)}: ' if options.verbose e= lse '' + print(f'{key}: {extra}{name}') + + else: + for key, value in GuidNames._dict_.items(): + if options.verbose: + extra =3D f'{GuidNames.to_c_guid(key)}: ' + else: + extra =3D '' + print(f'{key}: {extra}{value}') + + +class EfiHobCmd (gdb.Command): + """Dump EFI HOBs. Type 'hob -h' for more info.""" + + def __init__(self): + super(EfiHobCmd, self).__init__("efi hob", gdb.COMMAND_NONE) + self.file =3D GdbFileObject() + + def create_options(self, arg, from_tty): + usage =3D "usage: %prog [options] [arg]" + description =3D ( + "Command that can load EFI PE/COFF and TE image symbols. ") + + self.parser =3D optparse.OptionParser( + description=3Ddescription, + prog=3D'efi hob', + usage=3Dusage, + add_help_option=3DFalse) + + self.parser.add_option( + '-a', + '--address', + type=3D"int", + dest=3D'address', + help=3D'Parse HOBs from address', + default=3DNone) + + self.parser.add_option( + '-t', + '--type', + type=3D"int", + dest=3D'type', + help=3D'Only dump HOBS of his type', + default=3DNone) + + self.parser.add_option( + '-v', + '--verbose', + action=3D'store_true', + dest=3D'verbose', + help=3D'hex dump extra data', + default=3DFalse) + + self.parser.add_option( + '-h', + '--help', + action=3D'store_true', + dest=3D'help', + help=3D'Show help for the command', + default=3DFalse) + + return self.parser.parse_args(shlex.split(arg)) + + def invoke(self, arg, from_tty): + '''gdb command to dump EFI System Tables''' + + try: + (options, _) =3D self.create_options(arg, from_tty) + if options.help: + self.parser.print_help() + return + except ValueError: + print('bad arguments!') + return + + if options.address: + try: + value =3D gdb.parse_and_eval(options.address) + address =3D int(value) + except ValueError: + address =3D None + else: + address =3D None + + hob =3D EfiHob(self.file, + address, + options.verbose).get_hob_by_type(options.type) + print(hob) + + +class EfiTablesCmd (gdb.Command): + """Dump EFI System Tables. Type 'table -h' for more info.""" + + def __init__(self): + super(EfiTablesCmd, self).__init__("efi table",=20 + gdb.COMMAND_NONE) + + self.file =3D GdbFileObject() + + def create_options(self, arg, from_tty): + usage =3D "usage: %prog [options] [arg]" + description =3D "Dump EFI System Tables. Requires symbols to be lo= aded" + + self.parser =3D optparse.OptionParser( + description=3Ddescription, + prog=3D'efi table', + usage=3Dusage, + add_help_option=3DFalse) + + self.parser.add_option( + '-h', + '--help', + action=3D'store_true', + dest=3D'help', + help=3D'Show help for the command', + default=3DFalse) + + return self.parser.parse_args(shlex.split(arg)) + + def invoke(self, arg, from_tty): + '''gdb command to dump EFI System Tables''' + + try: + (options, _) =3D self.create_options(arg, from_tty) + if options.help: + self.parser.print_help() + return + except ValueError: + print('bad arguments!') + return + + gST =3D gdb.lookup_global_symbol('gST') + if gST is None: + print('Error: This command requires symbols for gST to be load= ed') + return + + table =3D EfiConfigurationTable( + self.file, int(gST.value(gdb.selected_frame()))) + if table: + print(table, '\n') + + +class EfiSymbolsCmd (gdb.Command): + """Load Symbols for EFI. Type 'efi symbols -h' for more info.""" + + def __init__(self): + super(EfiSymbolsCmd, self).__init__("efi symbols", + gdb.COMMAND_NONE, + gdb.COMPLETE_EXPRESSION) + self.file =3D GdbFileObject() + self.gST =3D None + self.efi_symbols =3D EfiSymbols(self.file) + + def create_options(self, arg, from_tty): + usage =3D "usage: %prog [options]" + description =3D ( + "Command that can load EFI PE/COFF and TE image symbols. " + "If you are having trouble in PEI try adding --pei. " + "Given any address search backward for the PE/COFF (or TE head= er) " + "and then parse the PE/COFF image to get debug info. " + "The address can come from the current pc, pc values in the " + "frame, or an address provided to the command" + "") + + self.parser =3D optparse.OptionParser( + description=3Ddescription, + prog=3D'efi symbols', + usage=3Dusage, + add_help_option=3DFalse) + + self.parser.add_option( + '-a', + '--address', + type=3D"str", + dest=3D'address', + help=3D'Load symbols for image that contains address', + default=3DNone) + + self.parser.add_option( + '-c', + '--clear', + action=3D'store_true', + dest=3D'clear', + help=3D'Clear the cache of loaded images', + default=3DFalse) + + self.parser.add_option( + '-f', + '--frame', + action=3D'store_true', + dest=3D'frame', + help=3D'Load symbols for current stack frame', + default=3DFalse) + + self.parser.add_option( + '-p', + '--pc', + action=3D'store_true', + dest=3D'pc', + help=3D'Load symbols for pc', + default=3DFalse) + + self.parser.add_option( + '--pei', + action=3D'store_true', + dest=3D'pei', + help=3D'Load symbols for PEI (searches every 4 bytes)', + default=3DFalse) + + self.parser.add_option( + '-e', + '--extended', + action=3D'store_true', + dest=3D'extended', + help=3D'Try to load all symbols based on config tables', + default=3DFalse) + + self.parser.add_option( + '-r', + '--range', + type=3D"long", + dest=3D'range', + help=3D'How far to search backward for start of PE/COFF Image'= , + default=3DNone) + + self.parser.add_option( + '-s', + '--stride', + type=3D"long", + dest=3D'stride', + help=3D'Boundary to search for PE/COFF header', + default=3DNone) + + self.parser.add_option( + '-t', + '--thread', + action=3D'store_true', + dest=3D'thread', + help=3D'Load symbols for the frames of all threads', + default=3DFalse) + + self.parser.add_option( + '-v', + '--verbose', + action=3D'store_true', + dest=3D'verbose', + help=3D'Show more info on symbols loading in gdb', + default=3DFalse) + + self.parser.add_option( + '-h', + '--help', + action=3D'store_true', + dest=3D'help', + help=3D'Show help for the command', + default=3DFalse) + + return self.parser.parse_args(shlex.split(arg)) + + def save_user_state(self): + self.pagination =3D gdb.parameter("pagination") + if self.pagination: + gdb.execute("set pagination off") + + self.user_selected_thread =3D gdb.selected_thread() + self.user_selected_frame =3D gdb.selected_frame() + + def restore_user_state(self): + self.user_selected_thread.switch() + self.user_selected_frame.select() + + if self.pagination: + gdb.execute("set pagination on") + + def canonical_address(self, address): + ''' + Scrub out 48-bit non canonical addresses + Raw frames in gdb can have some funky values + ''' + + # Skip lowest 256 bytes to avoid interrupt frames + if address > 0xFF and address < 0x00007FFFFFFFFFFF: + return True + if address >=3D 0xFFFF800000000000: + return True + + return False + + def pc_set_for_frames(self): + '''Return a set for the PC's in the current frame''' + pc_list =3D [] + frame =3D gdb.newest_frame() + while frame: + pc =3D int(frame.read_register('pc')) + if self.canonical_address(pc): + pc_list.append(pc) + frame =3D frame.older() + + return set(pc_list) + + def invoke(self, arg, from_tty): + '''gdb command to symbolicate all the frames from all the threads'= '' + + try: + (options, _) =3D self.create_options(arg, from_tty) + if options.help: + self.parser.print_help() + return + except ValueError: + print('bad arguments!') + return + + self.dont_repeat() + + self.save_user_state() + + if options.clear: + self.efi_symbols.clear() + return + + if options.pei: + # XIP code can be 4 byte aligned in the FV + options.stride =3D 4 + options.range =3D 0x100000 + self.efi_symbols.configure_search(options.stride, + options.range, + options.verbose) + + if options.thread: + thread_list =3D gdb.selected_inferior().threads() + else: + thread_list =3D (gdb.selected_thread(),) + + address =3D None + if options.address: + value =3D gdb.parse_and_eval(options.address) + address =3D int(value) + elif options.pc: + address =3D gdb.selected_frame().pc() + + if address: + res =3D self.efi_symbols.address_to_symbols(address) + print(res) + else: + + for thread in thread_list: + thread.switch() + + # You can not iterate over frames as you load symbols. Loa= ding + # symbols changes the frames gdb can see due to inlining a= nd + # boom. So we loop adding symbols for the current frame, a= nd + # we test to see if new frames have shown up. If new frame= s + # show up we process those new frames. Thus 1st pass is th= e + # raw frame, and other passes are only new PC values. + NewPcSet =3D self.pc_set_for_frames() + while NewPcSet: + PcSet =3D self.pc_set_for_frames() + for pc in NewPcSet: + res =3D self.efi_symbols.address_to_symbols(pc) + print(res) + + NewPcSet =3D PcSet.symmetric_difference( + self.pc_set_for_frames()) + + # find the EFI System tables the 1st time + if self.gST is None: + gST =3D gdb.lookup_global_symbol('gST') + if gST is not None: + self.gST =3D int(gST.value(gdb.selected_frame())) + table =3D EfiConfigurationTable(self.file, self.gST) + else: + table =3D None + else: + table =3D EfiConfigurationTable(self.file, self.gST) + + if options.extended and table: + # load symbols from EFI System Table entry + for address, _ in table.DebugImageInfo(): + res =3D self.efi_symbols.address_to_symbols(address) + print(res) + + # sync up the GUID database from the build output + for m in gdb.objfiles(): + if GuidNames.add_build_guid_file(str(m.filename)): + break + + self.restore_user_state() + + +class EfiCmd (gdb.Command): + """Commands for debugging EFI. efi """ + + def __init__(self): + super(EfiCmd, self).__init__("efi", + gdb.COMMAND_NONE, + gdb.COMPLETE_NONE, + True) + + def invoke(self, arg, from_tty): + '''default to loading symbols''' + if '-h' in arg or '--help' in arg: + gdb.execute('help efi') + else: + # default to loading all symbols + gdb.execute('efi symbols --extended') + + +class LoadEmulatorEfiSymbols(gdb.Breakpoint): + ''' + breakpoint for EmulatorPkg to load symbols + Note: make sure SecGdbScriptBreak is not optimized away! + Also turn off the dlopen() flow like on macOS. + ''' + def stop(self): + symbols =3D EfiSymbols() + # Emulator adds SizeOfHeaders so we need file alignment to search + symbols.configure_search(0x20) + + frame =3D gdb.newest_frame() + + try: + # gdb was looking at spill address, pre spill :( + LoadAddress =3D frame.read_register('rdx') + AddSymbolFlag =3D frame.read_register('rcx') + except gdb.error: + LoadAddress =3D frame.read_var('LoadAddress') + AddSymbolFlag =3D frame.read_var('AddSymbolFlag') + + if AddSymbolFlag =3D=3D 1: + res =3D symbols.address_to_symbols(LoadAddress) + else: + res =3D symbols.unload_symbols(LoadAddress) + print(res) + + # keep running + return False + + +# Get python backtraces to debug errors in this script gdb.execute("set=20 +python print-stack full") + +# tell efi_debugging how to walk data structures with pointers +try: + pointer_width =3D gdb.lookup_type('int').pointer().sizeof +except ValueError: + pointer_width =3D 8 +patch_ctypes(pointer_width) + +register_pretty_printer(None, build_pretty_printer(), replace=3DTrue) + +# gdb commands that we are adding +# add `efi` prefix gdb command +EfiCmd() + +# subcommands for `efi` +EfiSymbolsCmd() +EfiTablesCmd() +EfiHobCmd() +EfiDevicePathCmd() +EfiGuidCmd() + +# +bp =3D LoadEmulatorEfiSymbols('SecGdbScriptBreak', internal=3DTrue) if=20 +bp.pending: + try: + gdb.selected_frame() + # Not the emulator so do this when you attach + gdb.execute('efi symbols --frame --extended', True) + gdb.execute('bt') + # If you want to skip the above commands comment them out + pass + except gdb.error: + # If you load the script and there is no target ignore the error. + pass +else: + # start the emulator + gdb.execute('run') -- 2.34.1