From patchwork Mon Oct 11 08:45:09 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Jose E. Marchesi" X-Patchwork-Id: 46061 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id F3826385841F for ; Mon, 11 Oct 2021 08:45:33 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org F3826385841F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1633941934; bh=zL+Do9fu9FIGQ+n7+kO+3ksHqllmhIGV1CWsDv9+7n8=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Help: List-Subscribe:From:Reply-To:From; b=WXEPLWLWOMvhXDHJXn/1vBm7D7zp9lhnaC0bAOa8/rujRR15s/u8e9oMJ+AUIrjUw lgxanDtwKoVVie1ROHpi6oEsUaQh1FgeKM95Agj2uMGUxyKFOpyOrpiVGiu50a7weT pK4E80VW+E9DN6yfp8BKXWy8K6QzukXAmX/ULz7E= X-Original-To: libabigail@sourceware.org Delivered-To: libabigail@sourceware.org Received: from mx0a-00069f02.pphosted.com (mx0a-00069f02.pphosted.com [205.220.165.32]) by sourceware.org (Postfix) with ESMTPS id DEB203858D28 for ; Mon, 11 Oct 2021 08:45:24 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org DEB203858D28 Received: from pps.filterd (m0246627.ppops.net [127.0.0.1]) by mx0b-00069f02.pphosted.com (8.16.1.2/8.16.1.2) with SMTP id 19B8iqJp017564 for ; Mon, 11 Oct 2021 08:45:23 GMT Received: from userp3030.oracle.com (userp3030.oracle.com [156.151.31.80]) by mx0b-00069f02.pphosted.com with ESMTP id 3bkvh9u0td-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK) for ; Mon, 11 Oct 2021 08:45:23 +0000 Received: from pps.filterd (userp3030.oracle.com [127.0.0.1]) by userp3030.oracle.com (8.16.1.2/8.16.1.2) with SMTP id 19B8V1qE112243 for ; Mon, 11 Oct 2021 08:45:20 GMT Received: from nam02-sn1-obe.outbound.protection.outlook.com (mail-sn1anam02lp2046.outbound.protection.outlook.com [104.47.57.46]) by userp3030.oracle.com with ESMTP id 3bkyv6pdh6-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK) for ; Mon, 11 Oct 2021 08:45:19 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=kQtAYRnf6Qs5tVWAENV0hFCjWnb53PJRjHAfQvYcULbJyrM1lzWkjcowi8bhE1IEqr+VgcUwz3oLCpl+MOPttKXG8H1R8Jd48SR5QYjAMw4USqkn5tqiv/NofLHcUI5M7afLm41Nzi0ubbGZmAr01Kie86S4EF7z1H9SLuYMBOUvxE2toPWLAwS0Sfdcgphx0R2SOKn1oOYxjliEVoBxUMO68Z6nvXLsbpZEYcRxo8yuZf4hHhtcAGh+NWRS2ixlsspK9Go1uI2tlpBnfNiIlZPd+cWpdA0xATt57B0zo1l3DrZWoYt1eWfWPQkcHRzyrWZDjuvGVo7Mj3CMdYY7Cw== 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=zL+Do9fu9FIGQ+n7+kO+3ksHqllmhIGV1CWsDv9+7n8=; b=H1Teo6mlyKBcibcxjuRb59Dzufguu4ttq3H94t6CdQxVEEkJkeWLGk9Aaw6W/Y6rVsFNB4k6kzD809UaN3YaQ/NJOR04wdXduHARHTIBT90Jx+IiLoy2fIrOmNrWdNgst/M++9v6v9Lbn9evIvLINxyIgx3yZ2KSqPTWITWz90QknH0xsV5uFjZ6bqIv9utkfBOgMRgWBkQOCw/AHpVGhXvpH2/jzvc91WCwNf9pBV8QJcHsdXIfNpkRugRO2RRaR8Tt6XpncdyeYWlxLZnyz5qz8mTBDjJjP/95PfHmMH7Fe9eri03IB8v3Bz6vsOrBDbo15xyD2GHGkYFHb/Ig9A== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=oracle.com; dmarc=pass action=none header.from=oracle.com; dkim=pass header.d=oracle.com; arc=none Received: from BYAPR10MB2888.namprd10.prod.outlook.com (2603:10b6:a03:88::32) by SJ0PR10MB4622.namprd10.prod.outlook.com (2603:10b6:a03:2d6::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4587.18; Mon, 11 Oct 2021 08:45:17 +0000 Received: from BYAPR10MB2888.namprd10.prod.outlook.com ([fe80::48bf:86b4:32e1:6574]) by BYAPR10MB2888.namprd10.prod.outlook.com ([fe80::48bf:86b4:32e1:6574%4]) with mapi id 15.20.4587.026; Mon, 11 Oct 2021 08:45:17 +0000 To: libabigail@sourceware.org Subject: [PATCH] Add support for the CTF debug format to libabigail. Date: Mon, 11 Oct 2021 10:45:09 +0200 Message-Id: <20211011084509.9044-1-jose.marchesi@oracle.com> X-Mailer: git-send-email 2.11.0 X-ClientProxiedBy: AS8P250CA0012.EURP250.PROD.OUTLOOK.COM (2603:10a6:20b:330::17) To BYAPR10MB2888.namprd10.prod.outlook.com (2603:10b6:a03:88::32) MIME-Version: 1.0 Received: from termi.localdomain (2a01:c22:cd17:b900:225:d3ff:fe40:d58d) by AS8P250CA0012.EURP250.PROD.OUTLOOK.COM (2603:10a6:20b:330::17) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4587.25 via Frontend Transport; Mon, 11 Oct 2021 08:45:16 +0000 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: a70d62aa-74fd-492c-1a42-08d98c937325 X-MS-TrafficTypeDiagnostic: SJ0PR10MB4622: X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:751; X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: fJcX3WiH8EosTGwVrAnlAPDxldNrsqTBTVXSQEfANleM+HrARfvCV6gtYyNl3XvxxlUTZsQ93r5jMvJBhqMki0XWbmZpZgfDb7m6sfXseXYGZQRa+aNY/aI74c1FVZNxCCe7m42tKMfyRFGbhglYcrWPUuSvmVcmnpHTMt5OW+Sy62Nlp5Ri9yj5g8B17caJNqFTVtDneg8beUIFv//Dh/pz84s4uJdjKDHn12sR1IxM7TQJvp2FDZ6r0qi8wrhz49kZMMbBAiEzOKjD+OrMRetvXEKM/A269xEKKxBMzTCt0XTewhwKM1WCZyTcel0FqDUG6VBPQaF6xvQs/oQbQmPX9VOzsLC4JtND82Y8yfFSkTXaau6W8GZwesmgPdsDas25ZlYll/QG72+WsgH2R7TFJoRY0nh/Hsv20bbtM95YBo+jnj9606NIto2cME4Z3x7p0xAlD/GESZiSOu0D8ffRI0WImrlY2pqfMxg/OPUzuldXnJmmxvH+o1VPQmSgKaKkNNzHFwqUGjnBdUwqULbAZc2VJsuseMYfHscAOHpr4rxSVIKsCL/Gl88FMWBXphdyJ3KHSMXlJ+Zxs6f9xNbGUZH1wjpxdo1oc32meQxJWhIElQyLOroW9zlxgfbNIn1zXZvKggsSKELlONn+ow== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:BYAPR10MB2888.namprd10.prod.outlook.com; PTR:; CAT:NONE; SFS:(366004)(66476007)(2906002)(316002)(66556008)(52116002)(6512007)(6916009)(508600001)(30864003)(2616005)(38100700002)(36756003)(186003)(66946007)(6506007)(4001150100001)(8936002)(6486002)(8676002)(1076003)(6666004)(83380400001)(5660300002)(86362001)(559001)(579004); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: J8+xIIgaeMfqq5fysDvxpjSKESiZ5rG2Lv2oV/bl5pEXsvHdJn11bwiojFfyQHfpBzi+ySyJ5LPDzTIrJKIwYLiJmlNufp/s+DSRoqc7ozHa1rSsj+kL96DYX8bz/Ci5nuUUNJJ0u1FQdurP6HhuI+a3YFnGncXF6hRHrKO1ky5+vmVSO4AGX16ndgzL+NT9aFdNm4R4zprOrzY8hBiK6AAxpfexKUKYwxKisImnzwP0Pu8s7WTurixssGsgLYvVWrOCzYZANq8hFV8Kclui1E4o14Dbrew+ZttwmgeU2g8cgJHuf+e0K1hhxqISdf2VDap68KHEU7yc0Rqos/G0N0isQKJBsABVfmEMW8i9Dho4FUSea/8h1AnYqOIoDgU/vyFKmKRQ7wM9bKmVe7OUTEjlLMlNJN0jK3uvRQdeOXwzy3chCyLfnrMey4KhHAg3dfU2EqAEgF3pExdmNKnrJqAwOlFz8OVUuirZxGqZoyyHC5Ided43WENdfIHNvYRum2zC2bdCzaORc5V5lmGmTyXQ4/JPQZ+3wRRKI44jGCeEZ0OMFopbHTy7qcoqOFuiAXcYda8jT5WkoTTED71NQg533JSL1Lk8zctpaEL48GYQTZPsDns9391LGKHz+FxASXzFmfsHvB0e8UYpaoLR5XwGZv2ddL2pobK2uQWRIfaOfOiHJxKxhXz8yhD9+gMdtTlTwh8kZgN0qMvIgxdEXzx3hGQKW2A5pcRyaaW8XzgTutbgw5NGQA9rAdAJ1sRVnSoEu2K2OgTGEx8hbvnpWP36HyetJjA0mscTsHt8A70e0m5+3oRq5TCp0SYoK4mjpgT56t8RgGEO/dJ/iUmJQyucUpHmBpPfO2G15PKOsBBOZ0xNAsnbaCQ6hm/xO9ipQRWdksq4Pv4EJsDmogjaq/Hdevz/CdIyf63j9wqXHlFyGfzvyuEjB9YznsH6v8H0oFi/IqH/22/VYtqMX1twFhU+THxCtfADpSNPyutuCKsxlpTp8U6XotUtqkht8h6sYUJqdKaNmacweHeQ+jW+nW4D/3x5+cuaQEgr8jDK4E90PWaNWG5aOY1SMYPyDcCtkH0gVjLtvPlCqPJJcGpacyJe4GSNtedseyw5gVgwRykGuAyElxCHzj/rkz6PYG6sN9XDWwJT2geMloK8Q3veWLEFY/Ax8XSxgkAR+9hH8+8qSkJyqb+ephYPIHHrbD56SFk5/P0XDlMRpJ6X5O8k8A59uKf2Xm117ciNKiRmlYZz1c+pXvPj9XKxDUfedjNW2M1HCrgXqMYVr3Y3RMWQFOiMcr/ucDwxCkhd9ronbDmQsg45Lzb057VBqTcC1/QEtfKl2XtGNhUG6kVAkZkK6Wr8nqKNdV87CY3RJuELFsb7G1lvSKirzafRfCBPBiRY X-OriginatorOrg: oracle.com X-MS-Exchange-CrossTenant-Network-Message-Id: a70d62aa-74fd-492c-1a42-08d98c937325 X-MS-Exchange-CrossTenant-AuthSource: BYAPR10MB2888.namprd10.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 11 Oct 2021 08:45:17.5464 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 4e2c6054-71cb-48f1-bd6c-3a9705aca71b X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: 28e91hNBcv5S4qrSQBuzWbDjXV7jAC9v3PTUszdh5uWYU2mlCFRnsJA5fMNMYBukt7GDYGkcF2KPDYNQXoDBuZp/z4fEPtTiOGz0+JdRkfM= X-MS-Exchange-Transport-CrossTenantHeadersStamped: SJ0PR10MB4622 X-Proofpoint-Virus-Version: vendor=nai engine=6300 definitions=10133 signatures=668683 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 phishscore=0 bulkscore=0 malwarescore=0 adultscore=0 mlxscore=0 spamscore=0 mlxlogscore=999 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2109230001 definitions=main-2110110049 X-Proofpoint-ORIG-GUID: FTelDyJ_QKkGGXKmHT-_E51r-gHd1WxJ X-Proofpoint-GUID: FTelDyJ_QKkGGXKmHT-_E51r-gHd1WxJ X-Spam-Status: No, score=-12.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, MSGID_FROM_MTA_HEADER, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: libabigail@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Mailing list of the Libabigail project List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-Patchwork-Original-From: "Jose E. Marchesi via Libabigail" From: "Jose E. Marchesi" Reply-To: "Jose E. Marchesi" Errors-To: libabigail-bounces+patchwork=sourceware.org@sourceware.org Sender: "Libabigail" CTF (C Type Format) is a lightwieght debugging format that provices information about C types and the association between functions and data symbols and types. It is designed to be very compact and simple. This patch introduces support in libabigail to extract ABI information from CTF stored in ELF files. A few notes on this implementation: - The implementation is complete in terms of CTF support. Every CTF feature is processed and handled to generate libabigail IR. This includes basic types, typedefs, pointer, array and struct types. The CTF record of data objects (variables) and functions are also used in order to generate the corresponding libabigail IR artifacts. - The decoding of CTF data is done using the libctf library which is part of binutils. In order to link with it, binutils shall be built with --enable-shared for libctf.so to become available. - This initial implementation is aimed to simplicity. We have not tried to resolve any and every corner case that may require special handling. We have observed that the DWARF front-end (which is naturally way more complex as the scope is way bigger) is plagued with hacks to handle such situations. However, for the CTF support we prefer to proceed in a simpler and more modest way: we will handle these problems if/when we find them. The fact that CTF only supports C (currently) certainly helps there. - Likewise, in this basic support we are not handling symbol suppressions or other goodies that libabigail provides. We are new to libabigail and ABI analysis, and at this point we simply don't have a clear picture about what is most useful/relevant to support or not. With the maintainer's blesssing, we will tackle that functionaly after this basic support is applied upstream. - The implementation in abg-ctf-reader.{cc,h} is pretty much self-contained. As a result there is some duplication in terms of ELF handling with the DWARF reader, but since that logic is very simple and can be easily implemented, we don't consider this to be a big deal (for now.) Hopefully the maintainers agree. - The libabigail tools assume that ELF means to always use DWARF to generate the ABI IR. We added a new command-line option --ctf to the tools in order to make them to use the CTF debug info instead. We are definitely not sure whether this is the best user interface. In fact I would be suprised if it was ;) - We added support for --ctf to both abilint and abidiff. We are not sure whether it would make sense to add support for CTF to the other tools. Feedback welcome. - We are pondering about what to do in terms of testing. We have cursory tested this implementation using abilint and abidiff. We know we are generating IR corpus that seem to be ok. It would be good however to be able to run the libabigail testsuites using CTF. However the testsuites may need some non-trivial changes in order to make this possible. Let's talk about that :) Salud! Signed-off-by: Dodji Seketeli --- ChangeLog | 18 + configure.ac | 32 +- include/Makefile.am | 4 + include/abg-corpus.h | 1 + include/abg-ctf-reader.h | 34 ++ src/Makefile.am | 4 + src/abg-ctf-reader.cc | 999 +++++++++++++++++++++++++++++++++++++++++++++++ tools/abidiff.cc | 99 +++-- tools/abilint.cc | 37 +- 9 files changed, 1183 insertions(+), 45 deletions(-) create mode 100644 include/abg-ctf-reader.h create mode 100644 src/abg-ctf-reader.cc diff --git a/ChangeLog b/ChangeLog index 30918b49..385fe067 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2021-10-11 Jose E. Marchesi + + * configure.ac: Check for libctf. + * src/abg-ctf-reader.cc: New file. + * include/abg-ctf-reader.h: Likewise. + * src/Makefile.am (libabigail_la_SOURCES): Add abg-ctf-reader.cc + conditionally. + * include/Makefile.am (pkginclude_HEADERS): Add abg-ctf-reader.h + conditionally. + * tools/abilint.cc (struct options): New option `use_ctf'. + (display_usage): Documentation for --ctf. + (parse_command_line): Handle --ctf. + (main): Honour --ctf. + * tools/abidiff.cc (struct options): New option `use_ctf'. + (display_usage): Documentation for --ctf. + (parse_command_line): Handle --ctf. + (main): Honour --ctf. + 2021-10-04 Dodji Seketeli Update NEWS file for 2.0 diff --git a/configure.ac b/configure.ac index 9e91f496..1eb85008 100644 --- a/configure.ac +++ b/configure.ac @@ -138,6 +138,13 @@ AC_ARG_ENABLE(ubsan, ENABLE_UBSAN=$enableval, ENABLE_UBSAN=no) +dnl check if user has enabled CTF code +AC_ARG_ENABLE(ctf, + AS_HELP_STRING([--enable-ctf=yes|no], + [disable support of ctf files)]), + ENABLE_CTF=$enableval, + ENABLE_CTF=no) + dnl ************************************************* dnl check for dependencies dnl ************************************************* @@ -244,6 +251,24 @@ fi AC_SUBST(DW_LIBS) AC_SUBST([ELF_LIBS]) +dnl check for libctf presence if CTF code has been enabled by command line +dnl argument, and then define CTF flag (to build CTF file code) if libctf is +dnl found on the system +CTF_LIBS= +if test x$ENABLE_CTF = xyes; then + LIBCTF= + AC_CHECK_LIB(ctf, ctf_open, [LIBCTF=yes], [LIBCTF=no]) + if test x$LIBCTF = xyes; then + AC_MSG_NOTICE([activating CTF code]) + AC_DEFINE([CTF], 1, + [Defined if user enables and system has the libctf library]) + CTF_LIBS=-lctf + else + AC_MSG_NOTICE([CTF enabled but no libctf found]) + ENABLE_CTF=no + fi +fi + dnl Check for dependency: libxml LIBXML2_VERSION=2.6.22 PKG_CHECK_MODULES(XML, libxml-2.0 >= $LIBXML2_VERSION) @@ -593,7 +618,7 @@ AX_VALGRIND_CHECK dnl Set the list of libraries libabigail depends on -DEPS_LIBS="$XML_LIBS $ELF_LIBS $DW_LIBS" +DEPS_LIBS="$XML_LIBS $ELF_LIBS $DW_LIBS $CTF_LIBS" AC_SUBST(DEPS_LIBS) if test x$ABIGAIL_DEVEL != x; then @@ -631,6 +656,10 @@ if test x$ENABLE_UBSAN = xyes; then CXXFLAGS="$CXXFLAGS -fsanitize=undefined" fi +dnl Set a few Automake conditionals + +AM_CONDITIONAL([CTF_READER],[test "x$ENABLE_CTF" = "xyes"]) + dnl Set the level of C++ standard we use. CXXFLAGS="$CXXFLAGS -std=$CXX_STANDARD" @@ -936,6 +965,7 @@ AC_MSG_NOTICE([ Enable bash completion : ${ENABLE_BASH_COMPLETION} Enable fedabipkgdiff : ${ENABLE_FEDABIPKGDIFF} Enable python 3 : ${ENABLE_PYTHON3} + Enable CTF front-end : ${ENABLE_CTF} Enable running tests under Valgrind : ${enable_valgrind} Enable build with -fsanitize=address : ${ENABLE_ASAN} Enable build with -fsanitize=memory : ${ENABLE_MSAN} diff --git a/include/Makefile.am b/include/Makefile.am index 0f3b0936..9e5e037b 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -27,4 +27,8 @@ abg-viz-dot.h \ abg-viz-svg.h \ abg-regex.h +if CTF_READER +pkginclude_HEADERS += abg-ctf-reader.h +endif + EXTRA_DIST = abg-version.h.in diff --git a/include/abg-corpus.h b/include/abg-corpus.h index 136c348c..652a8294 100644 --- a/include/abg-corpus.h +++ b/include/abg-corpus.h @@ -46,6 +46,7 @@ public: ARTIFICIAL_ORIGIN = 0, NATIVE_XML_ORIGIN, DWARF_ORIGIN, + CTF_ORIGIN, LINUX_KERNEL_BINARY_ORIGIN }; diff --git a/include/abg-ctf-reader.h b/include/abg-ctf-reader.h new file mode 100644 index 00000000..07eccec6 --- /dev/null +++ b/include/abg-ctf-reader.h @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// -*- Mode: C++ -*- +// +// Copyright (C) 2021 Oracle, Inc. +// +// Author: Jose E. Marchesi + +/// @file +/// +/// This file contains the declarations of the entry points to +/// de-serialize an instance of @ref abigail::corpus from a file in +/// elf format, containing CTF information. + +#ifndef __ABG_CTF_READER_H__ +#define __ABG_CTF_READER_H__ + +#include +#include "abg-corpus.h" +#include "abg-suppression.h" + +namespace abigail +{ +namespace ctf_reader +{ + +class read_context; +read_context *create_read_context (std::string elf_path, + ir::environment *env); +corpus_sptr read_corpus (read_context *ctxt); + +} // end namespace ctf_reader +} // end namespace abigail + +#endif // ! __ABG_CTF_READER_H__ diff --git a/src/Makefile.am b/src/Makefile.am index 430ce98d..b60d74cb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -41,6 +41,10 @@ abg-symtab-reader.h \ abg-symtab-reader.cc \ $(VIZ_SOURCES) +if CTF_READER +libabigail_la_SOURCES += abg-ctf-reader.cc +endif + libabigail_la_LIBADD = $(DEPS_LIBS) libabigail_la_LDFLAGS = -lpthread -Wl,--as-needed -no-undefined diff --git a/src/abg-ctf-reader.cc b/src/abg-ctf-reader.cc new file mode 100644 index 00000000..a121b3c8 --- /dev/null +++ b/src/abg-ctf-reader.cc @@ -0,0 +1,999 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// -*- Mode: C++ -*- +// +// Copyright (C) 2021 Oracle, Inc. +// +// Author: Jose E. Marchesi + +/// @file +/// +/// This file contains the definitions of the entry points to +/// de-serialize an instance of @ref abigail::corpus from a file in +/// ELF format, containing CTF information. + +#include "config.h" + +#include /* For open(3) */ +#include + +#include "ctf-api.h" + +#include "abg-internal.h" +#include "abg-ir-priv.h" +#include "abg-elf-helpers.h" + +// +ABG_BEGIN_EXPORT_DECLARATIONS + +#include "abg-ctf-reader.h" +#include "abg-libxml-utils.h" +#include "abg-reader.h" +#include "abg-corpus.h" +#include "abg-symtab-reader.h" +#include "abg-tools-utils.h" + +ABG_END_EXPORT_DECLARATIONS +// + +namespace abigail +{ +namespace ctf_reader +{ + +class read_context +{ +public: + /// The name of the ELF file from which the CTF archive got + /// extracted. + string filename; + + /// The IR environment. + ir::environment *ir_env; + + /// The CTF archive read from FILENAME. If an archive couldn't + /// be read from the file then this is NULL. + ctf_archive_t *ctfa; + + /// A map associating CTF type ids with libabigail IR types. This + /// is used to reuse already generated types. + unordered_map types_map; + + /// Associate a given CTF type ID with a given libabigail IR type. + void add_type (ctf_id_t ctf_type, type_base_sptr type) + { + types_map.insert (std::make_pair (ctf_type, type)); + } + + /// Lookup a given CTF type ID in the types map. + /// + /// @param ctf_type the type ID of the type to lookup. + type_base_sptr lookup_type (ctf_id_t ctf_type) + { + type_base_sptr result; + + auto search = types_map.find (ctf_type); + if (search != types_map.end()) + result = search->second; + + return result; + } + + /// Constructor. + /// + /// @param elf_path the path to the ELF file. + read_context (string elf_path, ir::environment *env) + { + int err; + + types_map.clear (); + filename = elf_path; + ir_env = env; + ctfa = ctf_open (filename.c_str(), + NULL /* BFD target */, &err); + + if (ctfa == NULL) + fprintf (stderr, "cannot open %s: %s\n", filename.c_str(), ctf_errmsg (err)); + } + + /// Destructor of the @ref read_context type. + ~read_context () + { + ctf_close (ctfa); + } +}; // end class read_context. + +/// Forward reference, needed because several of the process_ctf_* +/// functions below are indirectly recursive through this call. +static type_base_sptr process_ctf_type (read_context *ctxt, corpus_sptr corp, + translation_unit_sptr tunit, + ctf_dict_t *ctf_dictionary, + ctf_id_t ctf_type); + +/// Build and return a typedef libabigail IR. +/// +/// @param ctxt the read context. +/// @param corp the libabigail IR corpus being constructed. +/// @param tunit the current IR translation unit. +/// @param ctf_dictionary the CTF dictionary being read. +/// @param ctf_type the CTF type ID of the source type. +/// +/// @return a shared pointer to the IR node for the typedef. + +static typedef_decl_sptr +process_ctf_typedef (read_context *ctxt, + corpus_sptr corp, + translation_unit_sptr tunit, + ctf_dict_t *ctf_dictionary, + ctf_id_t ctf_type) +{ + typedef_decl_sptr result; + + ctf_id_t ctf_utype = ctf_type_reference (ctf_dictionary, ctf_type); + const char *typedef_name = ctf_type_name_raw (ctf_dictionary, ctf_type); + type_base_sptr utype = ctxt->lookup_type (ctf_utype); + + if (!utype) + { + utype = process_ctf_type (ctxt, corp, tunit, ctf_dictionary, ctf_utype); + if (!utype) + return result; + } + + result.reset (new typedef_decl (typedef_name, utype, location (), + typedef_name /* mangled_name */)); + return result; +} + +/// Build and return an integer or float type declaration libabigail +/// IR. +/// +/// @param ctxt the read context. +/// @param corp the libabigail IR corpus being constructed. +/// @param ctf_dictionary the CTF dictionary being read. +/// @param ctf_type the CTF type ID of the source type. +/// +/// @return a shared pointer to the IR node for the type. + +static type_decl_sptr +process_ctf_base_type (read_context *ctxt, + corpus_sptr corp, + ctf_dict_t *ctf_dictionary, + ctf_id_t ctf_type) +{ + type_decl_sptr result; + + ssize_t type_alignment = ctf_type_align (ctf_dictionary, ctf_type); + const char *type_name = ctf_type_name_raw (ctf_dictionary, ctf_type); + + /* Get the type encoding and extract some useful properties of + the type from it. In case of any error, just ignore the + type. */ + ctf_encoding_t type_encoding; + if (ctf_type_encoding (ctf_dictionary, + ctf_type, + &type_encoding)) + return result; + + /* Create the IR type corresponding to the CTF type. */ + if (type_encoding.cte_bits == 0 + && type_encoding.cte_format == CTF_INT_SIGNED) + { + /* This is the `void' type. */ + type_base_sptr void_type = ctxt->ir_env->get_void_type (); + decl_base_sptr type_declaration = get_type_declaration (void_type); + result = is_type_decl (type_declaration); + } + else + { + result = lookup_basic_type (type_name, *corp); + if (!result) + result.reset (new type_decl (ctxt->ir_env, + type_name, + type_encoding.cte_bits, + type_alignment * 8 /* in bits */, + location (), + type_name /* mangled_name */)); + + } + + return result; +} + +/// Build and return a function type libabigail IR. +/// +/// @param ctxt the read context. +/// @param corp the libabigail IR corpus being constructed. +/// @param tunit the current IR translation unit. +/// @param ctf_dictionary the CTF dictionary being read. +/// @param ctf_type the CTF type ID of the source type. +/// +/// @return a shared pointer to the IR node for the function type. + +static function_type_sptr +process_ctf_function_type (read_context *ctxt, + corpus_sptr corp, + translation_unit_sptr tunit, + ctf_dict_t *ctf_dictionary, + ctf_id_t ctf_type) +{ + function_type_sptr result; + + /* Fetch the function type info from the CTF type. */ + ctf_funcinfo_t funcinfo; + ctf_func_type_info (ctf_dictionary, ctf_type, &funcinfo); + int vararg_p = funcinfo.ctc_flags & CTF_FUNC_VARARG; + + /* Take care first of the result type. */ + ctf_id_t ctf_ret_type = funcinfo.ctc_return; + type_base_sptr ret_type = ctxt->lookup_type (ctf_ret_type); + + if (!ret_type) + { + ret_type = process_ctf_type (ctxt, corp, tunit, ctf_dictionary, + ctf_ret_type); + if (!ret_type) + return result; + } + + /* Now process the argument types. */ + int argc = funcinfo.ctc_argc; + std::vector argv (argc); + if (ctf_func_type_args (ctf_dictionary, ctf_type, + argc, argv.data ()) == CTF_ERR) + return result; + + function_decl::parameters function_parms; + for (int i = 0; i < argc; i++) + { + ctf_id_t ctf_arg_type = argv[i]; + type_base_sptr arg_type = ctxt->lookup_type (ctf_arg_type); + + if (!arg_type) + { + arg_type = process_ctf_type (ctxt, corp, tunit, ctf_dictionary, + ctf_arg_type); + if (!arg_type) + return result; + } + + function_decl::parameter_sptr parm + (new function_decl::parameter (arg_type, "", + location (), + vararg_p && (i == argc - 1), + false /* is_artificial */)); + function_parms.push_back (parm); + } + + + /* Ok now the function type itself. */ + result.reset (new function_type (ret_type, + function_parms, + tunit->get_address_size (), + ctf_type_align (ctf_dictionary, ctf_type))); + + tunit->bind_function_type_life_time (result); + result->set_is_artificial (true); + return result; +} + +static void +process_ctf_sou_members (read_context *ctxt, + corpus_sptr corp, + translation_unit_sptr tunit, + ctf_dict_t *ctf_dictionary, + ctf_id_t ctf_type, + class_or_union_sptr sou) +{ + ssize_t member_size; + ctf_next_t *member_next = NULL; + const char *member_name = NULL; + ctf_id_t member_ctf_type; + + while ((member_size = ctf_member_next (ctf_dictionary, ctf_type, + &member_next, &member_name, + &member_ctf_type, + CTF_MN_RECURSE)) >= 0) + { + ctf_membinfo_t membinfo; + + if (ctf_member_info (ctf_dictionary, + ctf_type, + member_name, + &membinfo) == CTF_ERR) + return; + + /* Build the IR for the member's type. */ + type_base_sptr member_type = ctxt->lookup_type (member_ctf_type); + if (!member_type) + { + member_type = process_ctf_type (ctxt, corp, tunit, ctf_dictionary, + member_ctf_type); + if (!member_type) + /* Ignore this member. */ + continue; + } + + /* Create a declaration IR node for the member and add it to the + struct type. */ + var_decl_sptr data_member_decl (new var_decl (member_name, + member_type, + location (), + member_name)); + sou->add_data_member (data_member_decl, + public_access, + true /* is_laid_out */, + false /* is_static */, + membinfo.ctm_offset); + } + if (ctf_errno (ctf_dictionary) != ECTF_NEXT_END) + fprintf (stderr, "ERROR from ctf_member_next\n"); +} + +/// Build and return a struct type libabigail IR. +/// +/// @param ctxt the read context. +/// @param corp the libabigail IR corpus being constructed. +/// @param tunit the current IR translation unit. +/// @param ctf_dictionary the CTF dictionary being read. +/// @param ctf_type the CTF type ID of the source type. +/// +/// @return a shared pointer to the IR node for the struct type. + +static class_decl_sptr +process_ctf_struct_type (read_context *ctxt, + corpus_sptr corp, + translation_unit_sptr tunit, + ctf_dict_t *ctf_dictionary, + ctf_id_t ctf_type) +{ + class_decl_sptr result; + std::string struct_type_name = ctf_type_name_raw (ctf_dictionary, + ctf_type); + bool struct_type_is_anonymous = (struct_type_name == ""); + + /* The libabigail IR encodes C struct types in `class' IR nodes. */ + result.reset (new class_decl (ctxt->ir_env, + struct_type_name, + ctf_type_size (ctf_dictionary, ctf_type) * 8, + ctf_type_align (ctf_dictionary, ctf_type) * 8, + true /* is_struct */, + location (), + decl_base::VISIBILITY_DEFAULT, + struct_type_is_anonymous)); + if (!result) + return result; + + /* The C type system indirectly supports loops by the mean of + pointers to structs or unions. Since some contained type can + refer to this struct, we have to make it available in the cache + at this point even if the members haven't been added to the IR + node yet. */ + ctxt->add_type (ctf_type, result); + + /* Now add the struct members as specified in the CTF type description. + This is C, so named types can only be defined in the global + scope. */ + process_ctf_sou_members (ctxt, corp, tunit, ctf_dictionary, ctf_type, + result); + + return result; +} + +/// Build and return an union type libabigail IR. +/// +/// @param ctxt the read context. +/// @param corp the libabigail IR corpus being constructed. +/// @param tunit the current IR translation unit. +/// @param ctf_dictionary the CTF dictionary being read. +/// @param ctf_type the CTF type ID of the source type. +/// +/// @return a shared pointer to the IR node for the union type. + +static union_decl_sptr +process_ctf_union_type (read_context *ctxt, + corpus_sptr corp, + translation_unit_sptr tunit, + ctf_dict_t *ctf_dictionary, + ctf_id_t ctf_type) +{ + union_decl_sptr result; + std::string union_type_name = ctf_type_name_raw (ctf_dictionary, + ctf_type); + bool union_type_is_anonymous = (union_type_name == ""); + + /* Create the corresponding libabigail union IR node. */ + result.reset (new union_decl (ctxt->ir_env, + union_type_name, + ctf_type_size (ctf_dictionary, ctf_type) * 8, + location (), + decl_base::VISIBILITY_DEFAULT, + union_type_is_anonymous)); + if (!result) + return result; + + /* The C type system indirectly supports loops by the mean of + pointers to structs or unions. Since some contained type can + refer to this union, we have to make it available in the cache + at this point even if the members haven't been added to the IR + node yet. */ + ctxt->add_type (ctf_type, result); + + /* Now add the union members as specified in the CTF type description. + This is C, so named types can only be defined in the global + scope. */ + process_ctf_sou_members (ctxt, corp, tunit, ctf_dictionary, ctf_type, + result); + + return result; +} + +/// Build and return an array type libabigail IR. +/// +/// @param ctxt the read context. +/// @param corp the libabigail IR corpus being constructed. +/// @param tunit the current IR translation unit. +/// @param ctf_dictionary the CTF dictionary being read. +/// @param ctf_type the CTF type ID of the source type. +/// +/// @return a shared pointer to the IR node for the array type. + +static array_type_def_sptr +process_ctf_array_type (read_context *ctxt, + corpus_sptr corp, + translation_unit_sptr tunit, + ctf_dict_t *ctf_dictionary, + ctf_id_t ctf_type) +{ + array_type_def_sptr result; + ctf_arinfo_t ctf_ainfo; + + /* First, get the information about the CTF array. */ + if (ctf_array_info (ctf_dictionary, ctf_type, &ctf_ainfo) + == CTF_ERR) + return result; + + ctf_id_t ctf_element_type = ctf_ainfo.ctr_contents; + ctf_id_t ctf_index_type = ctf_ainfo.ctr_index; + uint64_t nelems = ctf_ainfo.ctr_nelems; + + /* Make sure the element type is generated. */ + type_base_sptr element_type = ctxt->lookup_type (ctf_element_type); + if (!element_type) + { + element_type = process_ctf_type (ctxt, corp, tunit, ctf_dictionary, ctf_element_type); + if (!element_type) + return result; + } + + /* Ditto for the index type. */ + type_base_sptr index_type = ctxt->lookup_type (ctf_index_type); + if (!index_type) + { + index_type = process_ctf_type (ctxt, corp, tunit, ctf_dictionary, ctf_index_type); + if (!index_type) + return result; + } + + /* The number of elements of the array determines the IR subranges + type to build. */ + array_type_def::subranges_type subranges; + array_type_def::subrange_sptr subrange; + array_type_def::subrange_type::bound_value lower_bound; + array_type_def::subrange_type::bound_value upper_bound; + + lower_bound.set_unsigned (0); /* CTF supports C only. */ + upper_bound.set_unsigned (nelems > 0 ? nelems - 1 : 0U); + + subrange.reset (new array_type_def::subrange_type (ctxt->ir_env, + "", + lower_bound, + upper_bound, + index_type, + location (), + translation_unit::LANG_C)); + if (!subrange) + return result; + + add_decl_to_scope (subrange, tunit->get_global_scope()); + canonicalize (subrange); + subranges.push_back (subrange); + + /* Finally build the IR for the array type and return it. */ + result.reset (new array_type_def (element_type, subranges, location ())); + return result; +} + +/// Build and return a qualified type libabigail IR. +/// +/// @param ctxt the read context. +/// @param corp the libabigail IR corpus being constructed. +/// @param tunit the current IR translation unit. +/// @param ctf_dictionary the CTF dictionary being read. +/// @param ctf_type the CTF type ID of the source type. + +static type_base_sptr +process_ctf_qualified_type (read_context *ctxt, + corpus_sptr corp, + translation_unit_sptr tunit, + ctf_dict_t *ctf_dictionary, + ctf_id_t ctf_type) +{ + type_base_sptr result; + int type_kind = ctf_type_kind (ctf_dictionary, ctf_type); + ctf_id_t ctf_utype = ctf_type_reference (ctf_dictionary, ctf_type); + type_base_sptr utype = ctxt->lookup_type (ctf_utype); + + if (!utype) + { + utype = process_ctf_type (ctxt, corp, tunit, ctf_dictionary, ctf_utype); + if (!utype) + return result; + } + + qualified_type_def::CV qualifiers = qualified_type_def::CV_NONE; + if (type_kind == CTF_K_CONST) + qualifiers |= qualified_type_def::CV_CONST; + else if (type_kind == CTF_K_VOLATILE) + qualifiers |= qualified_type_def::CV_VOLATILE; + else if (type_kind == CTF_K_RESTRICT) + qualifiers |= qualified_type_def::CV_RESTRICT; + else + ABG_ASSERT_NOT_REACHED; + + result.reset (new qualified_type_def (utype, qualifiers, location ())); + return result; +} + +/// Build and return a pointer type libabigail IR. +/// +/// @param ctxt the read context. +/// @param corp the libabigail IR corpus being constructed. +/// @param tunit the current IR translation unit. +/// @param ctf_dictionary the CTF dictionary being read. +/// @param ctf_type the CTF type ID of the source type. +/// +/// @return a shared pointer to the IR node for the pointer type. + +static pointer_type_def_sptr +process_ctf_pointer_type (read_context *ctxt, + corpus_sptr corp, + translation_unit_sptr tunit, + ctf_dict_t *ctf_dictionary, + ctf_id_t ctf_type) +{ + pointer_type_def_sptr result; + ctf_id_t ctf_target_type = ctf_type_reference (ctf_dictionary, ctf_type); + type_base_sptr target_type = ctxt->lookup_type (ctf_target_type); + + if (!target_type) + { + target_type = process_ctf_type (ctxt, corp, tunit, ctf_dictionary, + ctf_target_type); + if (!target_type) + return result; + } + + result.reset (new pointer_type_def (target_type, + ctf_type_size (ctf_dictionary, ctf_type) * 8, + ctf_type_align (ctf_dictionary, ctf_type) * 8, + location ())); + return result; +} + +/// Build and return an enum type libabigail IR. +/// +/// @param ctxt the read context. +/// @param corp the libabigail IR corpus being constructed. +/// @param tunit the current IR translation unit. +/// @param ctf_dictionary the CTF dictionary being read. +/// @param ctf_type the CTF type ID of the source type. +/// +/// @return a shared pointer to the IR node for the enum type. + +static enum_type_decl_sptr +process_ctf_enum_type (read_context *ctxt, + corpus_sptr corp, + translation_unit_sptr tunit, + ctf_dict_t *ctf_dictionary, + ctf_id_t ctf_type) +{ + enum_type_decl_sptr result; + + /* Build a signed integral type for the type of the enumerators, aka + the underlying type. The size of the enumerators in bytes is + specified in the CTF enumeration type. */ + size_t utype_size_in_bits = ctf_type_size (ctf_dictionary, ctf_type) * 8; + type_decl_sptr utype; + + utype.reset (new type_decl (ctxt->ir_env, + "", + utype_size_in_bits, + utype_size_in_bits, + location ())); + utype->set_is_anonymous (true); + utype->set_is_artificial (true); + if (!utype) + return result; + add_decl_to_scope (utype, tunit->get_global_scope()); + canonicalize (utype); + + /* Iterate over the enum entries. */ + enum_type_decl::enumerators enms; + ctf_next_t *enum_next = NULL; + const char *ename; + int evalue; + + while ((ename = ctf_enum_next (ctf_dictionary, ctf_type, &enum_next, &evalue))) + enms.push_back (enum_type_decl::enumerator (ctxt->ir_env, ename, evalue)); + if (ctf_errno (ctf_dictionary) != ECTF_NEXT_END) + { + fprintf (stderr, "ERROR from ctf_enum_next\n"); + return result; + } + + const char *enum_name = ctf_type_name_raw (ctf_dictionary, ctf_type); + result.reset (new enum_type_decl (enum_name, location (), + utype, enms, enum_name)); + return result; +} + +/// Add a new type declaration to the given libabigail IR corpus CORP. +/// +/// @param ctxt the read context. +/// @param corp the libabigail IR corpus being constructed. +/// @param tunit the current IR translation unit. +/// @param ctf_dictionary the CTF dictionary being read. +/// @param ctf_type the CTF type ID of the source type. +/// +/// Note that if @ref ctf_type can't reliably be translated to the IR +/// then it is simply ignored. +/// +/// @return a shared pointer to the IR node for the type. + +static type_base_sptr +process_ctf_type (read_context *ctxt, + corpus_sptr corp, + translation_unit_sptr tunit, + ctf_dict_t *ctf_dictionary, + ctf_id_t ctf_type) +{ + int type_kind = ctf_type_kind (ctf_dictionary, ctf_type); + type_base_sptr result; + + switch (type_kind) + { + case CTF_K_INTEGER: + case CTF_K_FLOAT: + { + type_decl_sptr type_decl + = process_ctf_base_type (ctxt, corp, ctf_dictionary, ctf_type); + + if (type_decl) + { + add_decl_to_scope (type_decl, tunit->get_global_scope ()); + result = is_type (type_decl); + } + break; + } + case CTF_K_TYPEDEF: + { + typedef_decl_sptr typedef_decl + = process_ctf_typedef (ctxt, corp, tunit, ctf_dictionary, ctf_type); + + if (typedef_decl) + { + add_decl_to_scope (typedef_decl, tunit->get_global_scope ()); + result = is_type (typedef_decl); + } + break; + } + case CTF_K_POINTER: + { + pointer_type_def_sptr pointer_type + = process_ctf_pointer_type (ctxt, corp, tunit, ctf_dictionary, ctf_type); + + if (pointer_type) + { + add_decl_to_scope (pointer_type, tunit->get_global_scope ()); + result = pointer_type; + } + break; + } + case CTF_K_CONST: + case CTF_K_VOLATILE: + case CTF_K_RESTRICT: + { + type_base_sptr qualified_type + = process_ctf_qualified_type (ctxt, corp, tunit, ctf_dictionary, ctf_type); + + if (qualified_type) + { + decl_base_sptr qualified_type_decl = get_type_declaration (qualified_type); + + add_decl_to_scope (qualified_type_decl, tunit->get_global_scope ()); + result = qualified_type; + } + break; + } + case CTF_K_ARRAY: + { + array_type_def_sptr array_type + = process_ctf_array_type (ctxt, corp, tunit, ctf_dictionary, ctf_type); + + if (array_type) + { + decl_base_sptr array_type_decl = get_type_declaration (array_type); + + add_decl_to_scope (array_type_decl, tunit->get_global_scope ()); + result = array_type; + } + break; + } + case CTF_K_ENUM: + { + enum_type_decl_sptr enum_type + = process_ctf_enum_type (ctxt, corp, tunit, ctf_dictionary, ctf_type); + + if (enum_type) + { + add_decl_to_scope (enum_type, tunit->get_global_scope ()); + result = enum_type; + } + + break; + } + case CTF_K_FUNCTION: + { + function_type_sptr function_type + = process_ctf_function_type (ctxt, corp, tunit, ctf_dictionary, ctf_type); + + if (function_type) + { + decl_base_sptr function_type_decl = get_type_declaration (function_type); + + add_decl_to_scope (function_type_decl, tunit->get_global_scope ()); + result = function_type; + } + break; + } + case CTF_K_STRUCT: + { + class_decl_sptr struct_decl + = process_ctf_struct_type (ctxt, corp, tunit, ctf_dictionary, ctf_type); + + if (struct_decl) + { + add_decl_to_scope (struct_decl, tunit->get_global_scope ()); + result = is_type (struct_decl); + } + break; + } + case CTF_K_UNION: + { + union_decl_sptr union_decl + = process_ctf_union_type (ctxt, corp, tunit, ctf_dictionary, ctf_type); + + if (union_decl) + { + add_decl_to_scope (union_decl, tunit->get_global_scope ()); + result = is_type (union_decl); + } + break; + } + case CTF_K_UNKNOWN: + /* Unknown types are simply ignored. */ + default: + break; + } + + if (result) + { + decl_base_sptr result_decl = get_type_declaration (result); + + canonicalize (result); + ctxt->add_type (ctf_type, result); + } + else + fprintf (stderr, "NOT PROCESSED TYPE %lu\n", ctf_type); + + return result; +} + +/// Process a CTF archive and create libabigail IR for the types, +/// variables and function declarations found in the archive. The IR +/// is added to the given corpus. +/// +/// @param ctxt the read context containing the CTF archive to +/// process. +/// @param corp the IR corpus to which add the new contents. + +static void +process_ctf_archive (read_context *ctxt, corpus_sptr corp) +{ + /* We only have a translation unit. */ + translation_unit_sptr ir_translation_unit = + std::make_shared (ctxt->ir_env, "", 64); + ir_translation_unit->set_language (translation_unit::LANG_C); + corp->add (ir_translation_unit); + + /* Iterate over the CTF dictionaries in the archive. */ + int ctf_err; + ctf_dict_t *ctf_dict; + ctf_next_t *dict_next = NULL; + const char *archive_name; + + while ((ctf_dict = ctf_archive_next (ctxt->ctfa, &dict_next, &archive_name, + 0 /* skip_parent */, &ctf_err)) != NULL) + { + /* Iterate over the CTF types stored in this archive. */ + ctf_id_t ctf_type; + int type_flag; + ctf_next_t *type_next = NULL; + + while ((ctf_type = ctf_type_next (ctf_dict, &type_next, &type_flag, + 1 /* want_hidden */)) != CTF_ERR) + { + process_ctf_type (ctxt, corp, ir_translation_unit, + ctf_dict, ctf_type); + } + if (ctf_errno (ctf_dict) != ECTF_NEXT_END) + fprintf (stderr, "ERROR from ctf_type_next\n"); + + /* Iterate over the CTF variables stored in this archive. */ + ctf_id_t ctf_var_type; + ctf_next_t *var_next = NULL; + const char *var_name; + + while ((ctf_var_type = ctf_variable_next (ctf_dict, &var_next, &var_name)) + != CTF_ERR) + { + type_base_sptr var_type = ctxt->lookup_type (ctf_var_type); + + if (!var_type) + { + var_type = process_ctf_type (ctxt, corp, ir_translation_unit, + ctf_dict, ctf_var_type); + if (!var_type) + /* Ignore variable if its type can't be sorted out. */ + continue; + } + + var_decl_sptr var_declaration; + var_declaration.reset (new var_decl (var_name, + var_type, + location (), + var_name)); + + add_decl_to_scope (var_declaration, + ir_translation_unit->get_global_scope ()); + } + if (ctf_errno (ctf_dict) != ECTF_NEXT_END) + fprintf (stderr, "ERROR from ctf_variable_next\n"); + + /* Iterate over the CTF functions stored in this archive. */ + ctf_next_t *func_next = NULL; + const char *func_name = NULL; + ctf_id_t ctf_sym; + + while ((ctf_sym = ctf_symbol_next (ctf_dict, &func_next, &func_name, + 1 /* functions symbols only */) != CTF_ERR)) + { + ctf_id_t ctf_func_type = ctf_lookup_by_name (ctf_dict, func_name); + type_base_sptr func_type = ctxt->lookup_type (ctf_func_type); + if (!func_type) + { + func_type = process_ctf_type (ctxt, corp, ir_translation_unit, + ctf_dict, ctf_func_type); + if (!func_type) + /* Ignore function if its type can't be sorted out. */ + continue; + } + + function_decl_sptr func_declaration; + func_declaration.reset (new function_decl (func_name, + func_type, + 0 /* is_inline */, + location ())); + + add_decl_to_scope (func_declaration, + ir_translation_unit->get_global_scope ()); + } + if (ctf_errno (ctf_dict) != ECTF_NEXT_END) + fprintf (stderr, "ERROR from ctf_symbol_next\n"); + } + if (ctf_err != ECTF_NEXT_END) + fprintf (stderr, "ERROR from ctf_archive_next\n"); + +} + +/// Slurp certain information from the ELF file described by a given +/// read context and install it in a libabigail corpus. +/// +/// @param ctxt the read context +/// @param corp the libabigail corpus in which to install the info. +/// +/// @return 0 if there is an error. +/// @return 1 otherwise. + +static int +slurp_elf_info (read_context *ctxt, corpus_sptr corp) +{ + /* libelf requires to negotiate/set the version of ELF. */ + if (elf_version (EV_CURRENT) == EV_NONE) + return 0; + + /* Open an ELF handler. */ + int elf_fd = open (ctxt->filename.c_str(), O_RDONLY); + if (elf_fd == -1) + return 0; + + Elf *elf_handler = elf_begin (elf_fd, ELF_C_READ, NULL); + if (elf_handler == NULL) + { + fprintf (stderr, "cannot open %s: %s\n", + ctxt->filename.c_str(), elf_errmsg (elf_errno ())); + close (elf_fd); + return 0; + } + + /* Set the ELF architecture. */ + GElf_Ehdr eh_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf_handler, &eh_mem); + corp->set_architecture_name (elf_helpers::e_machine_to_string (ehdr->e_machine)); + + /* Read the symtab from the ELF file and set it in the corpus. */ + symtab_reader::symtab_sptr symtab = + symtab_reader::symtab::load (elf_handler, ctxt->ir_env, + 0 /* No suppressions. */); + corp->set_symtab(symtab); + + /* Finish the ELF handler and close the associated file. */ + elf_end (elf_handler); + close (elf_fd); + + return 1; +} + +/// Create and return a new read context to process CTF information +/// from a given ELF file. +/// +/// @param elf_path the patch of some ELF file. +/// @param env a libabigail IR environment. + +read_context * +create_read_context (std::string elf_path, ir::environment *env) +{ + return new read_context (elf_path, env); +} + +/// Read the CTF information from some source described by a given +/// read context and process it to create a libabigail IR corpus. +/// Store the corpus in the same read context. +/// +/// @param ctxt the read context to use. +/// @return a shared pointer to the read corpus. + +corpus_sptr +read_corpus (read_context *ctxt) +{ + corpus_sptr corp + = std::make_shared (ctxt->ir_env, ctxt->filename); + + /* Set some properties of the corpus first. */ + corp->set_origin(corpus::CTF_ORIGIN); + if (!slurp_elf_info (ctxt, corp)) + return corp; + + /* Get out now if no CTF debug info is found. */ + if (ctxt->ctfa == NULL) + return corp; + + /* Process the CTF archive in the read context, if any. Information + about the types, variables, functions, etc contained in the + archive are added to the given corpus. */ + process_ctf_archive (ctxt, corp); + return corp; +} + +} // End of namespace ctf_reader +} // End of namespace abigail diff --git a/tools/abidiff.cc b/tools/abidiff.cc index 21f7ff61..db021bf4 100644 --- a/tools/abidiff.cc +++ b/tools/abidiff.cc @@ -19,6 +19,7 @@ #include "abg-tools-utils.h" #include "abg-reader.h" #include "abg-dwarf-reader.h" +#include "abg-ctf-reader.h" using std::vector; using std::string; @@ -104,6 +105,7 @@ struct options #ifdef WITH_DEBUG_SELF_COMPARISON bool do_debug; #endif + bool use_ctf; vector di_root_paths1; vector di_root_paths2; vector prepared_di_root_paths1; @@ -144,7 +146,8 @@ struct options show_impacted_interfaces(), dump_diff_tree(), show_stats(), - do_log() + do_log(), + use_ctf() #ifdef WITH_DEBUG_SELF_COMPARISON , do_debug() @@ -233,6 +236,7 @@ display_usage(const string& prog_name, ostream& out) << " --dump-diff-tree emit a debug dump of the internal diff tree to " "the error output stream\n" << " --stats show statistics about various internal stuff\n" + << " --ctf use CTF instead of DWARF in ELF files\n" #ifdef WITH_DEBUG_SELF_COMPARISON << " --debug debug the process of comparing an ABI corpus against itself" #endif @@ -579,6 +583,8 @@ parse_command_line(int argc, char* argv[], options& opts) opts.show_stats = true; else if (!strcmp(argv[i], "--verbose")) opts.do_log = true; + else if (!strcmp(argv[i], "--ctf")) + opts.use_ctf = true; #ifdef WITH_DEBUG_SELF_COMPARISON else if (!strcmp(argv[i], "--debug")) opts.do_debug = true; @@ -1150,23 +1156,35 @@ main(int argc, char* argv[]) case abigail::tools_utils::FILE_TYPE_ELF: // fall through case abigail::tools_utils::FILE_TYPE_AR: { - abigail::dwarf_reader::read_context_sptr ctxt = - abigail::dwarf_reader::create_read_context - (opts.file1, opts.prepared_di_root_paths1, - env.get(), /*read_all_types=*/opts.show_all_types, - opts.linux_kernel_mode); - assert(ctxt); - - abigail::dwarf_reader::set_show_stats(*ctxt, opts.show_stats); - set_suppressions(*ctxt, opts); - abigail::dwarf_reader::set_do_log(*ctxt, opts.do_log); - c1 = abigail::dwarf_reader::read_corpus_from_elf(*ctxt, c1_status); - if (!c1 - || (opts.fail_no_debug_info - && (c1_status & STATUS_ALT_DEBUG_INFO_NOT_FOUND) - && (c1_status & STATUS_DEBUG_INFO_NOT_FOUND))) - return handle_error(c1_status, ctxt.get(), - argv[0], opts); + if (opts.use_ctf) + { + abigail::ctf_reader::read_context *ctxt + = abigail::ctf_reader::create_read_context (opts.file1, + env.get()); + + assert (ctxt); + c1 = abigail::ctf_reader::read_corpus (ctxt); + } + else + { + abigail::dwarf_reader::read_context_sptr ctxt = + abigail::dwarf_reader::create_read_context + (opts.file1, opts.prepared_di_root_paths1, + env.get(), /*read_all_types=*/opts.show_all_types, + opts.linux_kernel_mode); + assert(ctxt); + + abigail::dwarf_reader::set_show_stats(*ctxt, opts.show_stats); + set_suppressions(*ctxt, opts); + abigail::dwarf_reader::set_do_log(*ctxt, opts.do_log); + c1 = abigail::dwarf_reader::read_corpus_from_elf(*ctxt, c1_status); + if (!c1 + || (opts.fail_no_debug_info + && (c1_status & STATUS_ALT_DEBUG_INFO_NOT_FOUND) + && (c1_status & STATUS_DEBUG_INFO_NOT_FOUND))) + return handle_error(c1_status, ctxt.get(), + argv[0], opts); + } } break; case abigail::tools_utils::FILE_TYPE_XML_CORPUS: @@ -1219,23 +1237,34 @@ main(int argc, char* argv[]) case abigail::tools_utils::FILE_TYPE_ELF: // Fall through case abigail::tools_utils::FILE_TYPE_AR: { - abigail::dwarf_reader::read_context_sptr ctxt = - abigail::dwarf_reader::create_read_context - (opts.file2, opts.prepared_di_root_paths2, - env.get(), /*read_all_types=*/opts.show_all_types, - opts.linux_kernel_mode); - assert(ctxt); - abigail::dwarf_reader::set_show_stats(*ctxt, opts.show_stats); - abigail::dwarf_reader::set_do_log(*ctxt, opts.do_log); - set_suppressions(*ctxt, opts); - - c2 = abigail::dwarf_reader::read_corpus_from_elf(*ctxt, c2_status); - if (!c2 - || (opts.fail_no_debug_info - && (c2_status & STATUS_ALT_DEBUG_INFO_NOT_FOUND) - && (c2_status & STATUS_DEBUG_INFO_NOT_FOUND))) - return handle_error(c2_status, ctxt.get(), argv[0], opts); - + if (opts.use_ctf) + { + abigail::ctf_reader::read_context *ctxt + = abigail::ctf_reader::create_read_context (opts.file2, + env.get()); + + assert (ctxt); + c2 = abigail::ctf_reader::read_corpus (ctxt); + } + else + { + abigail::dwarf_reader::read_context_sptr ctxt = + abigail::dwarf_reader::create_read_context + (opts.file2, opts.prepared_di_root_paths2, + env.get(), /*read_all_types=*/opts.show_all_types, + opts.linux_kernel_mode); + assert(ctxt); + abigail::dwarf_reader::set_show_stats(*ctxt, opts.show_stats); + abigail::dwarf_reader::set_do_log(*ctxt, opts.do_log); + set_suppressions(*ctxt, opts); + + c2 = abigail::dwarf_reader::read_corpus_from_elf(*ctxt, c2_status); + if (!c2 + || (opts.fail_no_debug_info + && (c2_status & STATUS_ALT_DEBUG_INFO_NOT_FOUND) + && (c2_status & STATUS_DEBUG_INFO_NOT_FOUND))) + return handle_error(c2_status, ctxt.get(), argv[0], opts); + } } break; case abigail::tools_utils::FILE_TYPE_XML_CORPUS: diff --git a/tools/abilint.cc b/tools/abilint.cc index 856f935d..b1551ea3 100644 --- a/tools/abilint.cc +++ b/tools/abilint.cc @@ -27,6 +27,7 @@ #include "abg-corpus.h" #include "abg-reader.h" #include "abg-dwarf-reader.h" +#include "abg-ctf-reader.h" #include "abg-writer.h" #include "abg-suppression.h" @@ -67,6 +68,7 @@ struct options bool read_tu; bool diff; bool noout; + bool use_ctf; std::shared_ptr di_root_path; vector suppression_paths; string headers_dir; @@ -77,7 +79,8 @@ struct options read_from_stdin(false), read_tu(false), diff(false), - noout(false) + noout(false), + use_ctf(false) {} };//end struct options; @@ -99,7 +102,8 @@ display_usage(const string& prog_name, ostream& out) "the input and the memory model saved back to disk\n" << " --noout do not display anything on stdout\n" << " --stdin|-- read abi-file content from stdin\n" - << " --tu expect a single translation unit file\n"; + << " --tu expect a single translation unit file\n" + << " --ctf use CTF instead of DWARF in ELF files\n"; } bool @@ -173,6 +177,8 @@ parse_command_line(int argc, char* argv[], options& opts) opts.read_from_stdin = true; else if (!strcmp(argv[i], "--tu")) opts.read_tu = true; + else if (!strcmp(argv[i], "--ctf")) + opts.use_ctf = true; else if (!strcmp(argv[i], "--diff")) opts.diff = true; else if (!strcmp(argv[i], "--noout")) @@ -338,13 +344,26 @@ main(int argc, char* argv[]) di_root_path = opts.di_root_path.get(); vector di_roots; di_roots.push_back(&di_root_path); - abigail::dwarf_reader::read_context_sptr ctxt = - abigail::dwarf_reader::create_read_context(opts.file_path, - di_roots, env.get(), - /*load_all_types=*/false); - assert(ctxt); - set_suppressions(*ctxt, opts); - corp = read_corpus_from_elf(*ctxt, s); + + if (opts.use_ctf) + { + abigail::ctf_reader::read_context *ctxt + = abigail::ctf_reader::create_read_context (opts.file_path, + env.get()); + + assert (ctxt); + corp = abigail::ctf_reader::read_corpus (ctxt); + } + else + { + abigail::dwarf_reader::read_context_sptr ctxt = + abigail::dwarf_reader::create_read_context(opts.file_path, + di_roots, env.get(), + /*load_all_types=*/false); + assert(ctxt); + set_suppressions(*ctxt, opts); + corp = read_corpus_from_elf(*ctxt, s); + } } break; case abigail::tools_utils::FILE_TYPE_XML_CORPUS: