diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c49a157bf..955518e7d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,27 +1,54 @@ name: CI -on: [push, pull_request] +on: + push: + branches: + - main + workflow_dispatch: + inputs: + test_all_python_versions: + description: "Run tests on all Python versions" + type: boolean + default: false + required: true + workflow_call: + inputs: + test_all_python_versions: + description: "Run tests on all Python versions" + type: boolean + default: false + required: true + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true jobs: test: - runs-on: ubuntu-latest + # We're stuck on Ubuntu 20.04 as long as we want to keep testing on Python + # 3.6 due to actions/setup-python#544. + runs-on: ubuntu-20.04 strategy: matrix: - python-version: ['3.10', '3.9', '3.8', '3.7', '3.6'] + python-version: ${{ (github.event_name == 'push' || inputs.test_all_python_versions) + && fromJSON('["3.12", "3.11", "3.10", "3.9", "3.8", "3.7", "3.6"]') + || fromJSON('["3.11", "3.6"]')}} cc: [gcc, clang] fail-fast: false env: CC: ${{ matrix.cc }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + allow-prereleases: true - name: Install dependencies run: | sudo apt-get update - sudo apt-get install libelf-dev libdw-dev qemu-kvm zstd ${{ matrix.cc == 'clang' && 'libomp-$(clang --version | sed -rn "s/.*clang version ([0-9]+).*/\\1/p")-dev' || '' }} + sudo apt-get install dwarves libelf-dev libdw-dev qemu-kvm zstd ${{ matrix.cc == 'clang' && 'libomp-$(clang --version | sed -rn "s/.*clang version ([0-9]+).*/\\1/p")-dev' || '' }} pip install pyroute2 pre-commit - name: Generate version.py run: python setup.py --version @@ -33,9 +60,11 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 + with: + python-version: '3.x' - name: Install dependencies run: pip install pre-commit - name: Run pre-commit hooks diff --git a/.github/workflows/dco-check.yml b/.github/workflows/dco-check.yml index 319b4b125..06822fe41 100644 --- a/.github/workflows/dco-check.yml +++ b/.github/workflows/dco-check.yml @@ -4,6 +4,10 @@ on: pull_request: types: [opened, synchronize, reopened, ready_for_review] +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + jobs: check: if: ${{ !github.event.pull_request.draft }} diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml new file mode 100644 index 000000000..6a9be9fd4 --- /dev/null +++ b/.github/workflows/pull_request.yml @@ -0,0 +1,16 @@ +name: Pull Request CI + +on: + pull_request: + types: + - opened + - synchronize + - reopened + - labeled + +jobs: + test: + uses: ./.github/workflows/ci.yml + if: ${{ github.event.action != 'labeled' || github.event.label.name == 'test-all-python-versions' }} + with: + test_all_python_versions: ${{ contains(github.event.pull_request.labels.*.name, 'test-all-python-versions') }} diff --git a/.github/workflows/vmtest-build.yml b/.github/workflows/vmtest-build.yml index 5fc9bc9f9..56d5df6bc 100644 --- a/.github/workflows/vmtest-build.yml +++ b/.github/workflows/vmtest-build.yml @@ -7,22 +7,37 @@ on: jobs: build: - runs-on: ubuntu-latest + strategy: + matrix: + flavor: [default, alternative, tiny] + arch: [x86_64, aarch64, ppc64, s390x, arm] + fail-fast: false + max-parallel: 5 + # Build on 20.04 so that we don't get host binaries (e.g., objtool) that + # depend on libraries too new for other distros. + runs-on: ubuntu-20.04 + permissions: + contents: write env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install dependencies run: | sudo apt-get update sudo apt-get install dwarves libelf-dev pip install aiohttp uritemplate - - name: Build and upload assets - run: python3 -m vmtest.manage --kernel-directory build/vmtest/linux.git --build-directory build/vmtest/kbuild -K + - name: Build and upload ${{ matrix.arch }} ${{ matrix.flavor }} kernels + run: python3 -m vmtest.manage --kernel-directory build/vmtest/linux.git --build-directory build/vmtest/kbuild -K -a ${{ matrix.arch }} -f ${{ matrix.flavor }} - name: Upload kernel build logs if: always() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: kernel-build-logs path: build/vmtest/kbuild/*.log if-no-files-found: ignore + test: + needs: build + uses: ./.github/workflows/ci.yml + with: + test_all_python_versions: true diff --git a/.packit.yaml b/.packit.yaml index 309978c5d..0be8d03bf 100644 --- a/.packit.yaml +++ b/.packit.yaml @@ -2,7 +2,7 @@ # https://packit.dev/docs/configuration/ specfile_path: python-drgn.spec -synced_files: +files_to_sync: - python-drgn.spec - .packit.yaml @@ -13,55 +13,60 @@ actions: # Fetch the specfile from Rawhide, drop any patches and disable rpmautospec post-upstream-clone: "bash -c \"curl -s https://src.fedoraproject.org/rpms/python-drgn/raw/main/f/python-drgn.spec | sed -e '/^Patch[0-9]/d' -e '/^%autochangelog$/d' > python-drgn.spec\"" +srpm_build_deps: + - bash + - curl + - python3-setuptools + - sed + jobs: - job: copr_build trigger: commit - metadata: - owner: "@meta" - project: drgn - targets: - - fedora-all-aarch64 - - fedora-all-armhfp - - fedora-all-i386 - - fedora-all-ppc64le - - fedora-all-s390x - - fedora-all-x86_64 - - fedora-eln-aarch64 - - fedora-eln-i386 - - fedora-eln-ppc64le - - fedora-eln-s390x - - fedora-eln-x86_64 - - epel-8-aarch64 - - epel-8-ppc64le - - epel-8-s390x - - epel-8-x86_64 - - epel-9-aarch64 - - epel-9-ppc64le - - epel-9-s390x - - epel-9-x86_64 + owner: "@meta" + project: drgn + targets: + - fedora-all-aarch64 + # Disabled due to https://bugzilla.redhat.com/show_bug.cgi?id=2152259. + # - fedora-all-armhfp + - fedora-all-i386 + - fedora-all-ppc64le + - fedora-all-s390x + - fedora-all-x86_64 + - fedora-eln-aarch64 + - fedora-eln-i386 + - fedora-eln-ppc64le + - fedora-eln-s390x + - fedora-eln-x86_64 + - epel-8-aarch64 + - epel-8-ppc64le + - epel-8-s390x + - epel-8-x86_64 + - epel-9-aarch64 + - epel-9-ppc64le + - epel-9-s390x + - epel-9-x86_64 - job: copr_build trigger: pull_request - metadata: - owner: "@meta" - project: drgn - targets: - - fedora-all-aarch64 - - fedora-all-armhfp - - fedora-all-i386 - - fedora-all-ppc64le - - fedora-all-s390x - - fedora-all-x86_64 - - fedora-eln-aarch64 - - fedora-eln-i386 - - fedora-eln-ppc64le - - fedora-eln-s390x - - fedora-eln-x86_64 - - epel-8-aarch64 - - epel-8-ppc64le - - epel-8-s390x - - epel-8-x86_64 - - epel-9-aarch64 - - epel-9-ppc64le - - epel-9-s390x - - epel-9-x86_64 + owner: "@meta" + project: drgn + targets: + - fedora-all-aarch64 + # - fedora-all-armhfp + - fedora-all-i386 + - fedora-all-ppc64le + - fedora-all-s390x + - fedora-all-x86_64 + - fedora-eln-aarch64 + - fedora-eln-i386 + - fedora-eln-ppc64le + - fedora-eln-s390x + - fedora-eln-x86_64 + - epel-8-aarch64 + - epel-8-ppc64le + - epel-8-s390x + - epel-8-x86_64 + - epel-9-aarch64 + - epel-9-ppc64le + - epel-9-s390x + - epel-9-x86_64 diff --git a/.readthedocs.yml b/.readthedocs.yaml similarity index 68% rename from .readthedocs.yml rename to .readthedocs.yaml index fe579b18e..03853730e 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yaml @@ -1,4 +1,8 @@ version: 2 +build: + os: ubuntu-22.04 + tools: + python: "3" sphinx: configuration: docs/conf.py python: diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 3074dc8c4..fcdaf0027 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -49,7 +49,6 @@ Coding Guidelines * Core functionality should be implemented in ``libdrgn`` and exposed to Python via the `C extension `_. Only the CLI and helpers should be in pure Python. -* Linux kernel helpers should work on all supported kernels if possible. C ^ @@ -107,6 +106,61 @@ and `isort `_ and checked with `flake8 Type hints should be provided for all interfaces (including helpers and the C extension). +Linux Kernel Helpers +^^^^^^^^^^^^^^^^^^^^ + +Linux kernel helpers should work on all `supported kernels +`_ +if possible. This may require handling changes between kernel releases. + +* Do NOT check the kernel version number to do this; Linux distributions often + backport changes without updating the version number. Instead, use the + presence or absence of variables, types, structure members, etc. +* Optimize for the latest kernel release, and follow "easier to ask for + forgiveness than permission" (`EAFP + `_). For example, assume + that a structure member from the latest release exists and catch the + exception if it doesn't. +* Reference the diverging commit and version number in the format ``Linux + kernel commit $abbreviated_commit_hash "$commit_subject" (in + v$kernel_version)``. + + For example: + + .. code-block:: python3 + + # Since Linux kernel commit 2f064a59a11f ("sched: Change + # task_struct::state") (in v5.14), the task state is named "__state". + # Before that, it is named "state". + try: + return task.__state + except AttributeError: + return task.state + + NOT: + + .. code-block:: python3 + + # BAD + if hasattr(task, "state"): + return task.state + else: + return task.__state + +* Document the expected C types of arguments and return values. For example: + + .. code-block:: python3 + + def cgroup_parent(cgrp: Object) -> Object: + """ + Return the parent cgroup of the given cgroup if it exists, ``NULL`` + otherwise. + + :param cgrp: ``struct cgroup *`` + :return: ``struct cgroup *`` + """ + ... + pre-commit ^^^^^^^^^^ diff --git a/COPYING b/COPYING index 94a9ed024..64a81d222 100644 --- a/COPYING +++ b/COPYING @@ -1,674 +1,6 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 +drgn is provided under: - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. + SPDX-License-Identifier: LGPL-2.1-or-later - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. +Some source files are provided under different licenses as noted in each file. +See the LICENSES directory for the full list of licenses used. diff --git a/LICENSES/GPL-2.0-or-later.txt b/LICENSES/GPL-2.0-or-later.txt new file mode 100644 index 000000000..17cb28643 --- /dev/null +++ b/LICENSES/GPL-2.0-or-later.txt @@ -0,0 +1,117 @@ +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. + + c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author + + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. + +signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice diff --git a/LICENSES/LGPL-2.1-or-later.txt b/LICENSES/LGPL-2.1-or-later.txt new file mode 100644 index 000000000..c9aa53018 --- /dev/null +++ b/LICENSES/LGPL-2.1-or-later.txt @@ -0,0 +1,175 @@ +GNU LESSER GENERAL PUBLIC LICENSE + +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. + +When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. + +To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. + +We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. + +(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. + +However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. + +When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: + + a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. + + e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. + + b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). + +To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + one line to give the library's name and an idea of what it does. + Copyright (C) year name of author + + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in +the library `Frob' (a library for tweaking knobs) written +by James Random Hacker. + +signature of Ty Coon, 1 April 1990 +Ty Coon, President of Vice +That's all there is to it! diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt new file mode 100644 index 000000000..2071b23b0 --- /dev/null +++ b/LICENSES/MIT.txt @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in index 8ac83e5a1..3ac3e300c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,7 +1,9 @@ recursive-include docs *.css *.ico *.png *.py *.rst -recursive-include examples *.py recursive-include tests *.py *.zst include tests/linux_kernel/kmod/Makefile tests/linux_kernel/kmod/drgn_test.c -recursive-include tools *.py +recursive-include contrib *.py *.rst +recursive-include tools *.py *.rst recursive-include vmtest *.c *.py *.rst +recursive-include vmtest/patches *.patch +recursive-include LICENSES *.txt include .flake8 CONTRIBUTING.rst COPYING pytest.ini util.py diff --git a/README.rst b/README.rst index 66f0b1ffe..ff9c614e8 100644 --- a/README.rst +++ b/README.rst @@ -64,6 +64,10 @@ Package Manager drgn can be installed using the package manager on some Linux distributions. +.. image:: https://repology.org/badge/vertical-allrepos/drgn.svg + :target: https://repology.org/project/drgn/versions + :alt: Packaging Status + * Fedora >= 32 .. code-block:: console @@ -83,12 +87,27 @@ drgn can be installed using the package manager on some Linux distributions. Install the `drgn `_ package from the `AUR `_. +* Debian >= 12 (Bookworm) + + .. code-block:: console + + $ sudo apt install python3-drgn + * openSUSE .. code-block:: console $ sudo zypper install python3-drgn +* Ubuntu + + Enable the `michel-slm/kernel-utils PPA `_. + Then: + + .. code-block:: console + + $ sudo apt install python3-drgn + pip ^^^ @@ -230,7 +249,7 @@ License Copyright (c) Meta Platforms, Inc. and affiliates. -drgn is licensed under the `GPLv3 -`_ or later. +drgn is licensed under the `LGPLv2.1 +`_ or later. .. end-license diff --git a/_drgn.pyi b/_drgn.pyi index c1b520fb8..007501d5f 100644 --- a/_drgn.pyi +++ b/_drgn.pyi @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ libdrgn bindings @@ -13,6 +13,7 @@ import sys from typing import ( Any, Callable, + ClassVar, Dict, Iterable, Iterator, @@ -26,9 +27,14 @@ from typing import ( ) if sys.version_info < (3, 8): - from typing_extensions import Protocol + from typing_extensions import Final, Protocol else: - from typing import Protocol + from typing import Final, Protocol + +if sys.version_info < (3, 10): + from typing_extensions import TypeAlias +else: + from typing import TypeAlias # This is effectively typing.SupportsIndex without @typing.runtime_checkable # (both of which are only available since Python 3.8), with a more @@ -43,7 +49,7 @@ class IntegerLike(Protocol): def __index__(self) -> int: ... -Path = Union[str, bytes, os.PathLike[str], os.PathLike[bytes]] +Path: TypeAlias = Union[str, bytes, os.PathLike[str], os.PathLike[bytes]] """ Filesystem path. @@ -185,6 +191,10 @@ class Program: """ Get the object (variable, constant, or function) with the given name. + When debugging the Linux kernel, this can look up certain special + objects documented in :ref:`kernel-special-objects`, sometimes without + any debugging information loaded. + :param name: The object name. :param flags: Flags indicating what kind of object to look for. :param filename: The source code file that contains the definition. See @@ -845,9 +855,9 @@ class FindObjectFlags(enum.Flag): class Thread: """A thread in a program.""" - tid: int + tid: Final[int] """Thread ID (as defined by :manpage:`gettid(2)`).""" - object: Object + object: Final[Object] """ If the program is the Linux kernel, the ``struct task_struct *`` object for this thread. Otherwise, not defined. @@ -921,13 +931,13 @@ class Platform: the architecture are used. """ ... - arch: Architecture + arch: Final[Architecture] """Instruction set architecture of this platform.""" - flags: PlatformFlags + flags: Final[PlatformFlags] """Flags which apply to this platform.""" - registers: Sequence[Register] + registers: Final[Sequence[Register]] """Processor registers on this platform.""" class Architecture(enum.Enum): @@ -954,6 +964,12 @@ class Architecture(enum.Enum): RISCV32 = ... """The 32-bit RISC-V architecture.""" + S390X = ... + """The s390x architecture, a.k.a. IBM Z or z/Architecture.""" + + S390 = ... + """The 32-bit s390 architecture, a.k.a. System/390.""" + UNKNOWN = ... """ An architecture which is not known to drgn. Certain features are not @@ -973,7 +989,7 @@ class PlatformFlags(enum.Flag): class Register: """A ``Register`` represents information about a processor register.""" - names: Sequence[str] + names: Final[Sequence[str]] """Names of this register.""" host_platform: Platform @@ -987,13 +1003,13 @@ class Language: languages. """ - name: str + name: Final[str] """Name of the programming language.""" - C: Language + C: ClassVar[Language] """The C programming language.""" - CPP: Language + CPP: ClassVar[Language] """The C++ programming language.""" class Object: @@ -1145,13 +1161,13 @@ class Object: ) -> None: """Create an absent object.""" ... - prog_: Program + prog_: Final[Program] """Program that this object is from.""" - type_: Type + type_: Final[Type] """Type of this object.""" - absent_: bool + absent_: Final[bool] """ Whether this object is absent. @@ -1159,20 +1175,20 @@ class Object: an invalid address). """ - address_: Optional[int] + address_: Final[Optional[int]] """ Address of this object if it is a reference, ``None`` if it is a value or absent. """ - bit_offset_: Optional[int] + bit_offset_: Final[Optional[int]] """ Offset in bits from this object's address to the beginning of the object if it is a reference, ``None`` otherwise. This can only be non-zero for scalars. """ - bit_field_size_: Optional[int] + bit_field_size_: Final[Optional[int]] """ Size in bits of this object if it is a bit field, ``None`` if it is not. """ @@ -1510,19 +1526,19 @@ class Symbol: identifier along with its corresponding address range in the program. """ - name: str + name: Final[str] """Name of this symbol.""" - address: int + address: Final[int] """Start address of this symbol.""" - size: int + size: Final[int] """Size of this symbol in bytes.""" - binding: SymbolBinding + binding: Final[SymbolBinding] """Linkage behavior and visibility of this symbol.""" - kind: SymbolKind + kind: Final[SymbolKind] """Kind of entity represented by this symbol.""" class SymbolBinding(enum.Enum): @@ -1613,10 +1629,12 @@ class StackTrace: traces are displayed with ``str()`` by default. """ - prog: Program + prog: Final[Program] """Program that this stack trace is from.""" def __getitem__(self, idx: IntegerLike) -> StackFrame: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[StackFrame]: ... def _repr_pretty_(self, p: Any, cycle: bool) -> None: ... class StackFrame: @@ -1641,7 +1659,7 @@ class StackFrame: (int)1 """ - name: Optional[str] + name: Final[Optional[str]] """ Name of the function at this frame, or ``None`` if it could not be determined. @@ -1660,7 +1678,7 @@ class StackFrame: name = hex(frame.pc) """ - is_inline: bool + is_inline: Final[bool] """ Whether this frame is for an inlined call. @@ -1669,7 +1687,7 @@ class StackFrame: symbol). """ - interrupted: bool + interrupted: Final[bool] """ Whether this stack frame was interrupted (for example, by a hardware interrupt, signal, trap, etc.). @@ -1683,8 +1701,12 @@ class StackFrame: the instruction after the call instruction. """ - pc: int + pc: Final[int] """Program counter at this stack frame.""" + + sp: Final[int] + """Stack pointer at this stack frame.""" + def __getitem__(self, name: str) -> Object: """ Implement ``self[name]``. Get the object (variable, function parameter, @@ -1704,6 +1726,16 @@ class StackFrame: :param name: Object name. """ ... + def locals(self) -> List[str]: + """ + Get a list of the names of all local objects (local variables, function + parameters, local constants, and nested functions) in the scope of this + frame. + + Not all names may have present values, but they can be used with the + :meth:`[] <.__getitem__>` operator to check. + """ + ... def source(self) -> Tuple[str, int, int]: """ Get the source code location of this frame. @@ -1765,60 +1797,60 @@ class Type: :ref:`api-type-constructors`. """ - prog: Program + prog: Final[Program] """Program that this type is from.""" - kind: TypeKind + kind: Final[TypeKind] """Kind of this type.""" - primitive: Optional[PrimitiveType] + primitive: Final[Optional[PrimitiveType]] """ If this is a primitive type (e.g., ``int`` or ``double``), the kind of primitive type. Otherwise, ``None``. """ - qualifiers: Qualifiers + qualifiers: Final[Qualifiers] """Bitmask of this type's qualifier.""" - language: Language + language: Final[Language] """Programming language of this type.""" - name: str + name: Final[str] """ Name of this type. This is present for integer, boolean, floating-point, and typedef types. """ - tag: Optional[str] + tag: Final[Optional[str]] """ Tag of this type, or ``None`` if this is an anonymous type. This is present for structure, union, class, and enumerated types. """ - size: Optional[int] + size: Final[Optional[int]] """ Size of this type in bytes, or ``None`` if this is an incomplete type. This is present for integer, boolean, floating-point, structure, union, class, and pointer types. """ - length: Optional[int] + length: Final[Optional[int]] """ Number of elements in this type, or ``None`` if this is an incomplete type. This is only present for array types. """ - is_signed: bool + is_signed: Final[bool] """Whether this type is signed. This is only present for integer types.""" - byteorder: str + byteorder: Final[str] """ Byte order of this type: ``'little'`` if it is little-endian, or ``'big'`` if it is big-endian. This is present for integer, boolean, floating-point, and pointer types. """ - type: Type + type: Final[Type] """ Type underlying this type, defined as follows: @@ -1832,30 +1864,30 @@ class Type: For other types, this attribute is not present. """ - members: Optional[Sequence[TypeMember]] + members: Final[Optional[Sequence[TypeMember]]] """ List of members of this type, or ``None`` if this is an incomplete type. This is present for structure, union, and class types. """ - enumerators: Optional[Sequence[TypeEnumerator]] + enumerators: Final[Optional[Sequence[TypeEnumerator]]] """ List of enumeration constants of this type, or ``None`` if this is an incomplete type. This is only present for enumerated types. """ - parameters: Sequence[TypeParameter] + parameters: Final[Sequence[TypeParameter]] """ List of parameters of this type. This is only present for function types. """ - is_variadic: bool + is_variadic: Final[bool] """ Whether this type takes a variable number of arguments. This is only present for function types. """ - template_parameters: Sequence[TypeTemplateParameter] + template_parameters: Final[Sequence[TypeTemplateParameter]] """ List of template parameters of this type. This is present for structure, union, class, and function types. @@ -1939,7 +1971,7 @@ class TypeMember: :param bit_offset: :attr:`TypeMember.bit_offset` """ ... - object: Object + object: Final[Object] """ Member as an :class:`Object`. @@ -1949,26 +1981,26 @@ class TypeMember: usually absent.) """ - type: Type + type: Final[Type] """ Member type. This is a shortcut for ``TypeMember.object.type``. """ - name: Optional[str] + name: Final[Optional[str]] """Member name, or ``None`` if the member is unnamed.""" - bit_offset: int + bit_offset: Final[int] """Offset of the member from the beginning of the type in bits.""" - offset: int + offset: Final[int] """ Offset of the member from the beginning of the type in bytes. If the offset is not byte-aligned, accessing this attribute raises :exc:`ValueError`. """ - bit_field_size: Optional[int] + bit_field_size: Final[Optional[int]] """ Size in bits of this member if it is a bit field, ``None`` if it is not. @@ -1996,10 +2028,10 @@ class TypeEnumerator: :param value: :attr:`TypeEnumerator.value` """ ... - name: str + name: Final[str] "Enumerator name." - value: int + value: Final[int] "Enumerator value." def __len__(self) -> int: ... def __getitem__(self, idx: int) -> Any: ... @@ -2031,7 +2063,7 @@ class TypeParameter: :param name: :attr:`TypeParameter.name` """ ... - default_argument: Object + default_argument: Final[Object] """ Default argument for parameter. @@ -2045,14 +2077,14 @@ class TypeParameter: usually absent. """ - type: Type + type: Final[Type] """ Parameter type. This is the same as ``TypeParameter.default_argument.type_``. """ - name: Optional[str] + name: Final[Optional[str]] """Parameter name, or ``None`` if the parameter is unnamed.""" class TypeTemplateParameter: @@ -2084,7 +2116,7 @@ class TypeTemplateParameter: :param is_default: :attr:`TypeTemplateParameter.is_default` """ ... - argument: Union[Type, Object] + argument: Final[Union[Type, Object]] """ Template argument. @@ -2092,10 +2124,10 @@ class TypeTemplateParameter: is a non-type template parameter, then this is an :class:`Object`. """ - name: Optional[str] + name: Final[Optional[str]] """Template parameter name, or ``None`` if the parameter is unnamed.""" - is_default: bool + is_default: Final[bool] """ Whether :attr:`argument` is the default for the template parameter. @@ -2290,16 +2322,10 @@ def _linux_helper_direct_mapping_offset(prog: Program) -> int: ... def _linux_helper_read_vm( prog: Program, pgtable: Object, address: IntegerLike, size: IntegerLike ) -> bytes: ... -def _linux_helper_radix_tree_lookup(root: Object, index: IntegerLike) -> Object: - """ - Look up the entry at a given index in a radix tree. - - :param root: ``struct radix_tree_root *`` - :param index: Entry index. - :return: ``void *`` found entry, or ``NULL`` if not found. - """ - ... - +def _linux_helper_follow_phys( + prog: Program, pgtable: Object, address: IntegerLike +) -> int: ... +def _linux_helper_xa_load(xa: Object, index: IntegerLike) -> Object: ... def _linux_helper_per_cpu_ptr(ptr: Object, cpu: IntegerLike) -> Object: """ Return the per-CPU pointer for a given CPU. @@ -2316,6 +2342,18 @@ def _linux_helper_per_cpu_ptr(ptr: Object, cpu: IntegerLike) -> Object: """ ... +def _linux_helper_cpu_curr(prog: Program, cpu: IntegerLike) -> Object: + """ + Return the task running on the given CPU. + + >>> cpu_curr(prog, 7).comm + (char [16])"python3" + + :param cpu: CPU number. + :return: ``struct task_struct *`` + """ + ... + def _linux_helper_idle_task(prog: Program, cpu: IntegerLike) -> Object: """ Return the idle thread (PID 0, a.k.a swapper) for the given CPU. @@ -2328,16 +2366,15 @@ def _linux_helper_idle_task(prog: Program, cpu: IntegerLike) -> Object: """ ... -def _linux_helper_idr_find(idr: Object, id: IntegerLike) -> Object: +def _linux_helper_task_cpu(task: Object) -> int: """ - Look up the entry with the given ID in an IDR. + Return the CPU number that the given task last ran on. - :param idr: ``struct idr *`` - :param id: Entry ID. - :return: ``void *`` found entry, or ``NULL`` if not found. + :param task: ``struct task_struct *`` """ ... +def _linux_helper_idr_find(idr: Object, id: IntegerLike) -> Object: ... def _linux_helper_find_pid( prog_or_ns: Union[Program, Object], pid: IntegerLike ) -> Object: diff --git a/contrib/README.rst b/contrib/README.rst new file mode 100644 index 000000000..f3177ffbf --- /dev/null +++ b/contrib/README.rst @@ -0,0 +1,35 @@ +Community-Contributed Content +============================= + +This directory contains drgn scripts, libraries, and notes that have been +contributed by the community but aren't considered a part of drgn proper. Code +in this directory is not tested and not necessarily up to the rest of the +project's standards. + +This is intended as a central location to share drgn ideas with a low barrier +to entry. If you have time to polish your code, consider submitting it as a +proper helper or tool. If not, feel free to dump it here. Someone else might +find it useful as a starting point for their own investigation. It could even +be adapted into a helper or tool later. + +Contributing to ``contrib`` +--------------------------- + +The bar for contributing to ``contrib`` is intentionally low. Code submitted +here can be rough and will be only lightly reviewed. The only hard requirements +are: + +* It must be relevant to drgn. +* All files must have a comment or docstring at the top describing what they + are. This can be short. + +There are also some boring legal requirements: + +* All files must have a copyright notice. +* All files must be licensed under the LGPLv2.1+ (using + ``SPDX-License-Identifier: LGPL-2.1-or-later``). +* All commits must have a ``Signed-off-by`` trailer. See `Signing Off + <../CONTRIBUTING.rst#signing-off>`_. + +We may choose to edit, reorganize, or drop parts your contribution. If in +doubt, go ahead and open a pull request, and we'll decide what to do with it. diff --git a/contrib/btrfs_tree.py b/contrib/btrfs_tree.py new file mode 100644 index 000000000..630e8754c --- /dev/null +++ b/contrib/btrfs_tree.py @@ -0,0 +1,432 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +import enum +import struct +from typing import NamedTuple, Sequence +import uuid + +import drgn +from drgn import NULL, cast, offsetof +from drgn.helpers.linux.mm import page_to_virt +from drgn.helpers.linux.radixtree import radix_tree_lookup + +"""Helpers for introspecting btrfs btree structures""" + +page_address = page_to_virt + + +_crc32c_table = [0] * 256 +for i in range(256): + fwd = i + for j in range(8, 0, -1): + if fwd & 1: + fwd = (fwd >> 1) ^ 0x82f63b78 + else: + fwd >>= 1 + _crc32c_table[i] = fwd & 0xffffffff + + +def crc32c(b: bytes, crc: int = 0) -> int: + for c in b: + crc = (crc >> 8) ^ _crc32c_table[(crc ^ c) & 0xff] + return crc + + +def btrfs_name_hash(name: bytes) -> int: + return crc32c(name, 0xfffffffe) + + +_btrfs_disk_key_fmt = "> prog["PAGE_SHIFT"]).value_() + offset = offset_in_page(eb.start + start) + ret = [] + while len > 0: + cur = min(len, PAGE_SIZE - offset) + ret.append(prog.read(page_address(eb.pages[i]) + offset, cur)) + len -= cur + offset = 0 + i += 1 + return b"".join(ret) + + +def _btrfs_bin_search(eb, p, item_size, key: BtrfsKey): + low = 0 + high = btrfs_header_nritems(eb) + + while low < high: + mid = (low + high) // 2 + offset = p + mid * item_size + disk_objectid, disk_type, disk_offset = _btrfs_disk_key_struct.unpack(read_extent_buffer(eb, offset, 17)) + disk_key = BtrfsKey(disk_objectid, BtrfsType(disk_type), disk_offset) + if disk_key < key: + low = mid + 1 + elif disk_key > key: + high = mid + else: + return 0, mid + return 1, low + + + +def _get_block_for_search(fs_info, eb, level, slot): + blocknr = btrfs_node_blockptr(eb, slot) + + tmp = cast( + eb.type_, + radix_tree_lookup(fs_info.buffer_radix.address_of_(), + blocknr >> fs_info.sectorsize_bits) + ) + if not tmp: + raise Exception(f"extent_buffer {blocknr} is not cached") + if tmp.refs.counter == 0: + raise Exception(f"extent_buffer {blocknr} is dead") + if not tmp.bflags & (1 << fs_info.prog_["EXTENT_BUFFER_UPTODATE"]): + raise Exception(f"extent_buffer {blocknr} is not up to date") + # TODO: check transid, level? + return tmp + + +def btrfs_search_slot(root, key: BtrfsKey, *, search_commit_root: bool = False): + prog = root.prog_ + fs_info = root.fs_info.read_() + + nodes = [] + slots = [] + prev_cmp = -1 + if search_commit_root: + b = root.commit_root.read_() + else: + b = root.node.read_() + try: + while True: + nodes.append(b) + level = btrfs_header_level(b) + + if prev_cmp == 0: + slot = 0 + ret = 0 + else: + if level == 0: + ret, slot = _btrfs_bin_search(b, _btrfs_leaf_items_offset, _btrfs_item_size, key) + else: + ret, slot = _btrfs_bin_search(b, _btrfs_node_ptrs_offset, _btrfs_key_ptr_size, key) + prev_cmp = ret + + if level == 0: + slots.append(slot) + break + + if ret and slot > 0: + slot -= 1 + slots.append(slot) + + b = _get_block_for_search(fs_info, b, level, slot) + except Exception as e: + print(e) + ret = -1 + nodes.reverse() + slots.reverse() + return ret, nodes, slots + + +def btrfs_leaf_items(eb): + if btrfs_header_level(eb) != 0: + raise ValueError("buffer is not leaf") + nritems = btrfs_header_nritems(eb) + buf = read_extent_buffer(eb, _btrfs_leaf_items_offset, nritems * _btrfs_item_size) + items = [] + for i in range(nritems): + raw_item = _btrfs_item_struct.unpack_from(buf, i * _btrfs_item_size) + items.append( + BtrfsItem(BtrfsKey(raw_item[0], raw_item[1], raw_item[2]), raw_item[3], raw_item[4]) + ) + return items + + +def _parse_item_from_buf(buf, type, offset): + while type.kind == drgn.TypeKind.TYPEDEF: + type = type.type + if type.kind == drgn.TypeKind.INT: + return int.from_bytes(buf[offset:offset + type.size], "little") + elif type.kind == drgn.TypeKind.STRUCT: + return { + member.name: _parse_item_from_buf(buf, member.type, offset + member.offset) + for member in type.members + } + elif type.kind == drgn.TypeKind.ARRAY: + element_type = type.type + element_size = sizeof(element_type) + return [ + _parse_item_from_buf(buf, element_type, offset + i * element_size) + for i in range(type.length) + ] + else: + assert False, type.kind + + +def btrfs_read_item(eb, slot): + if btrfs_header_level(eb) != 0: + raise ValueError("buffer is not leaf") + if slot >= btrfs_header_nritems(eb): + raise IndexError("slot is out of bounds") + + item_buf = read_extent_buffer(eb, _btrfs_leaf_items_offset + slot * _btrfs_item_size, _btrfs_item_size) + objectid, type, offset, data_offset, data_size = _btrfs_item_struct.unpack(item_buf) + + key = BtrfsKey(objectid, BtrfsType(type), offset) + + data_buf = read_extent_buffer(eb, _btrfs_leaf_items_offset + data_offset, data_size) + try: + type_name = _btrfs_types[key.type] + except KeyError: + return key, data_buf + + item_type = prog.type(type_name) + data = _parse_item_from_buf(data_buf, item_type, 0) + pos = sizeof(item_type) + if type_name in _btrfs_types_with_name: + data["name"] = data_buf[pos:pos + data["name_len"]] + pos += data["name_len"] + if type_name == "struct btrfs_dir_item": + data["data"] = data_buf[pos:pos + data["data_len"]] + pos += data["data_len"] + return key, data + + +def parse_extent_buffer(eb): + header = BtrfsHeader.from_bytes(read_extent_buffer(eb, 0, BtrfsHeader.sizeof())) + if header.level == 0: + assert False + else: + ptrs_buf = read_extent_buffer(eb, BtrfsHeader.sizeof(), header.nritems * BtrfsKeyPtr.sizeof()) + return BtrfsNode( + header, + [ + BtrfsKeyPtr.from_bytes(ptrs_buf, i * BtrfsKeyPtr.sizeof()) + for i in range(header.nritems) + ] + ) + + +def print_extent_buffer(eb): + node = parse_extent_buffer(eb) + print(f"node {node.header.bytenr} level {node.header.level} items {node.header.nritems} generation {node.header.generation} owner {node.header.owner}") + print(f"node {node.header.bytenr} flags {node.header.flags:#x}") + print(f"fs uuid {uuid.UUID(bytes=node.header.fsid)}") + print(f"chunk uuid {uuid.UUID(bytes=node.header.chunk_tree_uuid)}") + for i, ptr in enumerate(node.ptrs): + print(f"\tptr {i} key ({ptr.key.objectid}, {ptr.key.type}, {ptr.key.offset}) block {ptr.blockptr} gen {ptr.generation}") diff --git a/contrib/btrfs_tree_mod_log.py b/contrib/btrfs_tree_mod_log.py new file mode 100644 index 000000000..fd8de9b9b --- /dev/null +++ b/contrib/btrfs_tree_mod_log.py @@ -0,0 +1,77 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +import drgn +from drgn import NULL, Object, cast, container_of, execscript, offsetof, reinterpret, sizeof +from drgn.helpers.common import * +from drgn.helpers.linux import * +import collections +import sys + +"""Btrfs Tree Mod Log rewind simulator""" + +# you can get a tree mod log from fs_info.tree_mod_log + +# search for tree mod log entries for the given offset and minimum sequence +# returns a sorted list of matching entries. +def tree_mod_log_search(tml, start, min_seq): + es = collections.defaultdict(list) + for e in rbtree_inorder_for_each_entry("struct tree_mod_elem", tml, "node"): + es[int(e.logical)].append((int(e.seq), e)) + return [p[1] for p in sorted(es[start]) if p[0] >= min_seq] + +# apply the tree mod log entries returned by tree_mod_log_search +# in reverse to a model of a blank extent_buffer. Pay extra attention +# to a particular slot. +def tree_mod_log_rewind(tmes, my_slot): + eb_rewind = {} + for tme in reversed(tmes): + print(tme) + op = int(tme.op) + slot = int(tme.slot) + # replace + if op == 0: + if slot == my_slot: + print(f"writing {tme} into {my_slot}!") + eb_rewind[slot] = (tme.blockptr, tme.generation, tme.key) + # add + if op == 1: + if slot == my_slot: + print(f"nuking {my_slot}!") + del(eb_rewind[slot]) + # remove + if op in [2,3,4]: + if slot == my_slot: + print(f"writing {tme} into {my_slot}!") + eb_rewind[slot] = (int(tme.blockptr), int(tme.generation), tme.key) + # move + if op == 5: + src_slot = int(tme.move.dst_slot) + nr = int(tme.move.nr_items) + off = 0 + for src in range(src_slot, src_slot+nr): + if src in eb_rewind: + if slot + off == my_slot: + print(f"moving {eb_rewind[src]} into {my_slot}!") + eb_rewind[slot + off] = eb_rewind[src] + else: + if slot + off == my_slot: + print(f"moving garbage into {my_slot}!") + eb_rewind[slot + off] = (0,0,(0,0,0)) + off += 1 + return eb_rewind + +# compare the slots of a real eb and a rewound eb +# parsed_eb is the output of 'parse_extent_buffer' in btrfs.py +def diff_eb_rewind(parsed_eb, eb_rewind): + ebptrs = parsed_eb.ptrs + mismatch = False + for i, ptr in enumerate(ebptrs): + if i in eb_rewind: + if ptr.blockptr != eb_rewind[i][0]: + mismatch = True + print(f"EB {i}: {ptr.blockptr} EB_REWIND {i}: {eb_rewind[i][0]}") + elif ptr.blockptr != 0 and ptr.blockptr < 1 << 41: + mismatch = True + print(f"EB ONLY {i}: {ptr.blockptr}") + return mismatch diff --git a/examples/linux/cgroup.py b/contrib/cgroup.py similarity index 97% rename from examples/linux/cgroup.py rename to contrib/cgroup.py index c0b1b1887..24e4a9e37 100755 --- a/examples/linux/cgroup.py +++ b/contrib/cgroup.py @@ -1,6 +1,6 @@ #!/usr/bin/env drgn # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """List the paths of all descendants of a cgroup v2""" diff --git a/contrib/dump_btrfs_bgs.py b/contrib/dump_btrfs_bgs.py new file mode 100755 index 000000000..2c75dc105 --- /dev/null +++ b/contrib/dump_btrfs_bgs.py @@ -0,0 +1,76 @@ +#!/usr/bin/env drgn +# Copyright (c) Western Digital Corporation, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +""" Dump all block group caches for a given btrfs file-system """ + +import sys +import drgn +from enum import Flag +from drgn import NULL, Object, cast, container_of, execscript, \ + reinterpret, sizeof +from drgn.helpers.linux import * +from drgn.helpers.common import decode_flags + +BTRFS_BLOCK_GROUP_FLAGS = [ + ("BTRFS_BLOCK_GROUP_DATA", 0), + ("BTRFS_BLOCK_GROUP_SYSTEM", 1), + ("BTRFS_BLOCK_GROUP_METADATA", 2), + ("BTRFS_BLOCK_GROUP_RAID0", 3), + ("BTRFS_BLOCK_GROUP_RAID1", 4), + ("BTRFS_BLOCK_GROUP_DUP", 5), + ("BTRFS_BLOCK_GROUP_RAID10", 6), + ("BTRFS_BLOCK_GROUP_RAID5", 9), + ("BTRFS_BLOCK_GROUP_RAID6", 8), + ("BTRFS_BLOCK_GROUP_RAID1C3", 9), + ("BTRFS_BLOCK_GROUP_RAID1C4", 10) + ] + +BTRFS_BLOCK_GROUP_RUNTIME_FLAGS = [ + ("BLOCK_GROUP_FLAG_IREF", 0), + ("BLOCK_GROUP_FLAG_REMOVED", 1), + ("BLOCK_GROUP_FLAG_TO_COPY", 2), + ("BLOCK_GROUP_FLAG_RELOCATING_REPAIR", 3), + ("BLOCK_GROUP_FLAG_CHUNK_ITEM_INSERTED", 4), + ("BLOCK_GROUP_FLAG_ZONE_IS_ACTIVE", 5), + ("BLOCK_GROUP_FLAG_ZONED_DATA_RELOC", 6), + ("BLOCK_GROUP_FLAG_NEEDS_FREE_SPACE", 7), + ("BLOCK_GROUP_FLAG_SEQUENTIAL_ZONE", 8) + ] + +if len(sys.argv) > 1: + mnt_path = sys.argv[1] + mnt_path = mnt_path.rstrip('/') +else: + mnt_path = "/" + +mnt = None + +for mnt in for_each_mount(prog, dst = mnt_path): + pass + +if mnt is None: + sys.stderr.write(f'Error: mount point {mnt_path} not found') + sys.exit(1) + +try: + fs_info = cast('struct btrfs_fs_info *', mnt.mnt.mnt_sb.s_fs_info) +except LookupError: + print('cannot find \'struct btrfs_fs_info *\', module not loaded?') + sys.exit(1) + +def dump_bg(bg): + print(f'BG at {bg.start.value_()}') + print(f'\tflags: {decode_flags(bg.flags.value_(), BTRFS_BLOCK_GROUP_FLAGS)} ({hex(bg.flags)})') + print(f'\tlength: {bg.length.value_()}') + print(f'\tused: {bg.used.value_()}') + print(f'\tpinned: {bg.pinned.value_()}') + print(f'\treserved: {bg.reserved.value_()}') + print(f'\truntime_flags: {decode_flags(bg.runtime_flags.value_(), BTRFS_BLOCK_GROUP_RUNTIME_FLAGS)} ({hex(bg.runtime_flags)})') + if bg.fs_info.zone_size.value_() > 0: + print(f'\tzone_unsuable: {bg.zone_unusable.value_()}') + print() + +for bg in rbtree_inorder_for_each_entry("struct btrfs_block_group",\ + fs_info.block_group_cache_tree.rb_root, "cache_node"): + dump_bg(bg) diff --git a/examples/linux/fs_inodes.py b/contrib/fs_inodes.py similarity index 89% rename from examples/linux/fs_inodes.py rename to contrib/fs_inodes.py index 0d9252b1b..5c34bf28d 100755 --- a/examples/linux/fs_inodes.py +++ b/contrib/fs_inodes.py @@ -1,6 +1,6 @@ #!/usr/bin/env drgn # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """List the paths of all inodes cached in a given filesystem""" @@ -28,5 +28,5 @@ ): try: print(os.fsdecode(inode_path(inode))) - except ValueError: + except (TypeError, ValueError): continue diff --git a/contrib/kcore_list.py b/contrib/kcore_list.py new file mode 100755 index 000000000..123767df3 --- /dev/null +++ b/contrib/kcore_list.py @@ -0,0 +1,16 @@ +#!/usr/bin/env drgn +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +"""Dump the list of memory regions exposed by /proc/kcore.""" + +from drgn import cast +from drgn.helpers.linux.list import list_for_each_entry + +kcore_type = prog.type("enum kcore_type") +for entry in list_for_each_entry( + "struct kcore_list", prog["kclist_head"].address_of_(), "list" +): + print( + f"{cast(kcore_type, entry.type).format_(type_name=False)} {hex(entry.addr)} {hex(entry.size)}" + ) diff --git a/contrib/kernel_sys.py b/contrib/kernel_sys.py new file mode 100755 index 000000000..88c4c2e0a --- /dev/null +++ b/contrib/kernel_sys.py @@ -0,0 +1,39 @@ +#!/usr/bin/env drgn +# Copyright (c) SUSE Linux. +# SPDX-License-Identifier: LGPL-2.1-or-later + +"""Display system information and configuration data.""" + +from datetime import datetime +from datetime import timedelta + +from drgn.helpers.common.format import number_in_binary_units +from drgn.helpers.linux import for_each_online_cpu +from drgn.helpers.linux.mm import totalram_pages +from drgn.helpers.linux.pid import for_each_task +from drgn.helpers.linux.sched import loadavg + + +def print_line(key, value): + print(f"{key:<16} {value}") + + +uts = prog["init_uts_ns"].name + +timekeeper = prog["shadow_timekeeper"] +date = datetime.fromtimestamp(timekeeper.xtime_sec).strftime("%c") +uptime = timedelta(seconds=timekeeper.ktime_sec.value_()) +load = ", ".join([f"{v:.2f}" for v in loadavg(prog)]) +totalram = (prog['PAGE_SIZE'] * totalram_pages(prog)).value_() + + +print_line("CPUS", len(list(for_each_online_cpu(prog)))) +print_line("DATE", date) +print_line("UPTIME", uptime) +print_line("LOAD AVERAGE", load) +print_line("TASKS", len(list(for_each_task(prog)))) +print_line("NODENAME", uts.nodename.string_().decode()) +print_line("RELEASE", uts.release.string_().decode()) +print_line("VERSION", uts.version.string_().decode()) +print_line("MACHINE", uts.machine.string_().decode()) +print_line("MEMORY", number_in_binary_units(totalram)) diff --git a/contrib/lsmod.py b/contrib/lsmod.py new file mode 100755 index 000000000..87c40b83c --- /dev/null +++ b/contrib/lsmod.py @@ -0,0 +1,29 @@ +#!/usr/bin/env drgn +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +"""An implementation of lsmod(8) using drgn""" + +from drgn.helpers.linux.list import list_for_each_entry + +print("Module Size Used by") +config_module_unload = prog.type("struct module").has_member("refcnt") +for mod in list_for_each_entry("struct module", prog["modules"].address_of_(), "list"): + name = mod.name.string_().decode() + size = (mod.init_layout.size + mod.core_layout.size).value_() + if config_module_unload: + refcnt = mod.refcnt.counter.value_() - 1 + used_by = [ + use.source.name.string_().decode() + for use in list_for_each_entry( + "struct module_use", mod.source_list.address_of_(), "source_list" + ) + ] + else: + refcnt = "-" + used_by = [] + + used = ",".join(used_by) + if used: + used = " " + used + print(f"{name:19} {size:>8} {refcnt}{used}") diff --git a/contrib/mount.py b/contrib/mount.py new file mode 100755 index 000000000..222dd9568 --- /dev/null +++ b/contrib/mount.py @@ -0,0 +1,16 @@ +#!/usr/bin/env drgn +# Copyright (c) SUSE Linux. +# SPDX-License-Identifier: LGPL-2.1-or-later + +"""A simplified implementation of mount(1) using drgn""" + +from drgn.helpers.linux.fs import for_each_mount, mount_dst, mount_fstype, mount_src + +print("Mount Type Devname Dirname") +for mount in for_each_mount(prog): + maddr = mount.value_() + src = mount_src(mount).decode() + dst = mount_dst(mount).decode() + type_ = mount_fstype(mount).decode() + + print(f"{maddr:<16x} {type_:<12} {src:<12} {dst}") diff --git a/contrib/platform_drivers.py b/contrib/platform_drivers.py new file mode 100644 index 000000000..9357c19c4 --- /dev/null +++ b/contrib/platform_drivers.py @@ -0,0 +1,28 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +"""Print registered platform drivers.""" + +from drgn import NULL, container_of +from drgn.helpers.linux.list import list_for_each_entry + + +def bus_to_subsys(bus): + for sp in list_for_each_entry( + "struct subsys_private", + prog["bus_kset"].list.address_of_(), + "subsys.kobj.entry", + ): + if sp.bus == bus: + return sp + return NULL(bus.prog_, "struct subsys_private *") + + +sp = bus_to_subsys(prog["platform_bus_type"].address_of_()) +for priv in list_for_each_entry( + "struct driver_private", sp.drivers_kset.list.address_of_(), "kobj.entry" +): + driver = priv.driver + print(driver.name.string_().decode()) + platform_driver = container_of(driver, "struct platform_driver", "driver") + print(platform_driver) diff --git a/contrib/ps.py b/contrib/ps.py new file mode 100755 index 000000000..4def9f41a --- /dev/null +++ b/contrib/ps.py @@ -0,0 +1,303 @@ +#!/usr/bin/env drgn +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +"""An implementation of ps(1) using drgn""" + +import sys +from argparse import ArgumentParser +from collections import OrderedDict + +from drgn import ProgramFlags +from drgn.helpers.linux.cpumask import for_each_online_cpu +from drgn.helpers.linux.list import list_for_each_entry, list_count_nodes +from drgn.helpers.linux.mm import cmdline, totalram_pages +from drgn.helpers.linux.percpu import per_cpu, percpu_counter_sum +from drgn.helpers.linux.pid import for_each_task +from drgn.helpers.linux.sched import task_cpu, task_state_to_char + +PAGE_SIZE = prog["PAGE_SIZE"].value_() + + +def get_number_of_children(task): + """ + Returns number of children of a given task + """ + return list_count_nodes(task.children.address_of_()) + + +def get_cmd(task): + """ + Return the commandline arguments of a given task + """ + return b" ".join(cmdline(task)).decode() + + +def parse_cmdline_args(args): + """ + Command line argument parser + """ + parser = ArgumentParser(prog="drgn ps", + description="Report process status infromation") + group = parser.add_mutually_exclusive_group() + + group.add_argument("-a", "--active", action="store_true", default=False, + help="Print active thread on each CPU " + "(data may be inconsistent for live kernel)") + + group.add_argument("-c", "--children", nargs="*", default=None, type=int, + help="Print data about children of the given process(es)") + + group.add_argument("--cpus", nargs="*", default=None, type=int, + help="Ready/running processes for given CPUs") + + parser.add_argument("-d", "--detailed", action="store_true", default=False, + help="Print additional details about the threads") + + group.add_argument("--hierarchy", metavar="PID", nargs="+", default=None, + help="Print parent hierarchy") + + group.add_argument("-k", "--kthread", action='store_true', default=False, + help="Print kernel threads only") + + group.add_argument("-t", "--threads", nargs="+", default=None, type=int, + help="Print detailed information of a given threads") + + group.add_argument("-u", "--uthread", action="store_true", default=False, + help="Print userspace threads only") + + cmd_opts = parser.parse_args(args) + + return cmd_opts + + +def get_rss(task): + """ + Returns the Resident Set Size + """ + try: + return PAGE_SIZE * sum([percpu_counter_sum(x) for x in task.mm.rss_stat]) + except (AttributeError, TypeError): + return PAGE_SIZE * sum([x.counter for x in task.mm.rss_stat.count]) + + +def print_active_tasks(cmd_opts): + """ + Function to print active task on each CPU + """ + if prog.flags & ProgramFlags.IS_LIVE: + print("Running on live kernel - The data may be inconsistent\n") + + runqueues = prog["runqueues"] + + print_hdr = True + for cpu in for_each_online_cpu(prog): + task = per_cpu(runqueues, cpu).curr + print_std(task, print_hdr, cmd_opts) + print_hdr = False + + +def print_cpu_tasks(cmd_opts): + """ + Print running and runnable tasks on a given CPU + """ + for cpu in for_each_online_cpu(prog): + if cmd_opts.cpus: + if cpu not in cmd_opts.cpus: + continue + print_hdr = True + for task in for_each_task(prog): + if task_cpu(task) == cpu: + print_std(task, print_hdr, cmd_opts) + print_hdr = False + + +def hierarchy(cmd_opts): + """ + Print information of all parent processes + """ + pids = cmd_opts.hierarchy + tasks = [] + for task in for_each_task(prog): + if str(task.pid.value_()) not in pids: + continue + + pid = task.pid.value_() + pids.remove(str(pid)) + while (pid > 1): + pid = task.pid.value_() + tasks.append(task) + task = task.parent + + print_hdr = True + while len(tasks) != 0: + print_std(tasks.pop(), print_hdr, cmd_opts) + print_hdr = False + + print("\n") + + if len(pids) != 0: + print("the following pids are invalid: {0}".format(pids)) + + +def thread_details(cmd_opts): + """ + Prints details of all the threads including kernel and userspace both. + """ + print_hdr = True + for task in for_each_task(prog): + if cmd_opts.threads: + if task.pid.value_() in cmd_opts.threads: + cmd_opts.kthread = 0 + cmd_opts.uthread = 0 + else: + continue + if cmd_opts.kthread: + if task.mm: + continue + elif cmd_opts.uthread: + if not task.mm: + continue + print_std(task, print_hdr, cmd_opts) + print_hdr = False + + +def process_child_task(cmd_opts): + """ + Print all child tasks of the given parent tasks + """ + for task in for_each_task(prog): + if cmd_opts.children: + if task.pid not in cmd_opts.children: + continue + + print("Parent Task:") + print_hdr = True + print_std(task, print_hdr, cmd_opts) + print("Child Tasks:") + head = task.children.address_of_() + + for c_task in list_for_each_entry("struct task_struct", head, "sibling"): + print_std(c_task, print_hdr, cmd_opts) + print_hdr = False + + # No child found + if print_hdr: + print("NA") + print("\n") + + +def get_task_memory_info(task): + """ + Return RSS (Resident Set Size) memory and VMS (Virtual Memory Size) + for a given task. Return None if the task is a kernel thread. + """ + if not task.mm: + return None + + vms = PAGE_SIZE * task.mm.total_vm.value_() + + # Since Linux kernel commit f1a7941243c102a44e ("mm: convert mm's rss + # stats into percpu_counter") (in v6.2), rss_stat is percpu counter. + try: + rss = PAGE_SIZE * sum([percpu_counter_sum(x) for x in task.mm.rss_stat]) + except (AttributeError, TypeError): + rss = PAGE_SIZE * sum([x.counter for x in task.mm.rss_stat.count]) + + return (vms, rss) + + +""" +The headers_and_vals dictionary's entries follow: + key: (val, ) +""" +headers_and_vals = OrderedDict([ + ("PID", (lambda task: task.pid.value_(), 7)), + ("PPID", (lambda task: task.parent.pid.value_(), 7)), + ("CPU", (task_cpu, 4)), + ("Task Address", (hex, 19)), + ("Stack Address", (lambda task: hex(task.stack.value_()), 19)), + ("State", (task_state_to_char, 4)), + ("VMS", (lambda task: PAGE_SIZE * task.mm.total_vm.value_(), 10)), + ("RSS", (get_rss, 10)), + ("MEM%", (lambda task: (round(((get_rss(task) * 100) / + (PAGE_SIZE * totalram_pages(prog))), 4)), 8)), + ("comm", (lambda task: task.comm.string_().decode(), 9)) +]) + + +""" +No need to worry about specific spacing here +""" +headers_and_vals_detailed = OrderedDict([ + ("Execution Time (sec)", lambda task: (task.utime + task.stime).value_()/1e9), + ("mm_struct addr", lambda task: hex(task.mm.value_())), + ("cmdline", get_cmd), + ("Number of children", get_number_of_children), + ("Stack Trace", lambda task: '\n' + str(prog.stack_trace(task))) +]) + + +def print_std(task, need_hdr, cmd_opts): + """ + The print function is responsible for generating reports + """ + data_points = [] + detailed = cmd_opts.detailed + if detailed: + need_hdr = True + + for header in headers_and_vals.keys(): + try: + data = (headers_and_vals[header])[0](task) + data_points.append(data) + except Exception: + data_points.append('NA') + + # Print the headers + header_line = "" + if need_hdr: + for header in headers_and_vals.keys(): + width = headers_and_vals[header][1] + header_line += ''.join(format(str(header), f"<{width+2}")) + + print(header_line) + print("-" * len(header_line)) + + # Print the data rows + formatted_row = "" + index = 0 + for header in headers_and_vals.keys(): + width = headers_and_vals[header][1] + formatted_row += ''.join(format(str(data_points[index]), f"<{width+2}")) + index += 1 + + print(formatted_row + "\n") + + if detailed: + for header in headers_and_vals_detailed.keys(): + try: + data = headers_and_vals_detailed[header](task) + except Exception: + data = 'NA' + + print(header, ":", data) + + +def main(): + cmd_opts = parse_cmdline_args(sys.argv[1:]) + + if cmd_opts.active: + print_active_tasks(cmd_opts) + elif isinstance(cmd_opts.children, list): + process_child_task(cmd_opts) + elif isinstance(cmd_opts.cpus, list): + print_cpu_tasks(cmd_opts) + elif cmd_opts.hierarchy: + hierarchy(cmd_opts) + else: + thread_details(cmd_opts) + + +if __name__ == "__main__": + main() diff --git a/contrib/ptdrgn.py b/contrib/ptdrgn.py new file mode 100644 index 000000000..1e31f7483 --- /dev/null +++ b/contrib/ptdrgn.py @@ -0,0 +1,210 @@ +#!/usr/bin/python3 +# Copyright (c) 2023, Oracle and/or its affiliates. +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later +""" +Drgn CLI, but using ptpython rather than the standard code.interact() + +NOTE: this is definitely a bit of a hack, using implementation details of Drgn +*and* ptpython. It may break at any time, but it is also quite useful, and this +makes it worth sharing. + +Requires: "pip install ptpython" which brings in pygments and prompt_toolkit +""" +import functools +import importlib +import os +import shutil +import sys +from typing import Any, Callable, Dict, Optional, Set + +from prompt_toolkit.completion import Completion, Completer +from prompt_toolkit.formatted_text import PygmentsTokens +from prompt_toolkit.formatted_text import fragment_list_to_text, to_formatted_text +from ptpython import embed +from ptpython.completer import DictionaryCompleter +from ptpython.repl import run_config +from pygments.lexers.c_cpp import CLexer + +import drgn +import drgn.cli + + +class DummyForRepr: + """ + A dummy class to pass back to _format_result_output() that pretends to have + the given repr() + """ + + def __init__(self, s): + self.s = s + + def __repr__(self): + return self.s + + +class DummyForPtRepr: + """A similar dummy class for the __pt_repr__() method.""" + + def __init__(self, s): + self.s = s + + def __pt_repr__(self): + return self.s + + +def _maybe_c_format(s): + """Given a string, try to use pygments to highlight it it as a C string.""" + try: + tokens = CLexer().get_tokens_unprocessed(s) + formatted = PygmentsTokens([(tokentype, value) for index, tokentype, value in tokens]) + to_format = DummyForPtRepr(formatted) + except Exception as e: + to_format = DummyForRepr(s) + return to_format + + +@functools.lru_cache(maxsize=1) +def _object_fields() -> Set[str]: + return set(dir(drgn.Object)) + + +class ReorderDrgnObjectCompleter(Completer): + """A completer which puts Object member fields above Object defaults""" + + def __init__(self, c: Completer): + self.c = c + + def get_completions(self, document, complete_event): + completions = list(self.c.get_completions(document, complete_event)) + if not completions: + return completions + text = completions[0].text + member_fields = [] + # If the first completion is "absent_", it is *very likely* that we are + # now looking at the completion of on Object. Move the default Object + # attributes to the end of the list so that we get the struct attributes + if text == "absent_": + fields = _object_fields() + for i in reversed(range(len(completions))): + text = completions[i].text + if text not in fields: + member_fields.append(completions[i]) + del completions[i] + return list(reversed(member_fields)) + completions + return completions + + +def configure(repl) -> None: + """ + Muck around with the internals of PythonRepl so that we will special case the + drgn data structures, similar to how drgn messes with sys.displayhook. We can + do C syntax highlighting too, which is really nice. + + This also automatically runs the default config file: + ~/.config/ptpython/config.py + """ + _format_result_output_orig = repl._format_result_output + + def _format_result_output(result: object): + if isinstance(result, drgn.Object): + s = result.format_(columns=shutil.get_terminal_size((0, 0)).columns) + to_format = _maybe_c_format(s) + elif isinstance(result, (drgn.StackFrame, drgn.StackTrace)): + to_format = DummyForRepr(str(result)) + elif isinstance(result, drgn.Type): + to_format = _maybe_c_format(str(result)) + else: + to_format = result + return _format_result_output_orig(to_format) + + repl._format_result_output = _format_result_output + run_config(repl) + repl._completer = ReorderDrgnObjectCompleter(repl._completer) + repl.completer = ReorderDrgnObjectCompleter(repl.completer) + + +def run_interactive( + prog: drgn.Program, + banner_func: Optional[Callable[[str], str]] = None, + globals_func: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None, + quiet: bool = False, +) -> None: + """ + Run drgn's :ref:`interactive-mode` via ptpython + + :param prog: Pre-configured program to run against. Available as a global + named ``prog`` in the CLI. + :param banner_func: Optional function to modify the printed banner. Called + with the default banner, and must return a string to use as the new + banner. The default banner does not include the drgn version, which can + be retrieved via :func:`version_header()`. + :param globals_func: Optional function to modify globals provided to the + session. Called with a dictionary of default globals, and must return a + dictionary to use instead. + :param quiet: Whether to suppress non-fatal warnings. + """ + init_globals: Dict[str, Any] = { + "prog": prog, + "drgn": drgn, + "__name__": "__main__", + "__doc__": None, + } + drgn_globals = [ + "NULL", + "Object", + "cast", + "container_of", + "execscript", + "offsetof", + "reinterpret", + "sizeof", + ] + for attr in drgn_globals: + init_globals[attr] = getattr(drgn, attr) + + banner = f"""\ +For help, type help(drgn). +>>> import drgn +>>> from drgn import {", ".join(drgn_globals)} +>>> from drgn.helpers.common import *""" + + module = importlib.import_module("drgn.helpers.common") + for name in module.__dict__["__all__"]: + init_globals[name] = getattr(module, name) + if prog.flags & drgn.ProgramFlags.IS_LINUX_KERNEL: + banner += "\n>>> from drgn.helpers.linux import *" + module = importlib.import_module("drgn.helpers.linux") + for name in module.__dict__["__all__"]: + init_globals[name] = getattr(module, name) + + if banner_func: + banner = banner_func(banner) + if globals_func: + init_globals = globals_func(init_globals) + + old_path = list(sys.path) + # The ptpython history file format is different from a standard readline + # history file since it must handle multi-line input, and it includes some + # metadata as well. Use a separate history format, even though it would be + # nice to share. + histfile = os.path.expanduser("~/.drgn_history.ptpython") + try: + sys.path.insert(0, "") + + print(banner) + embed( + globals=init_globals, + history_filename=histfile, + title="drgn", + configure=configure, + ) + finally: + sys.path[:] = old_path + + +if __name__ == "__main__": + # Muck around with the internals of drgn: swap out run_interactive() with our + # ptpython version, and then call main as if nothing happened. + drgn.cli.run_interactive = run_interactive + drgn.cli._main() diff --git a/examples/linux/tcp_sock.py b/contrib/tcp_sock.py similarity index 76% rename from examples/linux/tcp_sock.py rename to contrib/tcp_sock.py index d2372c1e0..fc7a857f0 100755 --- a/examples/linux/tcp_sock.py +++ b/contrib/tcp_sock.py @@ -1,6 +1,6 @@ #!/usr/bin/env drgn # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """List all TCP sockets and their cgroup v2 paths""" @@ -8,12 +8,12 @@ import socket import struct -from drgn import cast, container_of +from drgn import cast from drgn.helpers.common.type import enum_type_to_class from drgn.helpers.linux import ( cgroup_path, - hlist_for_each, hlist_nulls_empty, + hlist_nulls_for_each_entry, sk_fullsock, sk_nulls_for_each, sk_tcpstate, @@ -92,10 +92,24 @@ def _print_sk(sk): tcp_hashinfo = prog.object("tcp_hashinfo") # 1. Iterate over all TCP sockets in TCP_LISTEN state. -for ilb in tcp_hashinfo.listening_hash: - for pos in hlist_for_each(ilb.head): - sk = container_of(pos, "struct sock", "__sk_common.skc_node") - _print_sk(sk) + +# Since Linux kernel commit cae3873c5b3a ("net: inet: Retire port only +# listening_hash") (in v5.19), listening_hash is removed and we need +# to iterate lhash2 table. + +try: + for ilb in tcp_hashinfo.listening_hash: + for sk in hlist_nulls_for_each_entry( + "struct sock", ilb.nulls_head, "__sk_common.skc_node" + ): + _print_sk(sk) +except AttributeError: + for i in range(tcp_hashinfo.lhash2_mask + 1): + head = tcp_hashinfo.lhash2[i].nulls_head + if hlist_nulls_empty(head): + continue + for sk in sk_nulls_for_each(head): + _print_sk(sk) # 2. And all other TCP sockets. for i in range(tcp_hashinfo.ehash_mask + 1): diff --git a/contrib/vmmap.py b/contrib/vmmap.py new file mode 100755 index 000000000..ed3418747 --- /dev/null +++ b/contrib/vmmap.py @@ -0,0 +1,57 @@ +#!/usr/bin/env drgn +# Copyright (c) SUSE Linux. +# SPDX-License-Identifier: LGPL-2.1-or-later + +"""Print memory map of a given task.""" + +import os +import sys + +from drgn.helpers.linux.device import MAJOR, MINOR +from drgn.helpers.linux.fs import d_path +from drgn.helpers.linux.pid import find_task + +if len(sys.argv) != 2: + sys.exit("Usage: ./vmmap.py PID") +pid = int(sys.argv[1]) + +task = find_task(prog, int(pid)) +if not task: + sys.exit(f"Cannot find task {pid}") + +try: + vma = task.mm.mmap +except AttributeError: + sys.exit('maple tree VMA mmap is not supported yet (v6.1+)') + +FLAGS = ((0x1, "r"), (0x2, "w"), (0x4, "x")) +PAGE_SHIFT = prog["PAGE_SHIFT"] + +print("Start End Flgs Offset Dev Inode File path") + +# Starting with 763ecb035029f500d7e6d ("mm: remove the vma linked list") (in v6.1), +# the VMA mmap linked list is replaced with maple tree which is not supported right now: +# https://github.com/osandov/drgn/issues/261 + +while vma: + flags = "".join([v if f & vma.vm_flags else "-" for f, v in FLAGS]) + flags += "s" if vma.vm_flags & 0x8 else "p" + print(f"{vma.vm_start.value_():0x}-{vma.vm_end.value_():0x} {flags} ", + end="") + + vmfile = vma.vm_file + if vmfile: + inode = vmfile.f_inode.i_ino.value_() + dev = vmfile.f_inode.i_sb.s_dev + major, minor = MAJOR(dev), MINOR(dev) + path = os.fsdecode(d_path(vmfile.f_path)) + pgoff = (vma.vm_pgoff << PAGE_SHIFT).value_() + else: + inode = 0 + major, minor = 0, 0 + path = "" + pgoff = 0 + + print(f"{pgoff:08x} {major:02x}:{minor:02x} {inode:<16} {path}") + + vma = vma.vm_next diff --git a/contrib/vmstat.py b/contrib/vmstat.py new file mode 100755 index 000000000..77ec3fa25 --- /dev/null +++ b/contrib/vmstat.py @@ -0,0 +1,51 @@ +#!/usr/bin/env drgn +# Copyright (c) SUSE Linux. +# SPDX-License-Identifier: LGPL-2.1-or-later + +"""Dump /proc/vmstat statistics.""" + +from drgn.helpers.linux.cpumask import for_each_online_cpu +from drgn.helpers.linux.percpu import per_cpu + + +def print_event_line(event, counter): + print(f"{event.name:<36} {counter.value_():>16}") + + +print(f"{'Event':<36} {'Count':>16}") + +# For all of the below, we skip the last enumerator item as it holds the number +# of enumerators. + +# 1) vm_zone_stat statistics are there since v4.8. +if "vm_zone_stat" in prog: + print("VM_ZONE_STAT:") + vm_zone_stat = prog["vm_zone_stat"] + for event in prog.type("enum zone_stat_item").enumerators[:-1]: + print_event_line(event, vm_zone_stat[event.value].counter) + print() + +# 2) vm_node_stat statistics are there since v4.8. +if "vm_node_stat" in prog: + print("VM_NODE_STAT:") + vm_node_stat = prog["vm_node_stat"] + for event in prog.type("enum node_stat_item").enumerators[:-1]: + print_event_line(event, vm_node_stat[event.value].counter) + print() + +# 3) vm_numa_event statistics are there since v5.14. They are only populated if +# CONFIG_NUMA is enabled. +if "node_subsys" in prog and "vm_numa_event" in prog: + print("VM_NUMA_EVENT:") + vm_numa_event = prog["vm_numa_event"] + for event in prog.type("enum numa_stat_item").enumerators[:-1]: + print_event_line(event, vm_numa_event[event.value].counter) + print() + +# 4) vm_event_states statistics (uses per-CPU counters) +print("VM_EVENT_STATES:") +vm_event_states = prog["vm_event_states"] +cpulist = list(for_each_online_cpu(prog)) +for event in prog.type("enum vm_event_item").enumerators[:-1]: + count = sum([per_cpu(vm_event_states, cpu).event[event.value] for cpu in cpulist]) + print_event_line(event, count) diff --git a/docs/advanced_usage.rst b/docs/advanced_usage.rst index 521398bac..ce941c5fe 100644 --- a/docs/advanced_usage.rst +++ b/docs/advanced_usage.rst @@ -46,7 +46,9 @@ Custom Programs The main components of a :class:`drgn.Program` are the program memory, types, and symbols. The CLI and equivalent library interfaces automatically determine these. However, it is also possible to create a "blank" ``Program`` and plug in -the main components. +the main components. The :func:`drgn.cli.run_interactive()` function allows you +to run the same drgn CLI once you've created a :class:`drgn.Program`, so it's +easy to make a custom program which allows interactive debugging. :meth:`drgn.Program.add_memory_segment()` defines a range of memory and how to read that memory. The following example uses a Btrfs filesystem image as the @@ -57,13 +59,14 @@ program "memory": import drgn import os import sys + from drgn.cli import run_interactive def btrfs_debugger(dev): file = open(dev, 'rb') size = file.seek(0, 2) - def read_file(address, count, physical, offset): + def read_file(address, count, offset, physical): file.seek(offset) return file.read(count) @@ -77,6 +80,7 @@ program "memory": prog = btrfs_debugger(sys.argv[1] if len(sys.argv) >= 2 else '/dev/sda') print(drgn.Object(prog, 'struct btrfs_super_block', address=65536)) + run_interactive(prog, banner_func=lambda _: "BTRFS debugger") :meth:`drgn.Program.add_type_finder()` and :meth:`drgn.Program.add_object_finder()` are the equivalent methods for @@ -115,3 +119,94 @@ Some of drgn's behavior can be modified through environment variables: kernel modules for the running kernel instead of getting them from the core dump (0 or 1). The default is 1. This environment variable is mainly intended for testing and may be ignored in the future. + +.. _kernel-special-objects: + +Linux Kernel Special Objects +---------------------------- + +When debugging the Linux kernel, there are some special :class:`drgn.Object`\ s +accessible with :meth:`drgn.Program.object()` and :meth:`drgn.Program[] +`. Some of these are available even without debugging +information, thanks to metadata called "vmcoreinfo" which is present in kernel +core dumps. These special objects include: + +``UTS_RELEASE`` + Object type: ``const char []`` + + This corresponds to the ``UTS_RELEASE`` macro in the Linux kernel source + code. This is the exact kernel release (i.e., the output of ``uname -r``). + + To use this as a Python string, you must convert it:: + + >>> release = prog["UTS_RELEASE"].string_().decode("ascii") + + This is available without debugging information. + +``PAGE_SIZE`` + Object type: ``unsigned long`` + +``PAGE_SHIFT`` + Object type: ``unsigned int`` + +``PAGE_MASK`` + Object type: ``unsigned long`` + + These correspond to the macros of the same name in the Linux kernel source + code. The page size is the smallest contiguous unit of physical memory + which can be allocated or mapped by the kernel. + + >>> prog['PAGE_SIZE'] + (unsigned long)4096 + >>> prog['PAGE_SHIFT'] + (int)12 + >>> prog['PAGE_MASK'] + (unsigned long)18446744073709547520 + >>> 1 << prog['PAGE_SHIFT'] == prog['PAGE_SIZE'] + True + >>> ~(prog['PAGE_SIZE'] - 1) == prog['PAGE_MASK'] + True + + These are available without debugging information. + +``jiffies`` + Object type: ``volatile unsigned long`` + + This is a counter of timer ticks. It is actually an alias of ``jiffies_64`` + on 64-bit architectures, or the least significant 32 bits of ``jiffies_64`` + on 32-bit architectures. Since this alias is defined via the linker, drgn + handles it specially. + + This is *not* available without debugging information. + +``vmemmap`` + Object type: ``struct page *`` + + This is a pointer to the "virtual memory map", an array of ``struct page`` + for each physical page of memory. While the purpose and implementation + details of this array are beyond the scope of this documentation, it is + enough to say that it is represented in the kernel source in an + architecture-dependent way, frequently as a macro or constant. The + definition provided by drgn ensures that users can access it without + resorting to architecture-specific logic. + + This is *not* available without debugging information. + +``VMCOREINFO`` + Object type: ``const char []`` + + This is the data contained in the vmcoreinfo note, which is present either + as an ELF note in ``/proc/kcore`` or ELF vmcores, or as a special data + section in kdump-formatted vmcores. The vmcoreinfo note contains critical + data necessary for interpreting the kernel image, such as KASLR offsets and + data structure locations. + + In the Linux kernel, this data is normally stored in a variable called + ``vmcoreinfo_data``. However, drgn reads this information from ELF note or + from the diskdump header. It is possible (in rare cases, usually with + vmcores created by hypervisors) for a vmcore to contain vmcoreinfo which + differs from the data in ``vmcoreinfo_data``, so it is important to + distinguish the contents. For that reason, we use the name ``VMCOREINFO`` to + distinguish it from the kernel variable ``vmcoreinfo_data``. + + This is available without debugging information. diff --git a/docs/api_reference.rst b/docs/api_reference.rst index f23df2c94..8e931c86d 100644 --- a/docs/api_reference.rst +++ b/docs/api_reference.rst @@ -130,3 +130,21 @@ Exceptions .. drgndoc:: MissingDebugInfoError .. drgndoc:: ObjectAbsentError .. drgndoc:: OutOfBoundsError + +CLI +--- + +.. drgndoc:: cli + +Logging +------- + +drgn logs using the standard :mod:`logging` module to a logger named +``"drgn"``. + +Thread Safety +------------- + +Only one thread at a time should access the same :class:`Program` (including +:class:`Object`, :class:`Type`, :class:`StackTrace`, etc. from that program). +It is safe to use different :class:`Program`\ s from concurrent threads. diff --git a/docs/conf.py b/docs/conf.py index 2e401113d..147651c4b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,6 +19,10 @@ ] extlinks = { + "contrib": ( + "https://github.com/osandov/drgn/blob/main/contrib/%s", + "%s", + ), "linux": ( "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/%s", "%s", diff --git a/docs/exts/drgndoc/docstrings.py b/docs/exts/drgndoc/docstrings.py index 4a728430b..0bc7ac622 100644 --- a/docs/exts/drgndoc/docstrings.py +++ b/docs/exts/drgndoc/docstrings.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import argparse import functools diff --git a/docs/exts/drgndoc/ext.py b/docs/exts/drgndoc/ext.py index 0a9d66815..62607b890 100644 --- a/docs/exts/drgndoc/ext.py +++ b/docs/exts/drgndoc/ext.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ drgn consists of a core C extension and supporting Python code. It also makes diff --git a/docs/exts/drgndoc/format.py b/docs/exts/drgndoc/format.py index 22256ad3b..1306d2722 100644 --- a/docs/exts/drgndoc/format.py +++ b/docs/exts/drgndoc/format.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import ast from typing import Any, List, Optional, Pattern, Sequence, Tuple, cast @@ -210,6 +210,28 @@ def visit_List( self._parts.append("\\") self._parts.append("]") + def visit_UnaryOp( + self, node: ast.UnaryOp, parent: Optional[ast.AST], sibling: Optional[ast.AST] + ) -> None: + if isinstance(node.op, ast.UAdd): + self._parts.append("+") + elif isinstance(node.op, ast.USub): + self._parts.append("-") + elif isinstance(node.op, ast.Not): + self._parts.append("not ") + elif isinstance(node.op, ast.Invert): + self._parts.append("~") + else: + raise NotImplementedError( + f"{node.op.__class__.__name__} formatting is not implemented" + ) + parens = not isinstance(node.operand, (ast.Constant, ast.Name)) + if parens: + self._parts.append("(") + self._visit(node.operand, node, None) + if parens: + self._parts.append(")") + class Formatter: def __init__( diff --git a/docs/exts/drgndoc/namespace.py b/docs/exts/drgndoc/namespace.py index 1a868ca2a..6bdff6771 100644 --- a/docs/exts/drgndoc/namespace.py +++ b/docs/exts/drgndoc/namespace.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import itertools from typing import Generic, Iterator, List, Mapping, Sequence, TypeVar, Union diff --git a/docs/exts/drgndoc/parse.py b/docs/exts/drgndoc/parse.py index aa3bc9bb3..153acf923 100644 --- a/docs/exts/drgndoc/parse.py +++ b/docs/exts/drgndoc/parse.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import ast import inspect diff --git a/docs/exts/drgndoc/util.py b/docs/exts/drgndoc/util.py index 1c9e9feda..9084082ab 100644 --- a/docs/exts/drgndoc/util.py +++ b/docs/exts/drgndoc/util.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from typing import Optional diff --git a/docs/exts/drgndoc/visitor.py b/docs/exts/drgndoc/visitor.py index b1fa51e1d..0ef71c45d 100644 --- a/docs/exts/drgndoc/visitor.py +++ b/docs/exts/drgndoc/visitor.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import ast from typing import Any, Optional diff --git a/docs/exts/setuptools_config.py b/docs/exts/setuptools_config.py index f9acf072b..13272e529 100644 --- a/docs/exts/setuptools_config.py +++ b/docs/exts/setuptools_config.py @@ -1,24 +1,6 @@ -# https://pypi.org/project/jaraco.packaging/ -# # Copyright Jason R. Coombs -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# SPDX-License-Identifier: MIT +# From https://pypi.org/project/jaraco.packaging/. from __future__ import unicode_literals diff --git a/docs/index.rst b/docs/index.rst index b4a68b3f3..43fa31eba 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -36,5 +36,7 @@ Table of Contents advanced_usage api_reference helpers + support_matrix case_studies getting_debugging_symbols + release_highlights diff --git a/docs/release_highlights.rst b/docs/release_highlights.rst new file mode 100644 index 000000000..384d6de06 --- /dev/null +++ b/docs/release_highlights.rst @@ -0,0 +1,11 @@ +Release Highlights +================== + +These are highlights of each release of drgn focusing on a few exciting items +from the full `release notes `_. + +.. toctree:: + + release_highlights/0.0.24.rst + release_highlights/0.0.23.rst + release_highlights/0.0.22.rst diff --git a/docs/release_highlights/0.0.22.rst b/docs/release_highlights/0.0.22.rst new file mode 100644 index 000000000..7955e12bc --- /dev/null +++ b/docs/release_highlights/0.0.22.rst @@ -0,0 +1,264 @@ +0.0.22 (Released January 5th, 2023) +=================================== + +These are some of the highlights of drgn 0.0.22. See the `GitHub release +`_ for the full release +notes, including more improvements and bug fixes. + +.. highlight:: pycon + +Listing Stack Frame Locals +-------------------------- + +:meth:`drgn.StackFrame.locals()` returns the names of all arguments and local +variables in the scope of a stack frame. This allows you to get a quick idea of +what's going on in a function without needing to read the source code right +away. + +Let's use the ``__schedule`` stack frame from the following trace as an +example:: + + >>> trace = prog.stack_trace(1) + >>> trace + #0 context_switch (./kernel/sched/core.c:5209:2) + #1 __schedule (./kernel/sched/core.c:6521:8) + #2 schedule (./kernel/sched/core.c:6597:3) + #3 do_wait (./kernel/exit.c:1562:4) + #4 kernel_wait4 (./kernel/exit.c:1706:8) + #5 __do_sys_wait4 (./kernel/exit.c:1734:13) + #6 do_syscall_x64 (./arch/x86/entry/common.c:50:14) + #7 do_syscall_64 (./arch/x86/entry/common.c:80:7) + #8 entry_SYSCALL_64+0x9b/0x197 (./arch/x86/entry/entry_64.S:120) + #9 0x7f6a34a00057 + >>> trace[1].locals() + ['sched_mode', 'prev', 'next', 'switch_count', 'prev_state', 'rf', 'rq', 'cpu'] + >>> for name in trace[1].locals(): + ... print(name, trace[1][name].format_(dereference=False)) + ... + sched_mode (unsigned int)0 + prev (struct task_struct *)0xffffa3b601178000 + next (struct task_struct *)0xffffa3b6026db800 + switch_count (unsigned long *)0xffffa3b601178528 + prev_state (unsigned long) + rf (struct rq_flags){ + .flags = (unsigned long)1, + .cookie = (struct pin_cookie){}, + .clock_update_flags = (unsigned int)4, + } + rq (struct rq *)0xffffa3b67fda9640 + cpu (int) + +Compare this to the `kernel source code +`_. +Note that some of the variables have been optimized out by the compiler. + +This feature was contributed by Stephen Brennan. + +Merged Slab Caches +------------------ + +The Linux kernel slab allocator merges "similar" slab caches as an +optimization, which often causes confusion. +:func:`~drgn.helpers.linux.slab.slab_cache_is_merged()` (added back in 0.0.20) +returns whether or not a slab cache has been merged, but not what it was merged +with. In this release, Stephen Brennan added +:func:`~drgn.helpers.linux.slab.get_slab_cache_aliases()`, which provides a +mapping from a slab cache name to the name of the cache it was merged into:: + + >>> get_slab_cache_aliases(prog) + {'io_kiocb': 'maple_node', 'ip_dst_cache': 'uid_cache', 'aio_kiocb': 'uid_cache', 'ip_fib_alias': 'Acpi-Parse', 'pid_namespace': 'pid', 'iommu_iova': 'vmap_area', 'fasync_cache': 'ftrace_event_field', 'dnotify_mark': 'Acpi-State', 'tcp_bind2_bucket': 'vmap_area', 'nsproxy': 'Acpi-Operand', 'shared_policy_node': 'ftrace_event_field', 'eventpoll_epi': 'pid', 'fib6_nodes': 'vmap_area', 'Acpi-Namespace': 'ftrace_event_field', 'posix_timers_cache': 'maple_node', 'inotify_inode_mark': 'Acpi-State', 'kernfs_iattrs_cache': 'trace_event_file', 'fs_cache': 'vmap_area', 'UDP-Lite': 'UDP', 'anon_vma_chain': 'vmap_area', 'ip6_dst_cache': 'maple_node', 'eventpoll_pwq': 'vmap_area', 'inet_peer_cache': 'uid_cache', 'fsnotify_mark_connector': 'numa_policy', 'ip_fib_trie': 'ftrace_event_field', 'filp': 'maple_node', 'dnotify_struct': 'numa_policy', 'UDPLITEv6': 'UDPv6', 'biovec-16': 'maple_node', 'PING': 'signal_cache', 'ep_head': 'blkdev_ioc', 'tcp_bind_bucket': 'pid', 'Acpi-ParseExt': 'Acpi-State', 'cred_jar': 'pid', 'ovl_aio_req': 'pid', 'pool_workqueue': 'maple_node', 'sigqueue': 'Acpi-State', 'file_lock_ctx': 'Acpi-Parse', 'kernfs_node_cache': 'pid'} + +This means that if you're looking for ``io_kiocb`` allocations, you actually +need to look at the ``maple_node`` slab cache. Conversely, if you're looking at +the ``maple_node`` slab cache, you need to be aware that it also contains +allocations from all of the following slab caches:: + + >>> [merged for merged, canonical in get_slab_cache_aliases(prog).items() if canonical == "maple_node"] + ['io_kiocb', 'posix_timers_cache', 'ip6_dst_cache', 'filp', 'biovec-16', 'pool_workqueue'] + +Slab Address Information +------------------------ + +This release extended :func:`~drgn.helpers.common.memory.identify_address()` to +show additional information about slab allocations:: + + >>> ptr1 = 0xffffa3b601178438 + >>> ptr2 = 0xffffa3b601176cc0 + >>> identify_address(prog, ptr1) + 'slab object: task_struct+0x438' + >>> identify_address(prog, ptr2) + 'free slab object: mm_struct+0x0' + +This means that ``ptr1`` is an address 0x438 bytes into an allocated object from +the ``task_struct`` slab cache, and ``ptr2`` is a free object from the +``mm_struct`` slab cache. + +:func:`~drgn.helpers.linux.slab.slab_object_info()` provides the same +information programmatically:: + + >>> slab_object_info(prog, ptr1) + SlabObjectInfo(slab_cache=Object(prog, 'struct kmem_cache *', value=0xffffa3b601045500), slab=Object(prog, 'struct slab *', value=0xffffe80840045e00), address=0xffffa3b601178000, allocated=True) + >>> slab_object_info(prog, ptr2) + SlabObjectInfo(slab_cache=Object(prog, 'struct kmem_cache *', value=0xffffa3b601045900), slab=Object(prog, 'struct slab *', value=0xffffe80840045c00), address=0xffffa3b601176cc0, allocated=False) + +Annotated Stack Memory +---------------------- + +:func:`~drgn.helpers.common.stack.print_annotated_stack()` prints a stack trace +and all of its memory, identifying anything that it can:: + + >>> print_annotated_stack(prog.stack_trace(1)) + STACK POINTER VALUE + [stack frame #0 at 0xffffffffaf8a68e9 (__schedule+0x429/0x488) in context_switch at ./kernel/sched/core.c:5209:2 (inlined)] + [stack frame #1 at 0xffffffffaf8a68e9 (__schedule+0x429/0x488) in __schedule at ./kernel/sched/core.c:6521:8] + ffffbb1ac0013d28: ffffffffaf4498f5 [function symbol: __flush_tlb_one_user+0x5] + ffffbb1ac0013d30: 00000000af449feb + ffffbb1ac0013d38: 0000000000000001 + ffffbb1ac0013d40: 0000000000000004 + ffffbb1ac0013d48: 25c5ff9539edc200 + ffffbb1ac0013d50: ffffa3b601178000 [slab object: task_struct+0x0] + ffffbb1ac0013d58: ffffa3b601178000 [slab object: task_struct+0x0] + ffffbb1ac0013d60: ffffbb1ac0013e10 + ffffbb1ac0013d68: ffffa3b601177ff0 [slab object: mm_struct+0x70] + ffffbb1ac0013d70: ffffa3b601178000 [slab object: task_struct+0x0] + ffffbb1ac0013d78: ffffa3b601178000 [slab object: task_struct+0x0] + ffffbb1ac0013d80: ffffffffaf8a69d1 [function symbol: schedule+0x89] + [stack frame #2 at 0xffffffffaf8a69d1 (schedule+0x89/0xc7) in schedule at ./kernel/sched/core.c:6597:3] + ffffbb1ac0013d88: ffffbb1ac0013de8 + ffffbb1ac0013d90: 0000000000000000 + ffffbb1ac0013d98: ffffffffaf4595ee [function symbol: do_wait+0x231] + [stack frame #3 at 0xffffffffaf4595ee (do_wait+0x231/0x2e3) in do_wait at ./kernel/exit.c:1562:4] + ffffbb1ac0013da0: ffffa3b601178450 [slab object: task_struct+0x450] + ffffbb1ac0013da8: ffffa3b601178000 [slab object: task_struct+0x0] + ffffbb1ac0013db0: 0000000000000004 + ffffbb1ac0013db8: 0000000000000000 + ffffbb1ac0013dc0: 00007ffe0984a170 + ffffbb1ac0013dc8: 0000000000000000 + ffffbb1ac0013dd0: fffffffffffffffd + ffffbb1ac0013dd8: 0000000000000004 + ffffbb1ac0013de0: ffffffffaf45a42f [function symbol: kernel_wait4+0xc2] + [stack frame #4 at 0xffffffffaf45a42f (kernel_wait4+0xc2/0x11b) in kernel_wait4 at ./kernel/exit.c:1706:8] + ffffbb1ac0013de8: 0000000400000004 + ffffbb1ac0013df0: 0000000000000000 + ffffbb1ac0013df8: 0000000000000000 + ffffbb1ac0013e00: 0000000000000000 + ffffbb1ac0013e08: 0000000000000000 + ffffbb1ac0013e10: ffffffff00000000 + ffffbb1ac0013e18: ffffa3b601178000 [slab object: task_struct+0x0] + ffffbb1ac0013e20: ffffffffaf45890c [function symbol: child_wait_callback+0x0] + ffffbb1ac0013e28: ffffa3b601188028 [slab object: signal_cache+0x28] + ffffbb1ac0013e30: ffffa3b601188028 [slab object: signal_cache+0x28] + ffffbb1ac0013e38: 000055d500000000 + ffffbb1ac0013e40: 25c5ff9539edc200 + ffffbb1ac0013e48: 0000000000000000 + ffffbb1ac0013e50: ffffbb1ac0013f30 + ffffbb1ac0013e58: ffffbb1ac0013f58 + ffffbb1ac0013e60: 0000000000000000 + ffffbb1ac0013e68: 0000000000000000 + ffffbb1ac0013e70: 0000000000000000 + ffffbb1ac0013e78: ffffffffaf45a4c0 [function symbol: __do_sys_wait4+0x38] + [stack frame #5 at 0xffffffffaf45a4c0 (__do_sys_wait4+0x38/0x8c) in __do_sys_wait4 at ./kernel/exit.c:1734:13] + ffffbb1ac0013e80: ffffffffaf8aaa21 [function symbol: _raw_spin_unlock_irq+0x10] + ffffbb1ac0013e88: ffffffffaf46460c [function symbol: do_sigaction+0xf8] + ffffbb1ac0013e90: ffffa3b601180020 [slab object: sighand_cache+0x20] + ffffbb1ac0013e98: ffffa3b6028d02d0 [slab object: vm_area_struct+0x0] + ffffbb1ac0013ea0: 25c5ff9539edc200 + ffffbb1ac0013ea8: 0000000000000002 + ffffbb1ac0013eb0: 00007ffe09849fb0 + ffffbb1ac0013eb8: ffffbb1ac0013f58 + ffffbb1ac0013ec0: 0000000000000000 + ffffbb1ac0013ec8: 0000000000000000 + ffffbb1ac0013ed0: 0000000000000046 + ffffbb1ac0013ed8: ffffa3b601178000 [slab object: task_struct+0x0] + ffffbb1ac0013ee0: ffffa3b601178000 [slab object: task_struct+0x0] + ffffbb1ac0013ee8: ffffbb1ac0013f58 + ffffbb1ac0013ef0: 0000000000000000 + ffffbb1ac0013ef8: ffffffffaf426def [function symbol: fpregs_assert_state_consistent+0x1b] + ffffbb1ac0013f00: 0000000000000000 + ffffbb1ac0013f08: ffffffffaf4b2f53 [function symbol: exit_to_user_mode_prepare+0xa6] + ffffbb1ac0013f10: 0000000000000000 + ffffbb1ac0013f18: 25c5ff9539edc200 + ffffbb1ac0013f20: ffffbb1ac0013f58 + ffffbb1ac0013f28: 0000000000000000 + ffffbb1ac0013f30: ffffbb1ac0013f48 + ffffbb1ac0013f38: ffffffffaf8a1573 [function symbol: do_syscall_64+0x70] + [stack frame #6 at 0xffffffffaf8a1573 (do_syscall_64+0x70/0x8a) in do_syscall_x64 at ./arch/x86/entry/common.c:50:14 (inlined)] + [stack frame #7 at 0xffffffffaf8a1573 (do_syscall_64+0x70/0x8a) in do_syscall_64 at ./arch/x86/entry/common.c:80:7] + ffffbb1ac0013f40: 0000000000000000 + ffffbb1ac0013f48: 0000000000000000 + ffffbb1ac0013f50: ffffffffafa0009b [symbol: entry_SYSCALL_64+0x9b] + [stack frame #8 at 0xffffffffafa0009b (entry_SYSCALL_64+0x9b/0x197) at ./arch/x86/entry/entry_64.S:120] + ffffbb1ac0013f58: 0000000000000000 + [stack frame #9 at 0x7f6a34a00057] + +Like :meth:`drgn.StackFrame.locals()`, this provides a nice overview of +everything happening in a function, which might include useful hints. Keep in +mind that it may identify "stale" addresses for anything that a function hasn't +reinitialized yet, and as always, be careful of slab cache merging. + +This was inspired by the crash ``bt -FF`` command. It was contributed by Nhat +Pham. + +XArray Helpers +-------------- + +XArrays were introduced in Linux 4.20 as a replacement for radix trees. drgn's +radix tree helpers also support XArrays in some cases, but this is awkward, not +obvious, and doesn't work for new, XArray-only functionality. + +This release added dedicated XArray helpers like +:func:`~drgn.helpers.linux.xarray.xa_load()` and +:func:`~drgn.helpers.linux.xarray.xa_for_each()`. + +s390x Support +------------- + +Sven Schnelle contributed s390x support for Linux kernel modules and stack +traces. This is the state of architecture support in this release: + +.. list-table:: drgn 0.0.22 Architecture Support + :header-rows: 1 + + * - Architecture + - Linux Kernel Modules + - Stack Traces + - Virtual Address Translation + * - x86-64 + - ✓ + - ✓ + - ✓ + * - AArch64 + - ✓ + - ✓ + - ✓ + * - ppc64 + - ✓ + - ✓ + - + * - s390x + - ✓ + - ✓ + - + * - i386 + - ✓ + - + - + * - Arm + - ✓ + - + - + * - RISC-V + - ✓ + - + - + +Relicensing to LGPL +------------------- + +drgn was originally licensed as GPLv3+. In this release, it was changed to +LGPLv2.1+. The motivation for this change was to enable the long term vision +for drgn that more projects can use it as a library providing programmatic +interfaces for debugger functionality. For example, `Object Introspection +`_, a userspace +memory profiler recently open sourced by Meta, uses drgn to parse debugging +information. diff --git a/docs/release_highlights/0.0.23.rst b/docs/release_highlights/0.0.23.rst new file mode 100644 index 000000000..b3c548331 --- /dev/null +++ b/docs/release_highlights/0.0.23.rst @@ -0,0 +1,172 @@ +0.0.23 (Released June 28th, 2023) +================================= + +These are some of the highlights of drgn 0.0.23. See the `GitHub release +`_ for the full release +notes, including more improvements and bug fixes. + +.. highlight:: pycon + +Virtual Address Translation Helpers +----------------------------------- + +This release added several Linux kernel helpers for translating virtual +addresses. + +:func:`~drgn.helpers.linux.mm.follow_phys()` translates a virtual address to a +physical address in a given address space. For example, to get the physical +address that virtual address 0x7f7fe46a4270 maps to in process 115:: + + >>> task = find_task(prog, 115) + >>> address = 0x7f7fe46a4270 + >>> print(hex(follow_phys(task.mm, address))) + 0x4090270 + +:func:`~drgn.helpers.linux.mm.follow_page()` translates a virtual address to +the ``struct page *`` that it maps to:: + + >>> follow_page(task.mm, address) + *(struct page *)0xffffd20ac0102400 = { + ... + } + +:func:`~drgn.helpers.linux.mm.follow_pfn()` translates a virtual address to the +page frame number (PFN) of the page that it maps to:: + + >>> follow_pfn(task.mm, address) + (unsigned long)16528 + +These can be used to translate arbitrary kernel virtual addresses by passing +``prog["init_mm"].address_of_()``:: + + >>> print(hex(follow_phys(prog["init_mm"].address_of_(), 0xffffffffc0483000))) + 0x2e4b000 + +Vmalloc/Vmap Address Translation Helpers +---------------------------------------- + +:func:`~drgn.helpers.linux.mm.vmalloc_to_page()` is a special case of +:func:`~drgn.helpers.linux.mm.follow_page()` for vmalloc and vmap addresses:: + + >>> vmalloc_to_page(prog, 0xffffffffc0477000) + *(struct page *)0xffffc902400b8980 = { + ... + } + +Likewise, :func:`~drgn.helpers.linux.mm.vmalloc_to_pfn()` is a special case of +:func:`~drgn.helpers.linux.mm.follow_pfn()` for vmalloc and vmap addresses:: + + >>> vmalloc_to_pfn(prog, 0xffffffffc0477000) + (unsigned long)11814 + +``contrib`` Directory +--------------------- + +Martin Liška, Boris Burkov, and Johannes Thumshirn added lots of new scripts to +the ``contrib`` directory: + +- :contrib:`btrfs_tree.py`: work-in-progress helpers for Btrfs B-trees +- :contrib:`btrfs_tree_mod_log.py`: simulator for the Btrfs tree modification log +- :contrib:`dump_btrfs_bgs.py`: print block groups in a Btrfs filesystem +- :contrib:`kcore_list.py`: print memory regions from ``/proc/kcore`` +- :contrib:`kernel_sys.py`: print system information (similar to crash's ``sys`` command) +- :contrib:`mount.py`: print a filesystem mount table +- :contrib:`platform_drivers.py`: print registered `platform drivers `_ +- :contrib:`vmmap.py`: print memory mappings in a process (similar to ``/proc/$pid/maps``) +- :contrib:`vmstat.py`: print information about kernel memory usage + +Embedding Interactive Mode +-------------------------- + +:meth:`drgn.cli.run_interactive()` runs drgn's interactive mode. It can be used +to embed drgn in another application. For example, you could use it for a +custom :class:`drgn.Program` that the standard drgn CLI can't set up: + +.. code-block:: python3 + + import drgn + import drgn.cli + + prog = drgn.Program() + prog.add_type_finder(...) + prog.add_object_finder(...) + prog.add_memory_segment(...) + drgn.cli.run_interactive(prog) + +Full s390x Support +------------------ + +Sven Schnelle contributed s390x virtual address translation support. This is +the state of architecture support in this release: + +.. list-table:: drgn 0.0.23 Architecture Support + :header-rows: 1 + + * - Architecture + - Linux Kernel Modules + - Stack Traces + - Virtual Address Translation + * - x86-64 + - ✓ + - ✓ + - ✓ + * - AArch64 + - ✓ + - ✓ + - ✓ + * - ppc64 + - ✓ + - ✓ + - + * - s390x + - ✓ + - ✓ + - ✓ + * - i386 + - ✓ + - + - + * - Arm + - ✓ + - + - + * - RISC-V + - ✓ + - + - + +Linux 6.3 & 6.4 Support +----------------------- + +Linux 6.3 and 6.4 had an unusual number of breaking changes for drgn. Here are +some errors you might see with older versions of drgn that are fixed in this +release. + +On startup (fixed by Ido Schimmel):: + + warning: could not get debugging information for: + kernel modules (could not find loaded kernel modules: 'struct module' has no member 'core_size') + +From :meth:`drgn.Program.stack_trace()` and :meth:`drgn.Thread.stack_trace()`:: + + Exception: unknown ORC entry type 3 + +From :func:`~drgn.helpers.linux.mm.compound_order()` and +:func:`~drgn.helpers.linux.mm.compound_nr()`:: + + AttributeError: 'struct page' has no member 'compound_order' + +From :func:`~drgn.helpers.linux.block.for_each_disk()` and +:func:`~drgn.helpers.linux.block.for_each_partition()`:: + + AttributeError: 'struct class' has no member 'p' + +Python 3.12 Support +------------------- + +Python 3.12, currently in beta, changed an implementation detail that drgn +depended on, which caused crashes like:: + + Py_SIZE: Assertion `ob->ob_type != &PyLong_Type' failed. + +Stephen Brennan fixed this. diff --git a/docs/release_highlights/0.0.24.rst b/docs/release_highlights/0.0.24.rst new file mode 100644 index 000000000..b57382640 --- /dev/null +++ b/docs/release_highlights/0.0.24.rst @@ -0,0 +1,115 @@ +0.0.24 (Released September 8th, 2023) +===================================== + +These are some of the highlights of drgn 0.0.24. See the `GitHub release +`_ for the full release +notes, including more improvements and bug fixes. + +.. highlight:: pycon + +Linked List Length Helper +------------------------- + +This release added :func:`~drgn.helpers.linux.list.list_count_nodes()`, which +returns the length of a Linux kernel linked list:: + + >>> list_count_nodes(prog["workqueues"].address_of_()) + 29 + +Networking Helpers +------------------ + +This release added a couple of Linux kernel networking helpers requested by +Jakub Kicinski. + +:func:`~drgn.helpers.linux.net.netdev_priv()` returns the private data of a +network device:: + + >>> dev = netdev_get_by_name(prog, "wlp0s20f3") + >>> netdev_priv(dev) + (void *)0xffff9419c9dec9c0 + >>> netdev_priv(dev, "struct ieee80211_sub_if_data") + *(struct ieee80211_sub_if_data *)0xffff9419c9dec9c0 = { + ... + } + +:func:`~drgn.helpers.linux.net.skb_shinfo()` returns the shared info for a +socket buffer. + +C++ Lookups +----------- + +This release added support for a few C++ features. + +Simple Type Specifiers +^^^^^^^^^^^^^^^^^^^^^^ + +Unlike C, C++ allows referring to ``class``, ``struct``, ``union``, and +``enum`` types without their respective keywords. For example: + +.. code-block:: c++ + + class Foo { ... }; + Foo foo; // Equivalent to class Foo foo; + +Previously, drgn always required the keyword, so ``prog.type("class Foo")`` +would succeed but ``prog.type("Foo")`` would fail with a :class:`LookupError`. +This requirement was surprising to C++ developers, so it was removed. For C++ +programs, ``prog.type("Foo")`` will now find a ``class``, ``struct``, +``union``, or ``enum`` type named ``Foo`` (for C programs, the keyword is still +required). + +Nested Classes +^^^^^^^^^^^^^^ + +Again unlike C, C++ allows ``class``, ``struct``, and ``union`` types to be +defined inside of other ``class``, ``struct``, and ``union`` types. For example: + +.. code-block:: c++ + + class Foo { + public: + class Bar { ... }; + ... + }; + Foo::Bar bar; + +drgn can now find such types with ``prog.type("Foo::Bar")``. + +Member Functions +^^^^^^^^^^^^^^^^ + +C++ supports member functions (a.k.a. methods). For example: + +.. code-block:: c++ + + class Foo { + int method() { ... } + }; + +drgn can now find member functions with :meth:`drgn.Program.function()`, +:meth:`drgn.Program.object()`, or :meth:`drgn.Program[] +` (e.g., ``prog.function("Foo::method")`` or +``prog["Foo::method"]``). + +Split DWARF +----------- + +drgn now supports split DWARF object (.dwo) files. This is enabled by the +``-gsplit-dwarf`` option in GCC and Clang or for the Linux kernel with +``CONFIG_DEBUG_INFO_SPLIT=y``. + +Split DWARF package (.dwp) file support is still in progress. + +Performance Improvements +------------------------ + +Thierry Treyer found a bug that made us search through much more debugging +information than necessary when getting a stack trace. Fixing this made stack +traces almost twice as fast. + +The C++ lookup and split DWARF support mentioned above require processing more +information in drgn's debugging information indexing step, which it does on +startup and whenever debugging information is manually loaded. This could've +been a performance regression, but instead, indexing was reworked from the +ground up in a way that's usually *faster* despite the added features. diff --git a/docs/requirements.txt b/docs/requirements.txt index 1c8673841..5388c8836 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1 +1 @@ -sphinx==5.1.1 +sphinx==5.3.0 diff --git a/docs/support_matrix.rst b/docs/support_matrix.rst new file mode 100644 index 000000000..29116aacf --- /dev/null +++ b/docs/support_matrix.rst @@ -0,0 +1,93 @@ +Support Matrix +============== + +Architectures +------------- + +Some features in drgn require architecture-specific support. The current status +of this support is: + +.. list-table:: + :header-rows: 1 + + * - Architecture + - Linux Kernel Modules [1]_ + - Stack Traces [2]_ + - Virtual Address Translation [3]_ + * - x86-64 + - ✓ + - ✓ + - ✓ + * - AArch64 + - ✓ + - ✓ + - ✓ + * - s390x + - ✓ + - ✓ + - ✓ + * - ppc64 + - ✓ + - ✓ + - + * - i386 + - ✓ + - + - + * - Arm + - ✓ + - + - + * - RISC-V + - ✓ + - + - + +.. rubric:: Key + +.. [1] Support for loading debugging symbols for Linux kernel modules. +.. [2] Support for capturing stack traces (:meth:`drgn.Program.stack_trace()`, :meth:`drgn.Thread.stack_trace()`). +.. [3] Support for translating virtual addresses, which is required for reading from vmalloc/vmap and module memory in Linux kernel vmcores and for various helpers in :mod:`drgn.helpers.linux.mm`. + +The listed architectures are recognized in :class:`drgn.Architecture`. Other +architectures are represented by :attr:`drgn.Architecture.UNKNOWN`. Features +not mentioned above should work on any architecture, listed or not. + +Cross-Debugging +^^^^^^^^^^^^^^^ + +drgn can debug architectures different from the host. For example, you can +debug an AArch64 (kernel or userspace) core dump from an x86-64 machine. + +Linux Kernel Versions +--------------------- + +drgn officially supports the current mainline, stable, and longterm kernel +releases from `kernel.org `_. (There may be some delay +before a new mainline version is fully supported.) End-of-life versions are +supported until it becomes too difficult to do so. The kernel versions +currently fully supported are: + +.. Keep this in sync with vmtest/config.py. + +- 6.0-6.5 +- 5.10-5.19 +- 5.4 +- 4.19 +- 4.14 +- 4.9 + +Other versions are not tested. They'll probably mostly work, but support is +best-effort. + +Kernel Configuration +^^^^^^^^^^^^^^^^^^^^ + +drgn supports debugging kernels with various configurations: + +- SMP and !SMP. +- Preemptible and non-preemptible. +- SLUB, SLAB, and SLOB allocators. + +drgn requires a kernel configured with ``CONFIG_PROC_KCORE=y`` for live kernel +debugging. diff --git a/docs/user_guide.rst b/docs/user_guide.rst index ee2a8ed7e..a8e233830 100644 --- a/docs/user_guide.rst +++ b/docs/user_guide.rst @@ -351,6 +351,8 @@ It's even possible to run drgn scripts directly with the proper `shebang $ sudo ./script2.py You have 36 filesystems mounted +.. _interactive-mode: + Interactive Mode ^^^^^^^^^^^^^^^^ @@ -400,6 +402,6 @@ Next Steps ---------- Refer to the :doc:`api_reference`. Look through the :doc:`helpers`. Read some -:doc:`case_studies`. Browse through the official `examples -`_ and `tools -`_. +:doc:`case_studies`. Browse through the `tools +`_. Check out the `community +contributions `_. diff --git a/drgn/__init__.py b/drgn/__init__.py index 06e6aee69..5e9ec7fb4 100644 --- a/drgn/__init__.py +++ b/drgn/__init__.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Programmable debugger @@ -142,7 +142,7 @@ if sys.version_info >= (3, 8): - _open_code = io.open_code + _open_code = io.open_code # novermin else: from typing import BinaryIO diff --git a/drgn/__main__.py b/drgn/__main__.py index 9fd749eb4..128b71e2e 100644 --- a/drgn/__main__.py +++ b/drgn/__main__.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ drgn entry point @@ -11,6 +11,6 @@ if __name__ == "__main__": - from drgn.internal.cli import main + from drgn.cli import _main - main() + _main() diff --git a/drgn/cli.py b/drgn/cli.py new file mode 100644 index 000000000..01b277bcd --- /dev/null +++ b/drgn/cli.py @@ -0,0 +1,411 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# Copyright (c) 2023, Oracle and/or its affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +"""Functions for embedding the drgn CLI.""" + +import argparse +import builtins +import code +import importlib +import logging +import os +import os.path +import pkgutil +import readline +import runpy +import shutil +import sys +from typing import Any, Callable, Dict, Optional + +import drgn +from drgn.internal.rlcompleter import Completer + +__all__ = ("run_interactive", "version_header") + +logger = logging.getLogger("drgn") + + +class _LogFormatter(logging.Formatter): + _LEVELS = ( + (logging.DEBUG, "debug", "36"), + (logging.INFO, "info", "32"), + (logging.WARNING, "warning", "33"), + (logging.ERROR, "error", "31"), + (logging.CRITICAL, "critical", "31;1"), + ) + + def __init__(self, color: bool) -> None: + if color: + level_prefixes = { + level: f"\033[{level_color}m{level_name}:\033[0m" + for level, level_name, level_color in self._LEVELS + } + else: + level_prefixes = { + level: f"{level_name}:" for level, level_name, _ in self._LEVELS + } + default_prefix = "%(levelname)s:" + + self._drgn_formatters = { + level: logging.Formatter(f"{prefix} %(message)s") + for level, prefix in level_prefixes.items() + } + self._default_drgn_formatter = logging.Formatter( + f"{default_prefix} %(message)s" + ) + + self._other_formatters = { + level: logging.Formatter(f"{prefix}%(name)s: %(message)s") + for level, prefix in level_prefixes.items() + } + self._default_other_formatter = logging.Formatter( + f"{default_prefix}%(name)s: %(message)s" + ) + + def format(self, record: logging.LogRecord) -> str: + if record.name == "drgn": + formatter = self._drgn_formatters.get( + record.levelno, self._default_drgn_formatter + ) + else: + formatter = self._other_formatters.get( + record.levelno, self._default_other_formatter + ) + return formatter.format(record) + + +def version_header() -> str: + """ + Return the version header printed at the beginning of a drgn session. + + The :func:`run_interactive()` function does not include this banner at the + beginning of an interactive session. Use this function to retrieve one line + of text to add to the beginning of the drgn banner, or print it before + calling :func:`run_interactive()`. + """ + python_version = ".".join(str(v) for v in sys.version_info[:3]) + libkdumpfile = f'with{"" if drgn._with_libkdumpfile else "out"} libkdumpfile' + return f"drgn {drgn.__version__} (using Python {python_version}, elfutils {drgn._elfutils_version}, {libkdumpfile})" + + +class _QuietAction(argparse.Action): + def __init__( + self, option_strings: Any, dest: Any, nargs: Any = 0, **kwds: Any + ) -> None: + super().__init__(option_strings, dest, nargs=nargs, **kwds) + + def __call__( + self, parser: Any, namespace: Any, values: Any, option_string: Any = None + ) -> None: + setattr(namespace, self.dest, True) + namespace.log_level = "none" + + +def _identify_script(path: str) -> str: + EI_NIDENT = 16 + SIZEOF_E_TYPE = 2 + + with open(path, "rb") as f: + header = f.read(EI_NIDENT + SIZEOF_E_TYPE) + + ELFMAG = b"\177ELF" + EI_DATA = 5 + ELFDATA2LSB = 1 + ELFDATA2MSB = 2 + ET_CORE = 4 + + if len(header) < EI_NIDENT + SIZEOF_E_TYPE or header[:4] != ELFMAG: + return "other" + + if header[EI_DATA] == ELFDATA2LSB: + byteorder = "little" + elif header[EI_DATA] == ELFDATA2MSB: + byteorder = "big" + else: + return "elf" + + e_type = int.from_bytes( + header[EI_NIDENT : EI_NIDENT + SIZEOF_E_TYPE], + byteorder, # type: ignore[arg-type] # python/mypy#9057 + ) + return "core" if e_type == ET_CORE else "elf" + + +def _displayhook(value: Any) -> None: + if value is None: + return + setattr(builtins, "_", None) + if isinstance(value, drgn.Object): + text = value.format_(columns=shutil.get_terminal_size((0, 0)).columns) + elif isinstance(value, (drgn.StackFrame, drgn.StackTrace, drgn.Type)): + text = str(value) + else: + text = repr(value) + try: + sys.stdout.write(text) + except UnicodeEncodeError: + encoded = text.encode(sys.stdout.encoding, "backslashreplace") + if hasattr(sys.stdout, "buffer"): + sys.stdout.buffer.write(encoded) + else: + text = encoded.decode(sys.stdout.encoding, "strict") + sys.stdout.write(text) + sys.stdout.write("\n") + setattr(builtins, "_", value) + + +def _main() -> None: + handler = logging.StreamHandler() + handler.setFormatter( + _LogFormatter(hasattr(sys.stderr, "fileno") and os.isatty(sys.stderr.fileno())) + ) + logging.getLogger().addHandler(handler) + + version = version_header() + parser = argparse.ArgumentParser(prog="drgn", description="Programmable debugger") + + program_group = parser.add_argument_group( + title="program selection", + ).add_mutually_exclusive_group() + program_group.add_argument( + "-k", "--kernel", action="store_true", help="debug the running kernel (default)" + ) + program_group.add_argument( + "-c", "--core", metavar="PATH", type=str, help="debug the given core dump" + ) + program_group.add_argument( + "-p", + "--pid", + metavar="PID", + type=int, + help="debug the running process with the given PID", + ) + + symbol_group = parser.add_argument_group("debugging symbols") + symbol_group.add_argument( + "-s", + "--symbols", + metavar="PATH", + type=str, + action="append", + help="load additional debugging symbols from the given file; this option may be given more than once", + ) + default_symbols_group = symbol_group.add_mutually_exclusive_group() + default_symbols_group.add_argument( + "--main-symbols", + dest="default_symbols", + action="store_const", + const={"main": True}, + help="only load debugging symbols for the main executable and those added with -s; " + "for userspace programs, this is currently equivalent to --no-default-symbols", + ) + default_symbols_group.add_argument( + "--no-default-symbols", + dest="default_symbols", + action="store_const", + const={}, + help="don't load any debugging symbols that were not explicitly added with -s", + ) + + parser.add_argument( + "--log-level", + choices=["debug", "info", "warning", "error", "critical", "none"], + default="warning", + help="log messages of at least the given level to standard error (default: warning)", + ) + parser.add_argument( + "-q", + "--quiet", + action=_QuietAction, + help="don't print any logs or download progress", + ) + parser.add_argument( + "script", + metavar="ARG", + type=str, + nargs=argparse.REMAINDER, + help="script to execute instead of running in interactive mode", + ) + parser.add_argument("--version", action="version", version=version) + + args = parser.parse_args() + + if args.script: + # A common mistake users make is running drgn $core_dump, which tries + # to run $core_dump as a Python script. Rather than failing later with + # some inscrutable syntax or encoding error, try to catch this early + # and provide a helpful message. + try: + script_type = _identify_script(args.script[0]) + except OSError as e: + sys.exit(e) + if script_type == "core": + sys.exit( + f"error: {args.script[0]} is a core dump\n" + f'Did you mean "-c {args.script[0]}"?' + ) + elif script_type == "elf": + sys.exit(f"error: {args.script[0]} is a binary, not a drgn script") + else: + print(version, file=sys.stderr, flush=True) + + if not args.quiet: + os.environ["DEBUGINFOD_PROGRESS"] = "1" + if args.log_level == "none": + logger.setLevel(logging.CRITICAL + 1) + else: + logger.setLevel(args.log_level.upper()) + + prog = drgn.Program() + try: + if args.core is not None: + prog.set_core_dump(args.core) + elif args.pid is not None: + prog.set_pid(args.pid or os.getpid()) + else: + prog.set_kernel() + except PermissionError as e: + print(e, file=sys.stderr) + if args.pid is not None: + print( + "error: attaching to live process requires ptrace attach permissions", + file=sys.stderr, + ) + elif args.core is None: + print( + "error: drgn debugs the live kernel by default, which requires root", + file=sys.stderr, + ) + sys.exit(1) + except OSError as e: + sys.exit(e) + except ValueError as e: + # E.g., "not an ELF core file" + sys.exit(f"error: {e}") + + if args.default_symbols is None: + args.default_symbols = {"default": True, "main": True} + try: + prog.load_debug_info(args.symbols, **args.default_symbols) + except drgn.MissingDebugInfoError as e: + logger.warning("%s", e) + + if args.script: + sys.argv = args.script + script = args.script[0] + if pkgutil.get_importer(script) is None: + sys.path.insert(0, os.path.dirname(os.path.abspath(script))) + runpy.run_path(script, init_globals={"prog": prog}, run_name="__main__") + else: + run_interactive(prog) + + +def run_interactive( + prog: drgn.Program, + banner_func: Optional[Callable[[str], str]] = None, + globals_func: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None, + quiet: bool = False, +) -> None: + """ + Run drgn's :ref:`interactive-mode` until the user exits. + + This function allows your application to embed the same REPL that drgn + provides when it is run on the command line in interactive mode. + + :param prog: Pre-configured program to run against. Available as a global + named ``prog`` in the CLI. + :param banner_func: Optional function to modify the printed banner. Called + with the default banner, and must return a string to use as the new + banner. The default banner does not include the drgn version, which can + be retrieved via :func:`version_header()`. + :param globals_func: Optional function to modify globals provided to the + session. Called with a dictionary of default globals, and must return a + dictionary to use instead. + :param quiet: Ignored. Will be removed in the future. + + .. note:: + + This function uses :mod:`readline` and modifies some settings. + Unfortunately, it is not possible for it to restore all settings. In + particular, it clears the ``readline`` history and resets the TAB + keybinding to the default. + + Applications using ``readline`` should save their history and clear any + custom settings before calling this function. After calling this + function, applications should restore their history and settings before + using ``readline``. + """ + init_globals: Dict[str, Any] = { + "prog": prog, + "drgn": drgn, + "__name__": "__main__", + "__doc__": None, + } + drgn_globals = [ + "NULL", + "Object", + "cast", + "container_of", + "execscript", + "offsetof", + "reinterpret", + "sizeof", + ] + for attr in drgn_globals: + init_globals[attr] = getattr(drgn, attr) + + banner = f"""\ +For help, type help(drgn). +>>> import drgn +>>> from drgn import {", ".join(drgn_globals)} +>>> from drgn.helpers.common import *""" + + module = importlib.import_module("drgn.helpers.common") + for name in module.__dict__["__all__"]: + init_globals[name] = getattr(module, name) + if prog.flags & drgn.ProgramFlags.IS_LINUX_KERNEL: + banner += "\n>>> from drgn.helpers.linux import *" + module = importlib.import_module("drgn.helpers.linux") + for name in module.__dict__["__all__"]: + init_globals[name] = getattr(module, name) + + if banner_func: + banner = banner_func(banner) + if globals_func: + init_globals = globals_func(init_globals) + + old_path = list(sys.path) + old_displayhook = sys.displayhook + old_history_length = readline.get_history_length() + old_completer = readline.get_completer() + histfile = os.path.expanduser("~/.drgn_history") + try: + readline.clear_history() + try: + readline.read_history_file(histfile) + except OSError as e: + if not isinstance(e, FileNotFoundError): + logger.warning("could not read history: %s", e) + + readline.set_history_length(1000) + readline.parse_and_bind("tab: complete") + readline.set_completer(Completer(init_globals).complete) + + sys.path.insert(0, "") + sys.displayhook = _displayhook + + try: + code.interact(banner=banner, exitmsg="", local=init_globals) + finally: + try: + readline.write_history_file(histfile) + except OSError as e: + logger.warning("could not write history: %s", e) + finally: + sys.displayhook = old_displayhook + sys.path[:] = old_path + readline.set_history_length(old_history_length) + readline.parse_and_bind("tab: self-insert") + readline.set_completer(old_completer) + readline.clear_history() diff --git a/drgn/helpers/__init__.py b/drgn/helpers/__init__.py index fc64a5ea0..6c25b75fc 100644 --- a/drgn/helpers/__init__.py +++ b/drgn/helpers/__init__.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Helpers diff --git a/drgn/helpers/common/__init__.py b/drgn/helpers/common/__init__.py index e5a3f1a4d..f611a6fd3 100644 --- a/drgn/helpers/common/__init__.py +++ b/drgn/helpers/common/__init__.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Common diff --git a/drgn/helpers/common/format.py b/drgn/helpers/common/format.py index e47c62905..2497a02f6 100644 --- a/drgn/helpers/common/format.py +++ b/drgn/helpers/common/format.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Formatting @@ -9,15 +9,16 @@ formatting different things as text. """ -from typing import Iterable, Tuple +from typing import Iterable, SupportsFloat, Tuple from drgn import IntegerLike, Type __all__ = ( + "decode_enum_type_flags", + "decode_flags", "escape_ascii_character", "escape_ascii_string", - "decode_flags", - "decode_enum_type_flags", + "number_in_binary_units", ) @@ -199,3 +200,41 @@ def decode_enum_type_flags( enumerators, # type: ignore # python/mypy#592 bit_numbers, ) + + +def number_in_binary_units(n: SupportsFloat, precision: int = 1) -> str: + """ + Format a number in binary units (i.e., "K" is 1024, "M" is 1024\\ :sup:`2`, + etc.). + + >>> number_in_binary_units(1280) + '1.2K' + + A precision can be specified: + + >>> number_in_binary_units(1280, precision=2) + '1.25K' + + Exact numbers are printed without a fractional part: + + >>> number_in_binary_units(1024 * 1024) + '1M' + + Numbers less than 1024 are not scaled: + + >>> number_in_binary_units(10) + '10' + + :param n: Number to format. + :param precision: Number of digits to include in fractional part. + """ + n = float(n) + for prefix in ("", "K", "M", "G", "T", "P", "E", "Z"): + if abs(n) < 1024: + break + n /= 1024 + else: + prefix = "Y" + if n.is_integer(): + precision = 0 + return f"{n:.{precision}f}{prefix}" diff --git a/drgn/helpers/common/memory.py b/drgn/helpers/common/memory.py index 507ee2ddb..90cc60d81 100644 --- a/drgn/helpers/common/memory.py +++ b/drgn/helpers/common/memory.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Memory @@ -14,7 +14,7 @@ import drgn from drgn import IntegerLike, Object, Program, SymbolKind from drgn.helpers.common.format import escape_ascii_string -from drgn.helpers.linux.slab import find_containing_slab_cache +from drgn.helpers.linux.slab import slab_object_info __all__ = ("identify_address",) @@ -39,14 +39,18 @@ def identify_address(prog: Program, addr: IntegerLike) -> Optional[str]: For all programs, this will identify addresses as follows: * Object symbols (e.g., addresses in global variables): - ``object symbol: {symbol_name}+{hex_offset}``. + ``object symbol: {symbol_name}+{hex_offset}`` (where ``hex_offset`` is + the offset from the beginning of the symbol in hexadecimal). * Function symbols (i.e., addresses in functions): ``function symbol: {symbol_name}+{hex_offset}``. * Other symbols: ``symbol: {symbol_name}+{hex_offset}``. Additionally, for the Linux kernel, this will identify: - * Slab objects: ``slab object: {slab_cache_name}``. + * Allocated slab objects: ``slab object: {slab_cache_name}+{hex_offset}`` + (where ``hex_offset`` is the offset from the beginning of the object in + hexadecimal). + * Free slab objects: ``free slab object: {slab_cache_name}+{hex_offset}``. This may recognize other types of addresses in the future. @@ -73,14 +77,22 @@ def identify_address( # type: ignore # Need positional-only arguments. if prog.flags & drgn.ProgramFlags.IS_LINUX_KERNEL: # Linux kernel-specific identification: - slab_cache = find_containing_slab_cache(prog, addr) - - if slab_cache: - # address is slab allocated - cache_name = escape_ascii_string( - slab_cache.name.string_(), escape_backslash=True - ) - return f"slab object: {cache_name}" + try: + slab = slab_object_info(prog, addr) + except NotImplementedError: + # Probably because virtual address translation isn't implemented + # for this architecture. + pass + else: + if slab: + # address is slab allocated + cache_name = escape_ascii_string( + slab.slab_cache.name.string_(), escape_backslash=True + ) + maybe_free = "" if slab.allocated else "free " + return ( + f"{maybe_free}slab object: {cache_name}+{hex(addr - slab.address)}" + ) # Check if address is of a symbol: try: diff --git a/drgn/helpers/common/stack.py b/drgn/helpers/common/stack.py new file mode 100644 index 000000000..a9d73d911 --- /dev/null +++ b/drgn/helpers/common/stack.py @@ -0,0 +1,119 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +""" +Stack +----- + +The ``drgn.helpers.common.stack`` module provides helpers for working with +stack traces. +""" + +from drgn import FaultError, PlatformFlags, StackTrace +from drgn.helpers.common.memory import identify_address + +__all__ = ("print_annotated_stack",) + + +def print_annotated_stack(trace: StackTrace) -> None: + """ + Print the contents of stack memory in a stack trace, annotating values that + can be identified. + + Currently, this will identify any addresses on the stack with + :func:`~drgn.helpers.common.memory.identify_address()`. + + >>> print_annotated_stack(prog.stack_trace(1)) + STACK POINTER VALUE + [stack frame #0 at 0xffffffff8dc93c41 (__schedule+0x429/0x488) in context_switch at ./kernel/sched/core.c:5209:2 (inlined)] + [stack frame #1 at 0xffffffff8dc93c41 (__schedule+0x429/0x488) in __schedule at ./kernel/sched/core.c:6521:8] + ffffa903c0013d28: ffffffff8d8497bf [function symbol: __flush_tlb_one_user+0x5] + ffffa903c0013d30: 000000008d849eb5 + ffffa903c0013d38: 0000000000000001 + ffffa903c0013d40: 0000000000000004 + ffffa903c0013d48: efdea37bb7cb1f00 + ffffa903c0013d50: ffff926641178000 [slab object: task_struct+0x0] + ffffa903c0013d58: ffff926641178000 [slab object: task_struct+0x0] + ffffa903c0013d60: ffffa903c0013e10 + ffffa903c0013d68: ffff926641177ff0 [slab object: mm_struct+0x70] + ffffa903c0013d70: ffff926641178000 [slab object: task_struct+0x0] + ffffa903c0013d78: ffff926641178000 [slab object: task_struct+0x0] + ffffa903c0013d80: ffffffff8dc93d29 [function symbol: schedule+0x89] + ... + + :param trace: Stack trace to print. + """ + prog = trace.prog + + # platform must be known if there is a stack trace + assert prog.platform is not None + + if prog.platform.flags & PlatformFlags.IS_LITTLE_ENDIAN: + byteorder = "little" + else: + byteorder = "big" + + if prog.platform.flags & PlatformFlags.IS_64_BIT: + word_size = 8 + line_format = "{:016x}: {:016x}{}" + print("STACK POINTER VALUE") + else: + word_size = 4 + line_format = "{:08x}: {:08x}{}" + print("STACK VALUE\nPOINTER") + + start = 0 + while start < len(trace): + # Find the bounds of this stack trace up to the next interrupted frame, + # which is often on a separate stack. + end = start + 1 + while end < len(trace) and not trace[end].interrupted: + end += 1 + + # Gather the frames for this stack. + frames = [trace[i] for i in range(start, end)] + frames_addrs = [frame.sp for frame in frames] + + start_addr = frames_addrs[0] + end_addr = frames_addrs[-1] + word_size - 1 + stack_size = end_addr - start_addr + 1 + + try: + stack_bytes = prog.read(start_addr, stack_size) + except FaultError: + # Couldn't read the stack. Just print the frames. + for frame in frames: + print(f"[stack frame {frame}]") + + start = end + continue + + frame_ind = 0 + + for offset in range(0, len(stack_bytes), word_size): + addr = start_addr + offset + + word_bytes = stack_bytes[offset : offset + word_size] + word_val = int.from_bytes( + word_bytes, + # The byteorder parameter is annotated as + # Literal['little', 'big'], but mypy infers that our byteorder + # variable is str. + byteorder=byteorder, # type: ignore[arg-type] + ) + + # There may be multiple frames matching this address (usually + # because of inline frames). + while frame_ind < len(frames_addrs) and addr == frames_addrs[frame_ind]: + frame = frames[frame_ind] + print(f"[stack frame {frame}]") + frame_ind += 1 + + identified = identify_address(prog, word_val) + if identified is None: + identified = "" + else: + identified = f" [{identified}]" + print(line_format.format(addr, word_val, identified)) + + start = end diff --git a/drgn/helpers/common/type.py b/drgn/helpers/common/type.py index 27197588b..26bac600f 100644 --- a/drgn/helpers/common/type.py +++ b/drgn/helpers/common/type.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Types diff --git a/drgn/helpers/linux/__init__.py b/drgn/helpers/linux/__init__.py index 810ab7e25..7ed3078ce 100644 --- a/drgn/helpers/linux/__init__.py +++ b/drgn/helpers/linux/__init__.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Linux Kernel diff --git a/drgn/helpers/linux/bitops.py b/drgn/helpers/linux/bitops.py index fd6210ddc..1bf5dbbd4 100644 --- a/drgn/helpers/linux/bitops.py +++ b/drgn/helpers/linux/bitops.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Bit Operations diff --git a/drgn/helpers/linux/block.py b/drgn/helpers/linux/block.py index 7f1e10a9f..505d712a5 100644 --- a/drgn/helpers/linux/block.py +++ b/drgn/helpers/linux/block.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Block Layer @@ -50,17 +50,39 @@ def disk_name(disk: Object) -> bytes: return disk.disk_name.string_() +def _class_to_subsys(class_: Object) -> Object: + # Walk the list of registered classes to find the struct subsys_private + # matching the given class. Note that before Linux kernel commit + # 2df418cf4b72 ("driver core: class: remove subsystem private pointer from + # struct class") (in v6.4), struct subsys_private could also be found in + # struct class::p, but it's easier to only maintain the newer code path. + for sp in list_for_each_entry( + "struct subsys_private", + class_.prog_["class_kset"].list.address_of_(), + "subsys.kobj.entry", + ): + if sp.member_("class") == class_: + return sp + else: + raise LookupError("block_class subsys_private not found") + + def _for_each_block_device(prog: Program) -> Iterator[Object]: try: - class_in_private = prog.cache["knode_class_in_device_private"] + devices, class_in_device_private = prog.cache["_for_each_block_device"] except KeyError: + devices = _class_to_subsys( + prog["block_class"].address_of_() + ).klist_devices.k_list.address_of_() # Linux kernel commit 570d0200123f ("driver core: move # device->knode_class to device_private") (in v5.1) moved the list # node. - class_in_private = prog.type("struct device_private").has_member("knode_class") - prog.cache["knode_class_in_device_private"] = class_in_private - devices = prog["block_class"].p.klist_devices.k_list.address_of_() - if class_in_private: + class_in_device_private = prog.type("struct device_private").has_member( + "knode_class" + ) + prog.cache["_for_each_block_device"] = devices, class_in_device_private + + if class_in_device_private: for device_private in list_for_each_entry( "struct device_private", devices, "knode_class.n_node" ): diff --git a/drgn/helpers/linux/boot.py b/drgn/helpers/linux/boot.py index 6e959bf4f..89618a79d 100644 --- a/drgn/helpers/linux/boot.py +++ b/drgn/helpers/linux/boot.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Boot diff --git a/drgn/helpers/linux/bpf.py b/drgn/helpers/linux/bpf.py index 8ca242fe7..75f94fe93 100644 --- a/drgn/helpers/linux/bpf.py +++ b/drgn/helpers/linux/bpf.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ BPF diff --git a/drgn/helpers/linux/cgroup.py b/drgn/helpers/linux/cgroup.py index ac36d8d39..b1150fe90 100644 --- a/drgn/helpers/linux/cgroup.py +++ b/drgn/helpers/linux/cgroup.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Cgroup @@ -37,7 +37,14 @@ def sock_cgroup_ptr(skcd: Object) -> Object: :param skcd: ``struct sock_cgroup_data *`` :return: ``struct cgroup *`` """ - return cast("struct cgroup *", skcd.val) + + # Since Linux kernel commit 8520e224f547 ("bpf, cgroups: Fix + # cgroup v2 fallback on v1/v2 mixed mode") (in v5.15), the sock_cgroup_data + # contains directly cgroup member (of struct cgroup * type). + try: + return skcd.cgroup + except AttributeError: + return cast("struct cgroup *", skcd.val) def cgroup_parent(cgrp: Object) -> Object: diff --git a/drgn/helpers/linux/cpumask.py b/drgn/helpers/linux/cpumask.py index 3001da913..13bbbc818 100644 --- a/drgn/helpers/linux/cpumask.py +++ b/drgn/helpers/linux/cpumask.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ CPU Masks diff --git a/drgn/helpers/linux/device.py b/drgn/helpers/linux/device.py index 6fde94295..8e8c14d52 100644 --- a/drgn/helpers/linux/device.py +++ b/drgn/helpers/linux/device.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Devices diff --git a/drgn/helpers/linux/fs.py b/drgn/helpers/linux/fs.py index 12d15d972..a71bc7736 100644 --- a/drgn/helpers/linux/fs.py +++ b/drgn/helpers/linux/fs.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Virtual Filesystem Layer @@ -339,7 +339,10 @@ def for_each_file(task: Object) -> Iterator[Tuple[int, Object]]: :param task: ``struct task_struct *`` :return: Iterator of (fd, ``struct file *``) tuples. """ - fdt = task.files.fdt.read_() + files = task.files.read_() + if not files: + return + fdt = files.fdt.read_() bits_per_long = 8 * sizeof(fdt.open_fds.type_.type) for i in range((fdt.max_fds.value_() + bits_per_long - 1) // bits_per_long): word = fdt.open_fds[i].value_() diff --git a/drgn/helpers/linux/idr.py b/drgn/helpers/linux/idr.py index c1d1ba2d9..dc3ee9fb3 100644 --- a/drgn/helpers/linux/idr.py +++ b/drgn/helpers/linux/idr.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ IDR @@ -7,14 +7,14 @@ The ``drgn.helpers.linux.idr`` module provides helpers for working with the IDR data structure in :linux:`include/linux/idr.h`. An IDR provides a mapping from -an ID to a pointer. This currently only supports Linux v4.11+; before this, -IDRs were not based on radix trees. +an ID to a pointer. """ +import operator from typing import Iterator, Tuple -from _drgn import _linux_helper_idr_find as idr_find -from drgn import Object +from _drgn import _linux_helper_idr_find +from drgn import NULL, IntegerLike, Object, cast, sizeof from drgn.helpers.linux.radixtree import radix_tree_for_each __all__ = ( @@ -22,6 +22,47 @@ "idr_for_each", ) +_IDR_BITS = 8 +_IDR_MASK = (1 << _IDR_BITS) - 1 + + +def idr_find(idr: Object, id: IntegerLike) -> Object: + """ + Look up the entry with the given ID in an IDR. + + :param idr: ``struct idr *`` + :param id: Entry ID. + :return: ``void *`` found entry, or ``NULL`` if not found. + """ + # Since Linux kernel commit 0a835c4f090a ("Reimplement IDR and IDA using + # the radix tree") (in v4.11), IDRs are backed by radix trees. Before that, + # they are a separate data structure. The helper in libdrgn only handles + # the radix tree version. + if hasattr(idr, "idr_rt"): + return _linux_helper_idr_find(idr, id) + else: + prog = idr.prog_ + id = operator.index(id) + + if id < 0: + return NULL(prog, "void *") + + p = idr.top.read_() + if not p: + return NULL(prog, "void *") + + n = (p.layer.value_() + 1) * _IDR_BITS + MAX_IDR_SHIFT = sizeof(prog.type("int")) * 8 - 1 + # Equivalent to id > idr_max(p->layer + 1) in the kernel. + if id >= (1 << min(n, MAX_IDR_SHIFT)): + return NULL(prog, "void *") + + while n > 0 and p: + n -= _IDR_BITS + p = p.ary[(id >> n) & _IDR_MASK].read_() + + return cast("void *", p) + def idr_for_each(idr: Object) -> Iterator[Tuple[int, Object]]: """ @@ -30,9 +71,29 @@ def idr_for_each(idr: Object) -> Iterator[Tuple[int, Object]]: :param idr: ``struct idr *`` :return: Iterator of (index, ``void *``) tuples. """ + # Since Linux kernel commit 0a835c4f090a ("Reimplement IDR and IDA using + # the radix tree") (in v4.11), IDRs are backed by radix trees. try: - base = idr.idr_base.value_() + idr_rt = idr.idr_rt except AttributeError: - base = 0 - for index, entry in radix_tree_for_each(idr.idr_rt.address_of_()): - yield index + base, entry + voidp_type = idr.prog_.type("void *") + + def aux(p: Object, id: int, n: int) -> Iterator[Tuple[int, Object]]: + p = p.read_() + if p: + if n == 0: + yield id, cast(voidp_type, p) + else: + n -= _IDR_BITS + for child in p.ary: + yield from aux(child, id, n) + id += 1 << n + + yield from aux(idr.top, 0, idr.layers.value_() * _IDR_BITS) + else: + try: + base = idr.idr_base.value_() + except AttributeError: + base = 0 + for index, entry in radix_tree_for_each(idr_rt.address_of_()): + yield index + base, entry diff --git a/drgn/helpers/linux/kconfig.py b/drgn/helpers/linux/kconfig.py index fb125cbb3..9b539e828 100644 --- a/drgn/helpers/linux/kconfig.py +++ b/drgn/helpers/linux/kconfig.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Kconfig @@ -48,7 +48,7 @@ def get_kconfig(prog: Program) -> Mapping[str, str]: except KeyError: raise LookupError( "kernel configuration data not found; kernel must be compiled with CONFIG_IKCONFIG" - ) + ) from None # The data is delimited by the magic strings "IKCFG_ST" and "IKCFG_ED" # plus a NUL byte. start = kernel_config_data.address_ + 8 # type: ignore[operator] diff --git a/drgn/helpers/linux/kernfs.py b/drgn/helpers/linux/kernfs.py index 0cbd21ff0..b60c958f3 100644 --- a/drgn/helpers/linux/kernfs.py +++ b/drgn/helpers/linux/kernfs.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Kernfs diff --git a/drgn/helpers/linux/list.py b/drgn/helpers/linux/list.py index 9ee7f4ea8..807afd3c4 100644 --- a/drgn/helpers/linux/list.py +++ b/drgn/helpers/linux/list.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Linked Lists @@ -19,6 +19,7 @@ "hlist_empty", "hlist_for_each", "hlist_for_each_entry", + "list_count_nodes", "list_empty", "list_first_entry", "list_first_entry_or_null", @@ -57,6 +58,15 @@ def list_is_singular(head: Object) -> bool: return next != head and next == head.prev +def list_count_nodes(head: Object) -> int: + """ + Return the number of nodes in a list. + + :param head: ``struct list_head *`` + """ + return sum(1 for _ in list_for_each(head)) + + def list_first_entry(head: Object, type: Union[str, Type], member: str) -> Object: """ Return the first entry in a list. diff --git a/drgn/helpers/linux/list_nulls.py b/drgn/helpers/linux/list_nulls.py index e4f862684..68e95bb4f 100644 --- a/drgn/helpers/linux/list_nulls.py +++ b/drgn/helpers/linux/list_nulls.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Nulls Lists diff --git a/drgn/helpers/linux/llist.py b/drgn/helpers/linux/llist.py index 08127dd94..2e543aa5f 100644 --- a/drgn/helpers/linux/llist.py +++ b/drgn/helpers/linux/llist.py @@ -1,5 +1,5 @@ # Copyright (c) 2022, Oracle and/or its affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Lockless Lists diff --git a/drgn/helpers/linux/mm.py b/drgn/helpers/linux/mm.py index 8452bd5a1..e28da56b7 100644 --- a/drgn/helpers/linux/mm.py +++ b/drgn/helpers/linux/mm.py @@ -1,19 +1,23 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Memory Management ----------------- The ``drgn.helpers.linux.mm`` module provides helpers for working with the -Linux memory management (MM) subsystem. Only AArch64 and x86-64 are currently -supported. +Linux memory management (MM) subsystem. Only AArch64, s390x, and x86-64 are +currently supported. """ import operator from typing import Iterator, List, Optional, Union, overload -from _drgn import _linux_helper_direct_mapping_offset, _linux_helper_read_vm +from _drgn import ( + _linux_helper_direct_mapping_offset, + _linux_helper_follow_phys, + _linux_helper_read_vm, +) from drgn import IntegerLike, Object, Program, cast from drgn.helpers.common.format import decode_enum_type_flags @@ -31,6 +35,9 @@ "compound_order", "decode_page_flags", "environ", + "follow_page", + "follow_pfn", + "follow_phys", "for_each_page", "page_size", "page_to_pfn", @@ -40,9 +47,12 @@ "pfn_to_virt", "phys_to_page", "phys_to_virt", + "totalram_pages", "virt_to_page", "virt_to_pfn", "virt_to_phys", + "vmalloc_to_page", + "vmalloc_to_pfn", # Generated by scripts/generate_page_flag_getters.py. "PageActive", "PageChecked", @@ -645,9 +655,28 @@ def compound_order(page: Object) -> Object: :param page: ``struct page *`` :return: ``unsigned int`` """ + prog = page.prog_ + if not PageHead(page): - return Object(page.prog_, "unsigned int", 0) - return cast("unsigned int", page[1].compound_order) + return Object(prog, "unsigned int", 0) + + # Before Linux kernel commit 379708ffde1b ("mm: add the first tail page to + # struct folio") (in v6.1), the compound order is in struct page. Since + # that commit, it is also in struct folio. Since Linux kernel commit + # 1c5509be58f6 ("mm: remove 'First tail page' members from struct page") + # (in v6.3), it is _only_ in struct folio. + try: + from_folio = prog.cache["compound_order_from_folio"] + except KeyError: + try: + from_folio = prog.type("struct folio").has_member("_folio_order") + except LookupError: + from_folio = False + prog.cache["compound_order_from_folio"] = from_folio + if from_folio: + return cast("unsigned int", cast("struct folio *", page)._folio_order) + else: + return cast("unsigned int", page[1].compound_order) def compound_nr(page: Object) -> Object: @@ -657,9 +686,7 @@ def compound_nr(page: Object) -> Object: :param page: ``struct page *`` :return: ``unsigned long`` """ - if not PageHead(page): - return Object(page.prog_, "unsigned long", 1) - return Object(page.prog_, "unsigned long", 1) << page[1].compound_order + return Object(page.prog_, "unsigned long", 1) << compound_order(page) def page_size(page: Object) -> Object: @@ -688,15 +715,56 @@ def decode_page_flags(page: Object) -> str: ) +# Get the struct page * for PFN 0. +def _page0(prog: Program) -> Object: + try: + return prog.cache["page0"] + except KeyError: + pass + try: + # With CONFIG_SPARSEMEM_VMEMMAP=y, page 0 is vmemmap. + page0 = prog["vmemmap"] + except KeyError: + contig_page_data = prog["contig_page_data"] + # With CONFIG_FLATMEM=y, page 0 is mem_map - ARCH_PFN_OFFSET, but we + # can't determine ARCH_PFN_OFFSET easily. Alternatively, + # contig_page_data.node_mem_map is the struct page * for + # contig_page_data.node_start_pfn, therefore page 0 is: + page0 = contig_page_data.node_mem_map - contig_page_data.node_start_pfn + # The struct page array is not contiguous for CONFIG_SPARSEMEM=y with + # CONFIG_SPARSEMEM_VMEMMAP=n or CONFIG_DISCONTIGMEM=y, so those are not + # supported yet. + prog.cache["page0"] = page0 + return page0 + + def for_each_page(prog: Program) -> Iterator[Object]: """ - Iterate over all pages in the system. + Iterate over every ``struct page *`` from the minimum to the maximum page. + + .. note:: + + This may include offline pages which don't have a valid ``struct + page``. Wrap accesses in a ``try`` ... ``except`` + :class:`drgn.FaultError`: + + >>> for page in for_each_page(prog): + ... try: + ... if PageLRU(page): + ... print(hex(page)) + ... except drgn.FaultError: + ... continue + 0xfffffb4a000c0000 + 0xfffffb4a000c0040 + ... + + This may be fixed in the future. :return: Iterator of ``struct page *`` objects. """ - vmemmap = prog["vmemmap"] + page0 = _page0(prog) for i in range(prog["min_low_pfn"], prog["max_pfn"]): - yield vmemmap + i + yield page0 + i @overload @@ -772,7 +840,7 @@ def page_to_pfn(page: Object) -> Object: :param page: ``struct page *`` :return: ``unsigned long`` """ - return cast("unsigned long", page - page.prog_["vmemmap"]) + return cast("unsigned long", page - _page0(page.prog_)) def page_to_phys(page: Object) -> Object: @@ -825,7 +893,7 @@ def pfn_to_page( # type: ignore # Need positional-only arguments. else: assert isinstance(prog_or_pfn, Program) prog = prog_or_pfn - return prog["vmemmap"] + pfn + return _page0(prog) + pfn @overload @@ -928,6 +996,28 @@ def virt_to_page(prog: Program, addr: IntegerLike) -> Object: The address can be given as an :class:`~drgn.Object` or as a :class:`~drgn.Program` and an integer. + .. _mm-helpers-direct-map: + + .. note:: + + This only works for virtual addresses from the "direct map". This + includes address from: + + * kmalloc + * Slab allocator + * Page allocator + + But not: + + * vmalloc + * vmap + * ioremap + * Symbols (function pointers, global variables) + + For vmalloc or vmap addresses, use :func:`vmalloc_to_page(addr) + `. For arbitrary kernel addresses, use + :func:`follow_page(prog["init_mm"].address_of_(), addr) `. + :param addr: ``void *`` :return: ``struct page *`` """ @@ -954,6 +1044,14 @@ def virt_to_pfn(prog: Program, addr: IntegerLike) -> Object: The address can be given as an :class:`~drgn.Object` or as a :class:`~drgn.Program` and an integer. + .. note:: + + This only works for virtual addresses from the :ref:`"direct map" + `. For vmalloc or vmap addresses, use + :func:`vmalloc_to_pfn(addr) `. For arbitrary kernel + addresses, use :func:`follow_pfn(prog["init_mm"].address_of_(), addr) + `. + :param addr: ``void *`` :return: ``unsigned long`` """ @@ -980,6 +1078,12 @@ def virt_to_phys(prog: Program, addr: IntegerLike) -> Object: The address can be given as an :class:`~drgn.Object` or as a :class:`~drgn.Program` and an integer. + .. note:: + + This only works for virtual addresses from the :ref:`"direct map" + `. For arbitrary kernel addresses, use + :func:`follow_phys(prog["init_mm"].address_of_(), addr) `. + :param addr: ``void *`` :return: ``phys_addr_t`` """ @@ -1003,6 +1107,125 @@ def virt_to_phys( # type: ignore # Need positional-only arguments. ) +def follow_page(mm: Object, addr: IntegerLike) -> Object: + """ + Get the page that a virtual address maps to in a virtual address space. + + >>> task = find_task(prog, 113) + >>> follow_page(task.mm, 0x7fffbbb6d4d0) + *(struct page *)0xffffbe4bc0337b80 = { + ... + } + + :param mm: ``struct mm_struct *`` + :param addr: ``void *`` + :return: ``struct page *`` + """ + return phys_to_page(follow_phys(mm, addr)) + + +def follow_pfn(mm: Object, addr: IntegerLike) -> Object: + """ + Get the page frame number (PFN) that a virtual address maps to in a virtual + address space. + + >>> task = find_task(prog, 113) + >>> follow_pfn(task.mm, 0x7fffbbb6d4d0) + (unsigned long)52718 + + :param mm: ``struct mm_struct *`` + :param addr: ``void *`` + :return: ``unsigned long`` + """ + return PHYS_PFN(follow_phys(mm, addr)) + + +def follow_phys(mm: Object, addr: IntegerLike) -> Object: + """ + Get the physical address that a virtual address maps to in a virtual + address space. + + >>> task = find_task(prog, 113) + >>> follow_phys(task.mm, 0x7fffbbb6d4d0) + (phys_addr_t)215934160 + + :param mm: ``struct mm_struct *`` + :param addr: ``void *`` + :return: ``phys_addr_t`` + """ + prog = mm.prog_ + return Object(prog, "phys_addr_t", _linux_helper_follow_phys(prog, mm.pgd, addr)) + + +@overload +def vmalloc_to_page(addr: Object) -> Object: + """""" + ... + + +@overload +def vmalloc_to_page(prog: Program, addr: IntegerLike) -> Object: + """ + Get the page containing a vmalloc or vmap address. + + The address can be given as an :class:`~drgn.Object` or as a + :class:`~drgn.Program` and an integer. + + >>> task = find_task(prog, 113) + >>> vmalloc_to_page(task.stack) + *(struct page *)0xffffbe4bc00a2200 = { + ... + } + + :param addr: ``void *`` + :return: ``struct page *`` + """ + ... + + +def vmalloc_to_page( # type: ignore # Need positional-only arguments. + prog_or_addr: Union[Program, Object], addr: Optional[IntegerLike] = None +) -> Object: + if addr is None: + assert isinstance(prog_or_addr, Object) + prog = prog_or_addr.prog_ + addr = prog_or_addr + else: + assert isinstance(prog_or_addr, Program) + prog = prog_or_addr + return follow_page(prog["init_mm"].address_of_(), addr) + + +@overload +def vmalloc_to_pfn(addr: Object) -> Object: + """""" + ... + + +@overload +def vmalloc_to_pfn(prog: Program, addr: IntegerLike) -> Object: + """ + Get the page frame number (PFN) containing a vmalloc or vmap address. + + The address can be given as an :class:`~drgn.Object` or as a + :class:`~drgn.Program` and an integer. + + >>> task = find_task(prog, 113) + >>> vmalloc_to_pfn(task.stack) + (unsigned long)10376 + + :param addr: ``void *`` + :return: ``unsigned long`` + """ + ... + + +def vmalloc_to_pfn( # type: ignore # Need positional-only arguments. + prog_or_addr: Union[Program, Object], addr: Optional[IntegerLike] = None +) -> Object: + return page_to_pfn(vmalloc_to_page(prog_or_addr, addr)) # type: ignore + + def access_process_vm(task: Object, address: IntegerLike, size: IntegerLike) -> bytes: """ Read memory from a task's virtual address space. @@ -1075,3 +1298,17 @@ def environ(task: Object) -> List[bytes]: env_start = mm.env_start.value_() env_end = mm.env_end.value_() return access_remote_vm(mm, env_start, env_end - env_start).split(b"\0")[:-1] + + +def totalram_pages(prog: Program) -> int: + """ + Return the total number of RAM memory pages. + """ + + try: + # The variable is present since Linux kernel commit ca79b0c211af63fa32 + # ("mm: convert totalram_pages and totalhigh_pages variables + # to atomic") (in v5.0). + return prog["_totalram_pages"].counter.value_() + except KeyError: + return prog["totalram_pages"].value_() diff --git a/drgn/helpers/linux/net.py b/drgn/helpers/linux/net.py index 9efbb3bfb..360ca2ee6 100644 --- a/drgn/helpers/linux/net.py +++ b/drgn/helpers/linux/net.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Networking @@ -12,7 +12,7 @@ import operator from typing import Iterator, Union -from drgn import NULL, IntegerLike, Object, Program, cast, container_of +from drgn import NULL, IntegerLike, Object, Program, Type, cast, container_of, sizeof from drgn.helpers.linux.fs import fget from drgn.helpers.linux.list import hlist_for_each_entry, list_for_each_entry from drgn.helpers.linux.list_nulls import hlist_nulls_for_each_entry @@ -26,8 +26,10 @@ "netdev_for_each_tx_queue", "netdev_get_by_index", "netdev_get_by_name", + "netdev_priv", "sk_fullsock", "sk_nulls_for_each", + "skb_shinfo", ) @@ -189,6 +191,32 @@ def netdev_get_by_name( return NULL(prog_or_net.prog_, "struct net_device *") +def netdev_priv(dev: Object, type: Union[str, Type] = "void") -> Object: + """ + Return the private data of a network device. + + >>> dev = netdev_get_by_name(prog, "wlp0s20f3") + >>> netdev_priv(dev) + (void *)0xffff9419c9dec9c0 + >>> netdev_priv(dev, "struct ieee80211_sub_if_data") + *(struct ieee80211_sub_if_data *)0xffff9419c9dec9c0 = { + ... + } + + :param dev: ``struct net_device *`` + :param type: Type of private data. + :return: ``type *`` + """ + prog = dev.prog_ + try: + offset = prog.cache["net_device_aligned_size"] + except KeyError: + # 31 is NETDEV_ALIGN - 1 + offset = (sizeof(prog.type("struct net_device")) + 31) & ~31 + prog.cache["net_device_aligned_size"] = offset + return Object(prog, prog.pointer_type(prog.type(type)), dev.value_() + offset) + + def sk_fullsock(sk: Object) -> bool: """ Check whether a socket is a full socket, i.e., not a time-wait or request @@ -213,3 +241,22 @@ def sk_nulls_for_each(head: Object) -> Iterator[Object]: "struct sock", head, "__sk_common.skc_nulls_node" ): yield sk + + +def skb_shinfo(skb: Object) -> Object: + """ + Get the shared info for a socket buffer. + + :param skb: ``struct sk_buff *`` + :return: ``struct skb_shared_info *`` + """ + prog = skb.prog_ + try: + NET_SKBUFF_DATA_USES_OFFSET = prog.cache["NET_SKBUFF_DATA_USES_OFFSET"] + except KeyError: + NET_SKBUFF_DATA_USES_OFFSET = sizeof(prog.type("long")) > 4 + prog.cache["NET_SKBUFF_DATA_USES_OFFSET"] = NET_SKBUFF_DATA_USES_OFFSET + if NET_SKBUFF_DATA_USES_OFFSET: + return cast("struct skb_shared_info *", skb.head + skb.end) + else: + return cast("struct skb_shared_info *", skb.end) diff --git a/drgn/helpers/linux/nodemask.py b/drgn/helpers/linux/nodemask.py index 5afe1b42e..1fc355829 100644 --- a/drgn/helpers/linux/nodemask.py +++ b/drgn/helpers/linux/nodemask.py @@ -1,5 +1,5 @@ # Copyright (c) ByteDance, Inc. and its affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ NUMA Node Masks diff --git a/drgn/helpers/linux/percpu.py b/drgn/helpers/linux/percpu.py index 3143533df..01904bd84 100644 --- a/drgn/helpers/linux/percpu.py +++ b/drgn/helpers/linux/percpu.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Per-CPU diff --git a/drgn/helpers/linux/pid.py b/drgn/helpers/linux/pid.py index ca14ad352..8bf45259d 100644 --- a/drgn/helpers/linux/pid.py +++ b/drgn/helpers/linux/pid.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Process IDS diff --git a/drgn/helpers/linux/printk.py b/drgn/helpers/linux/printk.py index c2f1d4937..7c14c8a3f 100644 --- a/drgn/helpers/linux/printk.py +++ b/drgn/helpers/linux/printk.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Log Buffer @@ -158,12 +158,23 @@ def add_record(current_id: int) -> None: def _get_printk_records_structured(prog: Program) -> List[PrintkRecord]: - printk_logp_type = prog.type("struct printk_log *") + try: + printk_logp_type = prog.type("struct printk_log *") + except LookupError: + # Before Linux kernel commit 62e32ac3505a ("printk: rename struct log + # to struct printk_log") (in v3.11), records were "struct log" instead + # of "struct printk_log". RHEL 7 kernel still uses old naming. + printk_logp_type = prog.type("struct log *") + have_caller_id = printk_logp_type.type.has_member("caller_id") LOG_CONT = prog["LOG_CONT"].value_() result = [] - log_buf = prog["log_buf"].read_() + # Between Linux kernel commits cbd357008604 ("bpf: verifier (add ability to + # receive verification log)") (in v3.18) and e7bf8249e8f1 ("bpf: + # encapsulate verifier log state into a structure") (in v4.15), + # kernel/bpf/verifier.c also contains a variable named log_buf. + log_buf = prog.object("log_buf", filename="printk.c").read_() current_idx = prog["log_first_idx"].read_() next_idx = prog["log_next_idx"].read_() seq = prog["log_first_seq"].value_() diff --git a/drgn/helpers/linux/radixtree.py b/drgn/helpers/linux/radixtree.py index 0339b0a08..f8118778d 100644 --- a/drgn/helpers/linux/radixtree.py +++ b/drgn/helpers/linux/radixtree.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Radix Trees @@ -7,36 +7,33 @@ The ``drgn.helpers.linux.radixtree`` module provides helpers for working with radix trees from :linux:`include/linux/radix-tree.h`. + +.. seealso:: + + `XArrays`_, which were introduced in Linux 4.20 as a replacement for radix + trees. """ from typing import Iterator, Tuple -from _drgn import _linux_helper_radix_tree_lookup as radix_tree_lookup -from drgn import Object, cast +from drgn import IntegerLike, Object +from drgn.helpers.linux.xarray import xa_for_each, xa_load __all__ = ( "radix_tree_for_each", "radix_tree_lookup", ) -_RADIX_TREE_ENTRY_MASK = 3 - - -def _is_internal_node(node: Object, internal_node: int) -> bool: - return (node.value_() & _RADIX_TREE_ENTRY_MASK) == internal_node - - -def _entry_to_node(node: Object, internal_node: int) -> Object: - return Object(node.prog_, node.type_, value=node.value_() & ~internal_node) +def radix_tree_lookup(root: Object, index: IntegerLike) -> Object: + """ + Look up the entry at a given index in a radix tree. -def _radix_tree_root_node(root: Object) -> Tuple[Object, int]: - try: - node = root.xa_head - except AttributeError: - return root.rnode.read_(), 1 - else: - return cast("struct xa_node *", node).read_(), 2 + :param root: ``struct radix_tree_root *`` + :param index: Entry index. + :return: ``void *`` found entry, or ``NULL`` if not found. + """ + return xa_load(root, index) def radix_tree_for_each(root: Object) -> Iterator[Tuple[int, Object]]: @@ -46,17 +43,4 @@ def radix_tree_for_each(root: Object) -> Iterator[Tuple[int, Object]]: :param root: ``struct radix_tree_root *`` :return: Iterator of (index, ``void *``) tuples. """ - node, RADIX_TREE_INTERNAL_NODE = _radix_tree_root_node(root) - - def aux(node: Object, index: int) -> Iterator[Tuple[int, Object]]: - if _is_internal_node(node, RADIX_TREE_INTERNAL_NODE): - parent = _entry_to_node(node, RADIX_TREE_INTERNAL_NODE) - for i, slot in enumerate(parent.slots): - yield from aux( - cast(parent.type_, slot).read_(), - index + (i << parent.shift.value_()), - ) - elif node: - yield index, cast("void *", node) - - yield from aux(node, 0) + return xa_for_each(root) diff --git a/drgn/helpers/linux/rbtree.py b/drgn/helpers/linux/rbtree.py index a654d94b9..b9a272f63 100644 --- a/drgn/helpers/linux/rbtree.py +++ b/drgn/helpers/linux/rbtree.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Red-Black Trees diff --git a/drgn/helpers/linux/sched.py b/drgn/helpers/linux/sched.py index b0fc5d0ee..4472e205f 100644 --- a/drgn/helpers/linux/sched.py +++ b/drgn/helpers/linux/sched.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ CPU Scheduler @@ -9,11 +9,20 @@ Linux CPU scheduler. """ -from _drgn import _linux_helper_idle_task as idle_task -from drgn import Object +from typing import Tuple + +from _drgn import ( + _linux_helper_cpu_curr as cpu_curr, + _linux_helper_idle_task as idle_task, + _linux_helper_task_cpu as task_cpu, +) +from drgn import Object, Program __all__ = ( + "cpu_curr", "idle_task", + "loadavg", + "task_cpu", "task_state_to_char", ) @@ -79,3 +88,17 @@ def task_state_to_char(task: Object) -> str: return "I" else: return char + + +def loadavg(prog: Program) -> Tuple[float, float, float]: + """ + Return system load averaged over 1, 5 and 15 minutes as + tuple of three float values. + + >>> loadavg(prog) + (2.34, 0.442, 1.33) + """ + + avenrun = prog["avenrun"] + vals = [avenrun[i].value_() / (1 << 11) for i in range(3)] + return (vals[0], vals[1], vals[2]) diff --git a/drgn/helpers/linux/slab.py b/drgn/helpers/linux/slab.py index 9defa5110..ab6f0bf84 100644 --- a/drgn/helpers/linux/slab.py +++ b/drgn/helpers/linux/slab.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Slab Allocator @@ -16,13 +16,25 @@ """ import operator -from typing import Iterator, Optional, Set, Union, overload - -from drgn import NULL, FaultError, IntegerLike, Object, Program, Type, cast, sizeof +from os import fsdecode +from typing import Callable, Dict, Iterator, Optional, Set, Tuple, Union, overload + +from drgn import ( + NULL, + FaultError, + IntegerLike, + Object, + Program, + Type, + cast, + container_of, + sizeof, +) from drgn.helpers.common.format import escape_ascii_string from drgn.helpers.linux.cpumask import for_each_online_cpu from drgn.helpers.linux.list import list_for_each_entry from drgn.helpers.linux.mm import ( + PageSlab, compound_head, for_each_page, page_to_virt, @@ -30,14 +42,17 @@ virt_to_page, ) from drgn.helpers.linux.percpu import per_cpu_ptr +from drgn.helpers.linux.rbtree import rbtree_inorder_for_each_entry __all__ = ( "find_containing_slab_cache", "find_slab_cache", "for_each_slab_cache", + "get_slab_cache_aliases", "print_slab_caches", "slab_cache_for_each_allocated_object", "slab_cache_is_merged", + "slab_object_info", ) @@ -92,6 +107,68 @@ def slab_cache_is_merged(slab_cache: Object) -> bool: return slab_cache.refcount > 1 +def get_slab_cache_aliases(prog: Program) -> Dict[str, str]: + """ + Return a dict mapping slab cache name to the cache it was merged with. + + The SLAB and SLUB subsystems can merge caches with similar settings and + object sizes, as described in the documentation of + :func:`slab_cache_is_merged()`. In some cases, the information about which + caches were merged is lost, but in other cases, we can reconstruct the info. + This function reconstructs the mapping, but requires that the kernel is + configured with ``CONFIG_SLUB`` and ``CONFIG_SYSFS``. + + The returned dict maps from original cache name, to merged cache name. You + can use this mapping to discover the correct cache to lookup via + :func:`find_slab_cache()`. The dict contains an entry only for caches which + were merged into a cache of a different name. + + >>> cache_to_merged = get_slab_cache_aliases(prog) + >>> cache_to_merged["dnotify_struct"] + 'avc_xperms_data' + >>> "avc_xperms_data" in cache_to_merged + False + >>> find_slab_cache(prog, "dnotify_struct") is None + True + >>> find_slab_cache(prog, "avc_xperms_data") is None + False + + :warning: This function will only work on kernels which are built with + ``CONFIG_SLUB`` and ``CONFIG_SYSFS`` enabled. + + :param prog: Program to search + :returns: Mapping of slab cache name to final merged name + :raises LookupError: If the helper fails because the debugged kernel + doesn't have the required configuration + """ + try: + slab_kset = prog["slab_kset"] + except KeyError: + raise LookupError( + "Couldn't find SLUB sysfs information: get_slab_cache_aliases() " + "requires CONFIG_SLUB and CONFIG_SYSFS enabled in the debugged " + "kernel." + ) from None + link_flag = prog.constant("KERNFS_LINK") + name_map = {} + for child in rbtree_inorder_for_each_entry( + "struct kernfs_node", + slab_kset.kobj.sd.dir.children.address_of_(), + "rb", + ): + if child.flags & link_flag: + cache = container_of( + cast("struct kobject *", child.symlink.target_kn.priv), + "struct kmem_cache", + "kobj", + ) + original_name = fsdecode(child.name.string_()) + target_name = fsdecode(cache.name.string_()) + if original_name != target_name: + name_map[original_name] = target_name + return name_map + + def for_each_slab_cache(prog: Program) -> Iterator[Object]: """ Iterate over all slab caches. @@ -125,73 +202,111 @@ def print_slab_caches(prog: Program) -> None: print(f"{name} ({s.type_.type_name()})0x{s.value_():x}") -def slab_cache_for_each_allocated_object( - slab_cache: Object, type: Union[str, Type] -) -> Iterator[Object]: - """ - Iterate over all allocated objects in a given slab cache. +# Between SLUB, SLAB, their respective configuration options, and the +# differences between kernel versions, there is a lot of state that we need to +# keep track of to inspect the slab allocator. It isn't pretty, but this class +# and its subclasses track all of that complexity so that we can share code +# between slab helpers. +class _SlabCacheHelper: + def __init__(self, slab_cache: Object) -> None: + self._prog = slab_cache.prog_ + self._slab_cache = slab_cache.read_() + + def _page_objects( + self, page: Object, slab: Object, pointer_type: Type + ) -> Iterator[Object]: + raise NotImplementedError() + + def for_each_allocated_object(self, type: Union[str, Type]) -> Iterator[Object]: + pointer_type = self._prog.pointer_type(self._prog.type(type)) + slab_type = _get_slab_type(self._prog) + PG_slab_mask = 1 << self._prog.constant("PG_slab") + for page in for_each_page(self._prog): + try: + if not page.flags & PG_slab_mask: + continue + except FaultError: + continue + slab = cast(slab_type, page) + if slab.slab_cache == self._slab_cache: + yield from self._page_objects(page, slab, pointer_type) - Only the SLUB and SLAB allocators are supported; SLOB does not store enough - information to identify objects in a slab cache. + def object_info( + self, page: Object, slab: Object, addr: int + ) -> "Optional[SlabObjectInfo]": + raise NotImplementedError() - >>> dentry_cache = find_slab_cache(prog, "dentry") - >>> next(slab_cache_for_each_allocated_object(dentry_cache, "struct dentry")) - *(struct dentry *)0xffff905e41404000 = { - ... - } - :param slab_cache: ``struct kmem_cache *`` - :param type: Type of object in the slab cache. - :return: Iterator of ``type *`` objects. - """ - prog = slab_cache.prog_ - slab_cache_size = slab_cache.size.value_() - pointer_type = prog.pointer_type(prog.type(type)) +class _SlabCacheHelperSlub(_SlabCacheHelper): + def __init__(self, slab_cache: Object) -> None: + super().__init__(slab_cache) - try: - freelist_type = prog.type("freelist_idx_t *") - slub = False - except LookupError: - slub = True + self._slab_cache_size = slab_cache.size.value_() - if slub: try: - red_left_pad = slab_cache.red_left_pad.value_() + self._red_left_pad = slab_cache.red_left_pad.value_() except AttributeError: - red_left_pad = 0 + self._red_left_pad = 0 # In SLUB, the freelist is a linked list with the next pointer located # at ptr + slab_cache->offset. - try: - freelist_offset = slab_cache.offset.value_() - except AttributeError: - raise ValueError("SLOB is not supported") from None + freelist_offset = slab_cache.offset.value_() # If CONFIG_SLAB_FREELIST_HARDENED is enabled, then the next pointer is # obfuscated using slab_cache->random. try: freelist_random = slab_cache.random.value_() except AttributeError: - - def _freelist_dereference(ptr_addr: int) -> int: - return prog.read_word(ptr_addr) - + self._freelist_dereference: Callable[[int], int] = self._prog.read_word else: - ulong_size = sizeof(prog.type("unsigned long")) - - def _freelist_dereference(ptr_addr: int) -> int: + ulong_size = sizeof(self._prog.type("unsigned long")) + + # Since Linux kernel commit 1ad53d9fa3f6 ("slub: improve bit + # diffusion for freelist ptr obfuscation") in v5.7, a swab() was + # added to the freelist dereferencing calculation. This commit was + # backported to all stable branches which have + # CONFIG_SLAB_FREELIST_HARDENED, but you can still encounter some + # older stable kernels which don't have it. Unfortunately, there's + # no easy way to detect whether it is in effect, since the commit + # adds no struct field or other detectable difference. + # + # To handle this, we implement both methods, and we start out with a + # "trial" function. On the first time we encounter a non-NULL + # freelist, we try using the method with the swab(), and test + # whether the resulting pointer may be dereferenced. If it can, we + # commit to using that method forever. If it cannot, we switch to + # the version without swab() and commit to using that. + + def _freelist_dereference_swab(ptr_addr: int) -> int: # *ptr_addr ^ slab_cache->random ^ byteswap(ptr_addr) return ( - prog.read_word(ptr_addr) + self._prog.read_word(ptr_addr) ^ freelist_random ^ int.from_bytes(ptr_addr.to_bytes(ulong_size, "little"), "big") ) + def _freelist_dereference_no_swab(ptr_addr: int) -> int: + # *ptr_addr ^ slab_cache->random ^ ptr_addr + return self._prog.read_word(ptr_addr) ^ freelist_random ^ ptr_addr + + def _try_hardened_freelist_dereference(ptr_addr: int) -> int: + result = _freelist_dereference_swab(ptr_addr) + if result: + try: + self._prog.read_word(result) + self._freelist_dereference = _freelist_dereference_swab + except FaultError: + result = _freelist_dereference_no_swab(ptr_addr) + self._freelist_dereference = _freelist_dereference_no_swab + return result + + self._freelist_dereference = _try_hardened_freelist_dereference + def _slub_get_freelist(freelist: Object, freelist_set: Set[int]) -> None: ptr = freelist.value_() while ptr: freelist_set.add(ptr) - ptr = _freelist_dereference(ptr + freelist_offset) + ptr = self._freelist_dereference(ptr + freelist_offset) cpu_freelists: Set[int] = set() cpu_slab = slab_cache.cpu_slab.read_() @@ -199,65 +314,256 @@ def _slub_get_freelist(freelist: Object, freelist_set: Set[int]) -> None: # page to struct slab by spatch") (in v5.17), the current slab for a # CPU is `struct slab *slab`. Before that, it is `struct page *page`. cpu_slab_attr = "slab" if hasattr(cpu_slab, "slab") else "page" - for cpu in for_each_online_cpu(prog): + for cpu in for_each_online_cpu(self._prog): this_cpu_slab = per_cpu_ptr(cpu_slab, cpu) slab = getattr(this_cpu_slab, cpu_slab_attr).read_() if slab and slab.slab_cache == slab_cache: _slub_get_freelist(this_cpu_slab.freelist, cpu_freelists) - def _slab_page_objects(page: Object, slab: Object) -> Iterator[Object]: + self._slub_get_freelist = _slub_get_freelist + self._cpu_freelists = cpu_freelists + + def _page_objects( + self, page: Object, slab: Object, pointer_type: Type + ) -> Iterator[Object]: + freelist: Set[int] = set() + self._slub_get_freelist(slab.freelist, freelist) + addr = page_to_virt(page).value_() + self._red_left_pad + end = addr + self._slab_cache_size * slab.objects + while addr < end: + if addr not in freelist and addr not in self._cpu_freelists: + yield Object(self._prog, pointer_type, value=addr) + addr += self._slab_cache_size + + def object_info(self, page: Object, slab: Object, addr: int) -> "SlabObjectInfo": + first_addr = page_to_virt(page).value_() + self._red_left_pad + address = ( + first_addr + + (addr - first_addr) // self._slab_cache_size * self._slab_cache_size + ) + if address in self._cpu_freelists: + allocated = False + else: freelist: Set[int] = set() - _slub_get_freelist(slab.freelist, freelist) - addr = page_to_virt(page).value_() + red_left_pad - end = addr + slab_cache_size * slab.objects - while addr < end: - if addr not in freelist and addr not in cpu_freelists: - yield Object(prog, pointer_type, value=addr) - addr += slab_cache_size + self._slub_get_freelist(slab.freelist, freelist) + allocated = address not in freelist + return SlabObjectInfo(self._slab_cache, slab, address, allocated) - else: + +class _SlabCacheHelperSlab(_SlabCacheHelper): + def __init__(self, slab_cache: Object) -> None: + super().__init__(slab_cache) + + self._slab_cache_size = slab_cache.size.value_() + + self._freelist_type = self._prog.type("freelist_idx_t *") try: - obj_offset = slab_cache.obj_offset.value_() + self._obj_offset = slab_cache.obj_offset.value_() except AttributeError: - obj_offset = 0 + self._obj_offset = 0 - slab_cache_num = slab_cache.num.value_() + self._slab_cache_num = slab_cache.num.value_() cpu_cache = slab_cache.cpu_cache.read_() cpu_caches_avail: Set[int] = set() - for cpu in for_each_online_cpu(prog): + for cpu in for_each_online_cpu(self._prog): ac = per_cpu_ptr(cpu_cache, cpu) for i in range(ac.avail): cpu_caches_avail.add(ac.entry[i].value_()) + self._cpu_caches_avail = cpu_caches_avail + + def _slab_freelist(self, slab: Object) -> Set[int]: + # In SLAB, the freelist is an array of free object indices. + freelist = cast(self._freelist_type, slab.freelist) + return {freelist[i].value_() for i in range(slab.active, self._slab_cache_num)} + + def _page_objects( + self, page: Object, slab: Object, pointer_type: Type + ) -> Iterator[Object]: + freelist = self._slab_freelist(slab) + s_mem = slab.s_mem.value_() + for i in range(self._slab_cache_num): + if i in freelist: + continue + addr = s_mem + i * self._slab_cache_size + self._obj_offset + if addr in self._cpu_caches_avail: + continue + yield Object(self._prog, pointer_type, value=addr) + + def object_info(self, page: Object, slab: Object, addr: int) -> "SlabObjectInfo": + s_mem = slab.s_mem.value_() + object_index = (addr - s_mem) // self._slab_cache_size + object_address = s_mem + object_index * self._slab_cache_size + return SlabObjectInfo( + self._slab_cache, + slab, + object_address, + allocated=object_address not in self._cpu_caches_avail + and object_index not in self._slab_freelist(slab), + ) - def _slab_freelist(slab: Object) -> Set[int]: - # In SLAB, the freelist is an array of free object indices. - freelist = cast(freelist_type, slab.freelist) - return {freelist[i].value_() for i in range(slab.active, slab_cache_num)} - def _slab_page_objects(page: Object, slab: Object) -> Iterator[Object]: - freelist = _slab_freelist(slab) - s_mem = slab.s_mem.value_() - for i in range(slab_cache_num): - if i in freelist: - continue - addr = s_mem + i * slab_cache_size + obj_offset - if addr in cpu_caches_avail: - continue - yield Object(prog, pointer_type, value=addr) +class _SlabCacheHelperSlob(_SlabCacheHelper): + def for_each_allocated_object(self, type: Union[str, Type]) -> Iterator[Object]: + raise ValueError("SLOB is not supported") + + def object_info(self, page: Object, slab: Object, addr: int) -> None: + return None - slab_type = _get_slab_type(prog) - PG_slab_mask = 1 << prog.constant("PG_slab") - for page in for_each_page(prog): +def _get_slab_cache_helper(slab_cache: Object) -> _SlabCacheHelper: + prog = slab_cache.prog_ + try: + type = prog.cache["slab_cache_helper_type"] + except KeyError: try: - if not page.flags & PG_slab_mask: - continue - except FaultError: - continue - slab = cast(slab_type, page) - if slab.slab_cache == slab_cache: - yield from _slab_page_objects(page, slab) + prog.type("freelist_idx_t *") + type = _SlabCacheHelperSlab + except LookupError: + if hasattr(slab_cache, "offset"): + type = _SlabCacheHelperSlub + else: + type = _SlabCacheHelperSlob + prog.cache["slab_cache_helper_type"] = type + return type(slab_cache) + + +def slab_cache_for_each_allocated_object( + slab_cache: Object, type: Union[str, Type] +) -> Iterator[Object]: + """ + Iterate over all allocated objects in a given slab cache. + + Only the SLUB and SLAB allocators are supported; SLOB does not store enough + information to identify objects in a slab cache. + + >>> dentry_cache = find_slab_cache(prog, "dentry") + >>> next(slab_cache_for_each_allocated_object(dentry_cache, "struct dentry")) + *(struct dentry *)0xffff905e41404000 = { + ... + } + + :param slab_cache: ``struct kmem_cache *`` + :param type: Type of object in the slab cache. + :return: Iterator of ``type *`` objects. + """ + return _get_slab_cache_helper(slab_cache).for_each_allocated_object(type) + + +def _find_containing_slab( + prog: Program, addr: int +) -> Optional[Tuple[Object, Object, Object]]: + start_addr = pfn_to_virt(prog["min_low_pfn"]).value_() + end_addr = (pfn_to_virt(prog["max_low_pfn"]) + prog["PAGE_SIZE"]).value_() + if addr < start_addr or addr >= end_addr: + # Not a directly mapped address + return None + + page = virt_to_page(prog, addr) + + try: + page = compound_head(page) + if not PageSlab(page): + return None + except FaultError: + # Page does not exist + return None + + slab = cast(_get_slab_type(prog), page) + try: + return slab.slab_cache, page, slab + except AttributeError: + # SLOB + return None + + +@overload +def slab_object_info(addr: Object) -> Optional["SlabObjectInfo"]: + """""" + ... + + +@overload +def slab_object_info(prog: Program, addr: IntegerLike) -> "Optional[SlabObjectInfo]": + """ + Get information about an address if it is in a slab object. + + >>> ptr = find_task(prog, 1).comm.address_of_() + >>> info = slab_object_info(ptr) + >>> info + SlabObjectInfo(slab_cache=Object(prog, 'struct kmem_cache *', address=0xffffdb93c0045e18), slab=Object(prog, 'struct slab *', value=0xffffdb93c0045e00), address=0xffffa2bf81178000, allocated=True) + + Note that :attr:`SlabObjectInfo.address` is the start address of the + object, which may be less than *addr* if *addr* points to a member inside + of the object: + + >>> ptr.value_() - info.address + 1496 + >>> offsetof(prog.type("struct task_struct"), "comm") + 1496 + + The address can be given as an :class:`~drgn.Object` or as a + :class:`~drgn.Program` and an integer. + + Note that SLOB does not store enough information to identify slab objects, + so if the kernel is configured to use SLOB, this will always return + ``None``. + + :param addr: ``void *`` + :return: :class:`SlabObjectInfo` if *addr* is in a slab object, or ``None`` + if not. + """ + ... + + +def slab_object_info( # type: ignore # Need positional-only arguments. + prog_or_addr: Union[Program, Object], addr: Optional[IntegerLike] = None +) -> Optional["SlabObjectInfo"]: + if addr is None: + assert isinstance(prog_or_addr, Object) + prog = prog_or_addr.prog_ + addr = prog_or_addr + else: + assert isinstance(prog_or_addr, Program) + prog = prog_or_addr + addr = operator.index(addr) + result = _find_containing_slab(prog, addr) + if result is None: + return None + slab_cache, page, slab = result + return _get_slab_cache_helper(slab_cache).object_info(page, slab, addr) + + +class SlabObjectInfo: + """Information about an object in the slab allocator.""" + + slab_cache: Object + """``struct kmem_cache *`` that the slab object is from.""" + + slab: Object + """ + Slab containing the slab object. + + Since Linux v5.17, this is a ``struct slab *``. Before that, it is a + ``struct page *``. + """ + + address: int + """Address of the slab object.""" + + allocated: bool + """``True`` if the object is allocated, ``False`` if it is free.""" + + def __init__( + self, slab_cache: Object, slab: Object, address: int, allocated: bool + ) -> None: + self.slab_cache = slab_cache + self.slab = slab + self.address = address + self.allocated = allocated + + def __repr__(self) -> str: + return f"SlabObjectInfo(slab_cache={self.slab_cache!r}, slab={self.slab!r}, address={hex(self.address)}, allocated={self.allocated})" @overload @@ -296,28 +602,7 @@ def find_containing_slab_cache( # type: ignore # Need positional-only argument assert isinstance(prog_or_addr, Program) prog = prog_or_addr addr = operator.index(addr) - - start_addr = pfn_to_virt(prog["min_low_pfn"]).value_() - end_addr = (pfn_to_virt(prog["max_pfn"]) + prog["PAGE_SIZE"]).value_() - if addr < start_addr or addr >= end_addr: - # Not a directly mapped address - return NULL(prog, "struct kmem_cache *") - - page = virt_to_page(prog, addr) - - try: - page = compound_head(page) - page_flags = page.flags - except FaultError: - # Page does not exist - return NULL(prog, "struct kmem_cache *") - - if not page_flags & (1 << prog.constant("PG_slab")): - # Not a slab page - return NULL(prog, "struct kmem_cache *") - - slab = cast(_get_slab_type(prog), page) - try: - return slab.slab_cache - except AttributeError: + result = _find_containing_slab(prog, addr) + if result is None: return NULL(prog, "struct kmem_cache *") + return result[0].read_() diff --git a/drgn/helpers/linux/tc.py b/drgn/helpers/linux/tc.py index 42022453c..3bbf73c72 100644 --- a/drgn/helpers/linux/tc.py +++ b/drgn/helpers/linux/tc.py @@ -1,5 +1,5 @@ # Copyright (c) ByteDance, Inc. and its affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Traffic Control (TC) diff --git a/drgn/helpers/linux/tcp.py b/drgn/helpers/linux/tcp.py index 0f92551af..2981355e5 100644 --- a/drgn/helpers/linux/tcp.py +++ b/drgn/helpers/linux/tcp.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ TCP diff --git a/drgn/helpers/linux/user.py b/drgn/helpers/linux/user.py index 32ee4fa92..091b02076 100644 --- a/drgn/helpers/linux/user.py +++ b/drgn/helpers/linux/user.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ Users diff --git a/drgn/helpers/linux/xarray.py b/drgn/helpers/linux/xarray.py new file mode 100644 index 000000000..77d318d1c --- /dev/null +++ b/drgn/helpers/linux/xarray.py @@ -0,0 +1,240 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +""" +XArrays +------- + +The ``drgn.helpers.linux.xarray`` module provides helpers for working with the +`XArray `_ data structure from +:linux:`include/linux/xarray.h`. + +.. note:: + + XArrays were introduced in Linux 4.20 as a replacement for `radix trees`_. + To make it easier to work with data structures that were changed from a + radix tree to an XArray (like ``struct address_space::i_pages``), drgn + treats XArrays and radix trees interchangeably in some cases. + + Specifically, :func:`~drgn.helpers.linux.xarray.xa_load()` is equivalent to + :func:`~drgn.helpers.linux.radixtree.radix_tree_lookup()`, and + :func:`~drgn.helpers.linux.xarray.xa_for_each()` is equivalent to + :func:`~drgn.helpers.linux.radixtree.radix_tree_for_each()`, except that + the radix tree helpers assume ``advanced=False``. (Therefore, + :func:`~drgn.helpers.linux.xarray.xa_load()` and + :func:`~drgn.helpers.linux.xarray.xa_for_each()` also accept a ``struct + radix_tree_root *``, and + :func:`~drgn.helpers.linux.radixtree.radix_tree_lookup()` and + :func:`~drgn.helpers.linux.radixtree.radix_tree_for_each()` also accept a + ``struct xarray *``.) +""" + +from typing import Iterator, Optional, Tuple + +from _drgn import _linux_helper_xa_load +from drgn import NULL, IntegerLike, Object, cast + +__all__ = ( + "xa_for_each", + "xa_is_value", + "xa_is_zero", + "xa_load", + "xa_to_value", +) + + +_XA_ZERO_ENTRY = 1030 # xa_mk_internal(257) + + +def xa_load(xa: Object, index: IntegerLike, *, advanced: bool = False) -> Object: + """ + Look up the entry at a given index in an XArray. + + >>> entry = xa_load(inode.i_mapping.i_pages.address_of_(), 2) + >>> cast("struct page *", entry) + *(struct page *)0xffffed6980306f40 = { + ... + } + + :param xa: ``struct xarray *`` + :param index: Entry index. + :param advanced: Whether to return nodes only visible to the XArray + advanced API. If ``False``, zero entries (see :func:`xa_is_zero()`) + will be returned as ``NULL``. + :return: ``void *`` found entry, or ``NULL`` if not found. + """ + entry = _linux_helper_xa_load(xa, index) + if not advanced and entry.value_() == _XA_ZERO_ENTRY: + return NULL(xa.prog_, "void *") + return entry + + +class _XAIteratorNode: + def __init__(self, node: Object, index: int) -> None: + self.slots = node.slots + self.shift = node.shift.value_() + self.index = index + self.next_slot = 0 + + +def xa_for_each(xa: Object, *, advanced: bool = False) -> Iterator[Tuple[int, Object]]: + """ + Iterate over all of the entries in an XArray. + + >>> for index, entry in xa_for_each(inode.i_mapping.i_pages.address_of_()): + ... print(index, entry) + ... + 0 (void *)0xffffed6980356140 + 1 (void *)0xffffed6980306f80 + 2 (void *)0xffffed6980306f40 + 3 (void *)0xffffed6980355b40 + + :param xa: ``struct xarray *`` + :param advanced: Whether to return nodes only visible to the XArray + advanced API. If ``False``, zero entries (see :func:`xa_is_zero()`) + will be skipped. + :return: Iterator of (index, ``void *``) tuples. + """ + prog = xa.prog_ + + def should_yield(entry_value: int) -> bool: + return entry_value != 0 + + # This handles three cases: + # + # 1. XArrays. + # 2. Radix trees since Linux kernel commit f8d5d0cc145c ("xarray: Add + # definition of struct xarray") (in v4.20) redefined them in terms of + # XArrays. These reuse the XArray structures and are close enough to + # case 1 that the same code handles both. + # 3. Radix trees before that commit. These are similar to cases 1 and 2, + # but they have different type and member names, use different flags in + # the lower bits (see Linux kernel commit 3159f943aafd ("xarray: Replace + # exceptional entries") (in v4.20)), and represent sibling entries + # differently (see Linux kernel commit 02c02bf12c5d ("xarray: Change + # definition of sibling entries") (in v4.20)). + try: + entry = xa.xa_head.read_() + except AttributeError: + entry = xa.rnode + node_type = entry.type_ + entry = cast("void *", entry) + + # Return > 0 if radix_tree_is_internal_node(), < 0 if + # is_sibling_entry(), and 0 otherwise. + def is_internal(slots: Optional[Object], entry_value: int) -> int: + if (entry_value & 3) == 1: + # slots must be a reference object, so address_ is never None. + if slots is not None and ( + slots.address_ <= entry_value < slots[len(slots)].address_ # type: ignore[operator] + ): + return -1 + else: + return 1 + return 0 + + # entry_to_node() + def to_node(entry_value: int) -> Object: + return Object(prog, node_type, entry_value - 1) + + else: + node_type = prog.type("struct xa_node *") + + # Return > 0 if xa_is_node(), < 0 if xa_is_sibling(), and 0 otherwise. + def is_internal(slots: Optional[Object], entry_value: int) -> int: + if (entry_value & 3) == 2: + if entry_value > 4096: + return 1 + elif entry_value < 256: + return -1 + return 0 + + # xa_to_node() + def to_node(entry_value: int) -> Object: + return Object(prog, node_type, entry_value - 2) + + if not advanced: + # We're intentionally redefining should_yield() for this case. + def should_yield(entry_value: int) -> bool: # noqa: F811 + return entry_value != 0 and entry_value != _XA_ZERO_ENTRY + + entry_value = entry.value_() + internal = is_internal(None, entry_value) + if internal > 0: + stack = [_XAIteratorNode(to_node(entry_value), 0)] + else: + if internal == 0 and should_yield(entry_value): + yield 0, entry + return + + while stack: + node = stack[-1] + if node.next_slot >= len(node.slots): + stack.pop() + continue + + entry = node.slots[node.next_slot].read_() + entry_value = entry.value_() + + index = node.index + (node.next_slot << node.shift) + node.next_slot += 1 + + internal = is_internal(node.slots, entry_value) + if internal > 0: + stack.append(_XAIteratorNode(to_node(entry_value), index)) + elif internal == 0 and should_yield(entry_value): + yield index, entry + + +def xa_is_value(entry: Object) -> bool: + """ + Return whether an XArray entry is a value. + + See :func:`xa_to_value()`. + + :param entry: ``void *`` + """ + return (entry.value_() & 1) != 0 + + +def xa_to_value(entry: Object) -> Object: + """ + Return the value in an XArray entry. + + In addition to pointers, XArrays can store integers between 0 and + ``LONG_MAX``. If :func:`xa_is_value()` returns ``True``, use this to get + the stored integer. + + >>> entry = xa_load(xa, 9) + >>> entry + (void *)0xc9 + >>> xa_is_value(entry) + True + >>> xa_to_value(entry) + (unsigned long)100 + + :param entry: ``void *`` + :return: ``unsigned long`` + """ + return cast("unsigned long", entry) >> 1 + + +def xa_is_zero(entry: Object) -> bool: + """ + Return whether an XArray entry is a "zero" entry. + + A zero entry is an entry that was reserved but is not present. These are + only visible to the XArray advanced API, so they are only returned by + :func:`xa_load()` and :func:`xa_for_each()` when ``advanced = True``. + + >>> entry = xa_load(xa, 10, advanced=True) + >>> entry + (void *)0x406 + >>> xa_is_zero(entry) + True + >>> xa_load(xa, 10) + (void *)0 + + :param entry: ``void *`` + """ + return entry.value_() == _XA_ZERO_ENTRY diff --git a/drgn/internal/__init__.py b/drgn/internal/__init__.py index 7400b44e2..cb173d50f 100644 --- a/drgn/internal/__init__.py +++ b/drgn/internal/__init__.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ drgn internals diff --git a/drgn/internal/cli.py b/drgn/internal/cli.py deleted file mode 100644 index 40d01d992..000000000 --- a/drgn/internal/cli.py +++ /dev/null @@ -1,242 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later - -"""drgn command line interface""" - -import argparse -import builtins -import code -import importlib -import os -import os.path -import pkgutil -import runpy -import shutil -import sys -from typing import Any, Dict - -import drgn - - -def _identify_script(path: str) -> str: - EI_NIDENT = 16 - SIZEOF_E_TYPE = 2 - - with open(path, "rb") as f: - header = f.read(EI_NIDENT + SIZEOF_E_TYPE) - - ELFMAG = b"\177ELF" - EI_DATA = 5 - ELFDATA2LSB = 1 - ELFDATA2MSB = 2 - ET_CORE = 4 - - if len(header) < EI_NIDENT + SIZEOF_E_TYPE or header[:4] != ELFMAG: - return "other" - - if header[EI_DATA] == ELFDATA2LSB: - byteorder = "little" - elif header[EI_DATA] == ELFDATA2MSB: - byteorder = "big" - else: - return "elf" - - e_type = int.from_bytes( - header[EI_NIDENT : EI_NIDENT + SIZEOF_E_TYPE], - byteorder, # type: ignore[arg-type] # python/mypy#9057 - ) - return "core" if e_type == ET_CORE else "elf" - - -def displayhook(value: Any) -> None: - if value is None: - return - setattr(builtins, "_", None) - if isinstance(value, drgn.Object): - text = value.format_(columns=shutil.get_terminal_size((0, 0)).columns) - elif isinstance(value, (drgn.StackFrame, drgn.StackTrace, drgn.Type)): - text = str(value) - else: - text = repr(value) - try: - sys.stdout.write(text) - except UnicodeEncodeError: - encoded = text.encode(sys.stdout.encoding, "backslashreplace") - if hasattr(sys.stdout, "buffer"): - sys.stdout.buffer.write(encoded) - else: - text = encoded.decode(sys.stdout.encoding, "strict") - sys.stdout.write(text) - sys.stdout.write("\n") - setattr(builtins, "_", value) - - -def main() -> None: - python_version = ".".join(str(v) for v in sys.version_info[:3]) - libkdumpfile = f'with{"" if drgn._with_libkdumpfile else "out"} libkdumpfile' - version = f"drgn {drgn.__version__} (using Python {python_version}, elfutils {drgn._elfutils_version}, {libkdumpfile})" - parser = argparse.ArgumentParser(prog="drgn", description="Programmable debugger") - - program_group = parser.add_argument_group( - title="program selection", - ).add_mutually_exclusive_group() - program_group.add_argument( - "-k", "--kernel", action="store_true", help="debug the running kernel (default)" - ) - program_group.add_argument( - "-c", "--core", metavar="PATH", type=str, help="debug the given core dump" - ) - program_group.add_argument( - "-p", - "--pid", - metavar="PID", - type=int, - help="debug the running process with the given PID", - ) - - symbol_group = parser.add_argument_group("debugging symbols") - symbol_group.add_argument( - "-s", - "--symbols", - metavar="PATH", - type=str, - action="append", - help="load additional debugging symbols from the given file; this option may be given more than once", - ) - default_symbols_group = symbol_group.add_mutually_exclusive_group() - default_symbols_group.add_argument( - "--main-symbols", - dest="default_symbols", - action="store_const", - const={"main": True}, - help="only load debugging symbols for the main executable and those added with -s; " - "for userspace programs, this is currently equivalent to --no-default-symbols", - ) - default_symbols_group.add_argument( - "--no-default-symbols", - dest="default_symbols", - action="store_const", - const={}, - help="don't load any debugging symbols that were not explicitly added with -s", - ) - - parser.add_argument( - "-q", - "--quiet", - action="store_true", - help="don't print download progress or non-fatal warnings " - "(e.g., about missing debugging information)", - ) - parser.add_argument( - "script", - metavar="ARG", - type=str, - nargs=argparse.REMAINDER, - help="script to execute instead of running in interactive mode", - ) - parser.add_argument("--version", action="version", version=version) - - args = parser.parse_args() - - if args.script: - # A common mistake users make is running drgn $core_dump, which tries - # to run $core_dump as a Python script. Rather than failing later with - # some inscrutable syntax or encoding error, try to catch this early - # and provide a helpful message. - try: - script_type = _identify_script(args.script[0]) - except OSError as e: - sys.exit(e) - if script_type == "core": - sys.exit( - f"error: {args.script[0]} is a core dump\n" - f'Did you mean "-c {args.script[0]}"?' - ) - elif script_type == "elf": - sys.exit(f"error: {args.script[0]} is a binary, not a drgn script") - else: - print(version, file=sys.stderr, flush=True) - if not args.quiet: - os.environ["DEBUGINFOD_PROGRESS"] = "1" - - prog = drgn.Program() - if args.core is not None: - prog.set_core_dump(args.core) - elif args.pid is not None: - prog.set_pid(args.pid or os.getpid()) - else: - prog.set_kernel() - if args.default_symbols is None: - args.default_symbols = {"default": True, "main": True} - try: - prog.load_debug_info(args.symbols, **args.default_symbols) - except drgn.MissingDebugInfoError as e: - if not args.quiet: - print(str(e), file=sys.stderr) - - init_globals: Dict[str, Any] = {"prog": prog} - if args.script: - sys.argv = args.script - script = args.script[0] - if pkgutil.get_importer(script) is None: - sys.path.insert(0, os.path.dirname(os.path.abspath(script))) - runpy.run_path(script, init_globals=init_globals, run_name="__main__") - else: - import atexit - import readline - - from drgn.internal.rlcompleter import Completer - - init_globals["drgn"] = drgn - drgn_globals = [ - "NULL", - "Object", - "cast", - "container_of", - "execscript", - "offsetof", - "reinterpret", - "sizeof", - ] - for attr in drgn_globals: - init_globals[attr] = getattr(drgn, attr) - init_globals["__name__"] = "__main__" - init_globals["__doc__"] = None - - histfile = os.path.expanduser("~/.drgn_history") - try: - readline.read_history_file(histfile) - except OSError as e: - if not isinstance(e, FileNotFoundError) and not args.quiet: - print("could not read history:", str(e), file=sys.stderr) - - def write_history_file() -> None: - try: - readline.write_history_file(histfile) - except OSError as e: - if not args.quiet: - print("could not write history:", str(e), file=sys.stderr) - - atexit.register(write_history_file) - - readline.set_history_length(1000) - readline.parse_and_bind("tab: complete") - readline.set_completer(Completer(init_globals).complete) - atexit.register(lambda: readline.set_completer(None)) - - sys.displayhook = displayhook - - banner = f"""\ -For help, type help(drgn). ->>> import drgn ->>> from drgn import {", ".join(drgn_globals)} ->>> from drgn.helpers.common import *""" - module = importlib.import_module("drgn.helpers.common") - for name in module.__dict__["__all__"]: - init_globals[name] = getattr(module, name) - if prog.flags & drgn.ProgramFlags.IS_LINUX_KERNEL: - banner += "\n>>> from drgn.helpers.linux import *" - module = importlib.import_module("drgn.helpers.linux") - for name in module.__dict__["__all__"]: - init_globals[name] = getattr(module, name) - code.interact(banner=banner, exitmsg="", local=init_globals) diff --git a/drgn/internal/rlcompleter.py b/drgn/internal/rlcompleter.py index 86d750f49..6d47a6064 100644 --- a/drgn/internal/rlcompleter.py +++ b/drgn/internal/rlcompleter.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """Improved readline completer""" diff --git a/examples/linux/lsmod.py b/examples/linux/lsmod.py deleted file mode 100755 index 634783799..000000000 --- a/examples/linux/lsmod.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env drgn -# Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later - -"""An implementation of lsmod(8) using drgn""" - -from drgn.helpers.linux.list import list_for_each_entry - -print("Module Size Used by") -for mod in list_for_each_entry("struct module", prog["modules"].address_of_(), "list"): - name = mod.name.string_().decode() - size = (mod.init_layout.size + mod.core_layout.size).value_() - refcnt = mod.refcnt.counter.value_() - 1 - print(f"{name:19} {size:>8} {refcnt}", end="") - first = True - for use in list_for_each_entry( - "struct module_use", mod.source_list.address_of_(), "source_list" - ): - if first: - print(" ", end="") - first = False - else: - print(",", end="") - print(use.source.name.string_().decode(), end="") - print() diff --git a/examples/linux/ps.py b/examples/linux/ps.py deleted file mode 100755 index d2c66a1c4..000000000 --- a/examples/linux/ps.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env drgn -# Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later - -"""A simplified implementation of ps(1) using drgn""" - -from drgn.helpers.linux.pid import for_each_task - -print("PID COMM") -for task in for_each_task(prog): - pid = task.pid.value_() - comm = task.comm.string_().decode() - print(f"{pid:<10} {comm}") diff --git a/libdrgn/Makefile.am b/libdrgn/Makefile.am index f0883e11a..6c970f35c 100644 --- a/libdrgn/Makefile.am +++ b/libdrgn/Makefile.am @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later ACLOCAL_AMFLAGS = -I m4 @@ -9,14 +9,13 @@ AM_CPPFLAGS = -I $(top_srcdir)/include -D_GNU_SOURCE AM_CFLAGS = $(WARN_CFLAGS) $(SANITIZER_CFLAGS) AM_LDFLAGS= $(SANITIZER_LDFLAGS) -noinst_HEADERS = include/dwarf.h \ - include/elf.h \ - include/elfutils/known-dwarf.h +noinst_HEADERS = include/elf.h include_HEADERS = drgn.h ARCH_DEFS_PYS = arch_aarch64_defs.py \ arch_ppc64_defs.py \ + arch_s390x_defs.py \ arch_x86_64_defs.py ARCH_DEFS_INCS = $(ARCH_DEFS_PYS:_defs.py=_defs.inc) @@ -27,7 +26,9 @@ STRSWITCH_INCS = drgn_program_parse_vmcoreinfo.inc \ BUILT_SOURCES = $(ARCH_DEFS_INCS) \ $(STRSWITCH_INCS) \ c_keywords.inc \ - drgn.h + drgn.h \ + drgn_section_name_to_index.inc \ + elf_sections.h noinst_LTLIBRARIES = libdrgnimpl.la @@ -46,12 +47,20 @@ libdrgnimpl_la_SOURCES = $(ARCH_DEFS_PYS:_defs.py=.c) \ cfi.c \ cfi.h \ cityhash.h \ + cleanup.h \ debug_info.c \ debug_info.h \ + drgn_section_name_to_index.inc \ + dwarf_constants.c \ + dwarf_constants.h \ dwarf_info.c \ dwarf_info.h \ + elf_file.c \ + elf_file.h \ + elf_sections.h \ error.c \ error.h \ + generics.h \ hash_table.c \ hash_table.h \ helpers.h \ @@ -67,6 +76,8 @@ libdrgnimpl_la_SOURCES = $(ARCH_DEFS_PYS:_defs.py=.c) \ linux_kernel.c \ linux_kernel.h \ linux_kernel_helpers.c \ + log.c \ + log.h \ memory_reader.c \ memory_reader.h \ minmax.h \ @@ -75,6 +86,8 @@ libdrgnimpl_la_SOURCES = $(ARCH_DEFS_PYS:_defs.py=.c) \ object.h \ object_index.c \ object_index.h \ + openmp.c \ + openmp.h \ orc.h \ orc_info.c \ orc_info.h \ @@ -99,7 +112,6 @@ libdrgnimpl_la_SOURCES = $(ARCH_DEFS_PYS:_defs.py=.c) \ type.c \ type.h \ util.h \ - vector.c \ vector.h libdrgnimpl_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden $(OPENMP_CFLAGS) \ @@ -128,6 +140,12 @@ drgn.h: drgn.h.in configure.ac -e 's/@DRGN_VERSION_PATCH@/$(word 3,$(subst ., ,@PACKAGE_VERSION@))/g' \ $< > $@ +drgn_section_name_to_index.inc: build-aux/gen_elf_sections.py build-aux/gen_strswitch.py build-aux/codegen_utils.py + $(AM_V_GEN)$(PYTHON) $< | $(PYTHON) $(word 2, $^) -o $@ - + +elf_sections.h: build-aux/gen_elf_sections.py build-aux/codegen_utils.py + $(AM_V_GEN)$(PYTHON) $< -H > $@ + lib_LTLIBRARIES = libdrgn.la libdrgn_la_SOURCES = @@ -147,7 +165,7 @@ _drgn_la_SOURCES = python/constants.c \ python/error.c \ python/helpers.c \ python/language.c \ - python/module.c \ + python/main.c \ python/object.c \ python/platform.c \ python/program.c \ @@ -188,6 +206,7 @@ EXTRA_DIST = $(ARCH_DEFS_PYS) \ build-aux/gen_arch_inc_strswitch.py \ build-aux/gen_c_keywords_inc_strswitch.py \ build-aux/gen_constants.py \ + build-aux/gen_elf_sections.py \ build-aux/gen_strswitch.py \ drgn.h.in diff --git a/libdrgn/arch_aarch64.c b/libdrgn/arch_aarch64.c index 4da00fc14..995e2d5ef 100644 --- a/libdrgn/arch_aarch64.c +++ b/libdrgn/arch_aarch64.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include @@ -48,28 +48,23 @@ static const struct drgn_cfi_row default_dwarf_cfi_row_aarch64 = DRGN_CFI_ROW( DRGN_CFI_SAME_VALUE_INIT(DRGN_REGISTER_NUMBER(x30)), ); -// Mask out the pointer authentication code if the return address is signed. -static void demangle_return_address_aarch64(struct drgn_program *prog, - struct drgn_register_state *regs, - drgn_register_number regno) +// Mask out the pointer authentication code from x30/lr. +static void demangle_cfi_registers_aarch64(struct drgn_program *prog, + struct drgn_register_state *regs) { struct optional_uint64 ra_sign_state = drgn_register_state_get_u64(prog, regs, ra_sign_state); if (!ra_sign_state.has_value || !(ra_sign_state.value & 1)) return; struct optional_uint64 ra = - drgn_register_state_get_u64_impl(prog, regs, regno, - register_layout[regno].offset, - register_layout[regno].size); - assert(ra.has_value); + drgn_register_state_get_u64(prog, regs, x30); + if (!ra.has_value) + return; if (ra.value & (UINT64_C(1) << 55)) ra.value |= prog->aarch64_insn_pac_mask; else ra.value &= ~prog->aarch64_insn_pac_mask; - drgn_register_state_set_from_u64_impl(prog, regs, regno, - register_layout[regno].offset, - register_layout[regno].size, - ra.value); + drgn_register_state_set_from_u64(prog, regs, x30, ra.value); } // Unwind using the frame pointer. Note that leaf functions may not allocate a @@ -124,8 +119,7 @@ fallback_unwind_aarch64(struct drgn_program *prog, if (prog->aarch64_insn_pac_mask) { drgn_register_state_set_from_u64(prog, unwound, ra_sign_state, 1); - demangle_return_address_aarch64(prog, unwound, - DRGN_REGISTER_NUMBER(x30)); + demangle_cfi_registers_aarch64(prog, unwound); } drgn_register_state_set_pc_from_register(prog, unwound, x30); // The location of the frame record within the stack frame is not @@ -497,8 +491,8 @@ const struct drgn_architecture_info arch_info_aarch64 = { DRGN_PLATFORM_IS_LITTLE_ENDIAN), DRGN_ARCHITECTURE_REGISTERS, .default_dwarf_cfi_row = &default_dwarf_cfi_row_aarch64, + .demangle_cfi_registers = demangle_cfi_registers_aarch64, .fallback_unwind = fallback_unwind_aarch64, - .demangle_return_address = demangle_return_address_aarch64, .pt_regs_get_initial_registers = pt_regs_get_initial_registers_aarch64, .prstatus_get_initial_registers = prstatus_get_initial_registers_aarch64, .linux_kernel_get_initial_registers = diff --git a/libdrgn/arch_aarch64_defs.py b/libdrgn/arch_aarch64_defs.py index 1848b9236..ef4a558ee 100644 --- a/libdrgn/arch_aarch64_defs.py +++ b/libdrgn/arch_aarch64_defs.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later REGISTERS = [ *[DrgnRegister(f"x{i}") for i in range(29)], @@ -20,3 +20,5 @@ DrgnRegisterLayout("pc", size=8, dwarf_number=32), DrgnRegisterLayout("pstate", size=8, dwarf_number=None), ] + +STACK_POINTER_REGISTER = "sp" diff --git a/libdrgn/arch_arm.c b/libdrgn/arch_arm.c index 3554717bd..69038ed31 100644 --- a/libdrgn/arch_arm.c +++ b/libdrgn/arch_arm.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include diff --git a/libdrgn/arch_i386.c b/libdrgn/arch_i386.c index f8483d302..c289fff7e 100644 --- a/libdrgn/arch_i386.c +++ b/libdrgn/arch_i386.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include diff --git a/libdrgn/arch_ppc64.c b/libdrgn/arch_ppc64.c index fa45f23ba..032258598 100644 --- a/libdrgn/arch_ppc64.c +++ b/libdrgn/arch_ppc64.c @@ -1,5 +1,5 @@ // (C) Copyright IBM Corp. 2020 -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include @@ -187,15 +187,13 @@ linux_kernel_get_initial_registers_ppc64(const struct drgn_object *task_obj, struct drgn_register_state **ret) { - static const uint64_t STACK_FRAME_OVERHEAD = 112; - static const uint64_t SWITCH_FRAME_SIZE = STACK_FRAME_OVERHEAD + 368; - struct drgn_error *err; struct drgn_program *prog = drgn_object_program(task_obj); struct drgn_object sp_obj; drgn_object_init(&sp_obj, prog); + // The top of the stack is saved in task->thread.ksp. err = drgn_object_member_dereference(&sp_obj, task_obj, "thread"); if (err) goto out; @@ -204,11 +202,27 @@ linux_kernel_get_initial_registers_ppc64(const struct drgn_object *task_obj, goto out; uint64_t ksp; err = drgn_object_read_unsigned(&sp_obj, &ksp); + if (err) + goto out; + // The previous stack pointer is stored at the top of the stack. + uint64_t r1; + err = drgn_program_read_u64(prog, ksp, false, &r1); + if (err) + goto out; + + // The struct pt_regs is stored above the previous stack pointer. + struct drgn_qualified_type pt_regs_type; + err = drgn_program_find_type(prog, "struct pt_regs", NULL, + &pt_regs_type); + if (err) + goto out; + uint64_t sizeof_pt_regs; + err = drgn_type_sizeof(pt_regs_type.type, &sizeof_pt_regs); if (err) goto out; char buf[312]; - err = drgn_program_read_memory(prog, buf, ksp + STACK_FRAME_OVERHEAD, + err = drgn_program_read_memory(prog, buf, r1 - sizeof_pt_regs, sizeof(buf), false); if (err) goto out; @@ -218,8 +232,7 @@ linux_kernel_get_initial_registers_ppc64(const struct drgn_object *task_obj, if (err) goto out; - drgn_register_state_set_from_u64(prog, *ret, r1, - ksp + SWITCH_FRAME_SIZE); + drgn_register_state_set_from_u64(prog, *ret, r1, r1); err = NULL; out: diff --git a/libdrgn/arch_ppc64_defs.py b/libdrgn/arch_ppc64_defs.py index 4edf810f7..a95f91f6b 100644 --- a/libdrgn/arch_ppc64_defs.py +++ b/libdrgn/arch_ppc64_defs.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later REGISTERS = [ *[DrgnRegister(f"r{i}") for i in range(32)], @@ -20,3 +20,5 @@ *[DrgnRegisterLayout(f"r{i}", size=8, dwarf_number=i) for i in range(32)], *[DrgnRegisterLayout(f"cr{i}", size=8, dwarf_number=68 + i) for i in range(8)], ] + +STACK_POINTER_REGISTER = "r1" diff --git a/libdrgn/arch_riscv.c b/libdrgn/arch_riscv.c index ab65ceba1..609fc1fba 100644 --- a/libdrgn/arch_riscv.c +++ b/libdrgn/arch_riscv.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include diff --git a/libdrgn/arch_s390x.c b/libdrgn/arch_s390x.c new file mode 100644 index 000000000..13a931457 --- /dev/null +++ b/libdrgn/arch_s390x.c @@ -0,0 +1,501 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include +#include +#include + +#include "error.h" +#include "platform.h" // IWYU pragma: associated +#include "program.h" +#include "register_state.h" + +// The ABI specification can be found at https://github.com/IBM/s390x-abi. + +#include "arch_s390x_defs.inc" + +static const struct drgn_cfi_row default_dwarf_cfi_row_s390x = DRGN_CFI_ROW( + // Callee-saved registers default to DW_CFA_same_value. This isn't + // explicitly documented in the psABI, but it seems to be the consensus. + DRGN_CFI_SAME_VALUE_INIT(DRGN_REGISTER_NUMBER(r6)), + DRGN_CFI_SAME_VALUE_INIT(DRGN_REGISTER_NUMBER(r7)), + DRGN_CFI_SAME_VALUE_INIT(DRGN_REGISTER_NUMBER(r8)), + DRGN_CFI_SAME_VALUE_INIT(DRGN_REGISTER_NUMBER(r9)), + DRGN_CFI_SAME_VALUE_INIT(DRGN_REGISTER_NUMBER(r10)), + DRGN_CFI_SAME_VALUE_INIT(DRGN_REGISTER_NUMBER(r11)), + DRGN_CFI_SAME_VALUE_INIT(DRGN_REGISTER_NUMBER(r12)), + DRGN_CFI_SAME_VALUE_INIT(DRGN_REGISTER_NUMBER(r13)), + // r14 is the return address, which also defaults to DW_CFA_same_value + // by consensus. + DRGN_CFI_SAME_VALUE_INIT(DRGN_REGISTER_NUMBER(r14)), + DRGN_CFI_SAME_VALUE_INIT(DRGN_REGISTER_NUMBER(r15)), +); + +static struct drgn_error * +apply_elf_reloc_s390(const struct drgn_relocating_section *relocating, + uint64_t r_offset, uint32_t r_type, + const int64_t *r_addend, uint64_t sym_value) +{ + switch (r_type) { + case R_390_NONE: + return NULL; + case R_390_8: + return drgn_reloc_add8(relocating, r_offset, r_addend, + sym_value); + case R_390_16: + return drgn_reloc_add16(relocating, r_offset, r_addend, + sym_value); + case R_390_32: + return drgn_reloc_add32(relocating, r_offset, r_addend, + sym_value); + case R_390_PC32: + return drgn_reloc_add32(relocating, r_offset, r_addend, + sym_value + - (relocating->addr + r_offset)); + case R_390_PC16: + return drgn_reloc_add16(relocating, r_offset, r_addend, + sym_value + - (relocating->addr + r_offset)); + case R_390_64: + return drgn_reloc_add64(relocating, r_offset, r_addend, + sym_value); + case R_390_PC64: + return drgn_reloc_add64(relocating, r_offset, r_addend, + sym_value + - (relocating->addr + r_offset)); + default: + return DRGN_UNKNOWN_RELOCATION_TYPE(r_type); + } +} + +static struct drgn_error * +pt_regs_get_initial_registers_s390x_impl(struct drgn_program *prog, + const void *buf, + struct drgn_register_state **ret) +{ + struct drgn_register_state *regs = + drgn_register_state_create(pswa, true); + if (!regs) + return &drgn_enomem; + drgn_register_state_set_range_from_buffer(regs, r6, r15, + (uint64_t *)buf + 9); + drgn_register_state_set_range_from_buffer(regs, r0, r5, + (uint64_t *)buf + 3); + drgn_register_state_set_range_from_buffer(regs, pswm, pswa, + (uint64_t *)buf + 1); + drgn_register_state_set_pc_from_register(prog, regs, pswa); + *ret = regs; + return NULL; +} + +static struct drgn_error * +fallback_unwind_s390x(struct drgn_program *prog, + struct drgn_register_state *regs, + struct drgn_register_state **ret) +{ + struct drgn_error *err; + + // For userspace, we can't rely on the backchain entry because it is + // normally compiled without -mbackchain. + if (!(prog->flags & DRGN_PROGRAM_IS_LINUX_KERNEL)) + return &drgn_stop; + + // For the Linux kernel, we assume -mpacked-stack. + + struct optional_uint64 r15 = drgn_register_state_get_u64(prog, regs, + r15); + if (!r15.has_value) + return &drgn_stop; + + static const size_t stack_frame_backchain_offset = 152; + uint64_t backchain; + err = drgn_program_read_u64(prog, + r15.value + stack_frame_backchain_offset, + false, &backchain); + if (err) + goto err; + + if (!backchain) { + // The Linux kernel saves a struct stack_frame with back_chain = + // 0 and a struct pt_regs on the stack when entering program + // check handler, irq handlers, or the system call handler. + uint64_t buf[19]; + err = drgn_program_read_memory(prog, buf, r15.value + 160, + sizeof(buf), false); + if (err) + goto err; + return pt_regs_get_initial_registers_s390x_impl(prog, buf, ret); + } + + // r14 and r15. + uint64_t buf[2]; + static const size_t stack_frame_r14_offset = 136; + err = drgn_program_read_memory(prog, buf, + backchain + stack_frame_r14_offset, + sizeof(buf), false); + if (err) + goto err; + + struct drgn_register_state *unwound = + drgn_register_state_create(r15, false); + if (!unwound) + return &drgn_enomem; + drgn_register_state_set_range_from_buffer(unwound, r14, r15, buf); + drgn_register_state_set_pc_from_register(prog, unwound, r14); + *ret = unwound; + return NULL; + +err: + if (err->code == DRGN_ERROR_FAULT) { + drgn_error_destroy(err); + return &drgn_stop; + } + return err; +} + +static struct drgn_error * +pt_regs_get_initial_registers_s390x(const struct drgn_object *obj, + struct drgn_register_state **ret) +{ + if (drgn_object_size(obj) < 152) { + return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, + "registers are truncated"); + } + return pt_regs_get_initial_registers_s390x_impl(drgn_object_program(obj), + drgn_object_buffer(obj), + ret); +} + +// On s390x, elf_gregset_t (a.k.a. s390_regs) is different from struct pt_regs. +static struct drgn_error * +prstatus_get_initial_registers_s390x(struct drgn_program *prog, + const void *prstatus, size_t size, + struct drgn_register_state **ret) +{ + if (size < 208) { + return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, + "registers are truncated"); + } + + // offsetof(struct elf_prstatus, pr_reg) + static const size_t pr_reg_offset = 112; + const uint64_t *buf = + (const uint64_t *)((const char *)prstatus + pr_reg_offset); + + struct drgn_register_state *regs = + drgn_register_state_create(a15, true); + if (!regs) + return &drgn_enomem; + drgn_register_state_set_range_from_buffer(regs, r0, r5, buf + 2); + drgn_register_state_set_range_from_buffer(regs, r6, r15, buf + 8); + drgn_register_state_set_range_from_buffer(regs, pswm, pswa, buf); + drgn_register_state_set_range_from_buffer(regs, a0, a15, buf + 18); + drgn_register_state_set_pc_from_register(prog, regs, pswa); + *ret = regs; + return NULL; +} + +// The Linux kernel saves the callee-saved registers in a struct stack_frame and +// saves this address to struct task_struct.thread.ksp. See __switch_to() in +// arch/s390/kernel/entry.S. +static struct drgn_error * +linux_kernel_get_initial_registers_s390x(const struct drgn_object *task_obj, + struct drgn_register_state **ret) +{ + struct drgn_program *prog = drgn_object_program(task_obj); + struct drgn_error *err; + + struct drgn_object ctx; + drgn_object_init(&ctx, prog); + + err = drgn_object_member_dereference(&ctx, task_obj, "thread"); + if (err) + goto out; + err = drgn_object_member(&ctx, &ctx, "ksp"); + if (err) + goto out; + uint64_t ksp; + err = drgn_object_read_unsigned(&ctx, &ksp); + if (err) + goto out; + + // r6-r15 + uint64_t buf[10]; + static const size_t stack_frame_gprs_offset = 72; + err = drgn_program_read_memory(prog, buf, ksp + stack_frame_gprs_offset, + sizeof(buf), false); + if (err) + goto out; + + struct drgn_register_state *regs = + drgn_register_state_create(r15, false); + if (!regs) { + err = &drgn_enomem; + goto out; + } + drgn_register_state_set_range_from_buffer(regs, r6, r15, buf); + drgn_register_state_set_pc_from_register(prog, regs, r14); + *ret = regs; + err = NULL; +out: + drgn_object_deinit(&ctx); + return err; +} + +struct pgtable_data { + uint64_t entries[2048]; + uint64_t addr; + int length; + int offset; +}; + +struct pgtable_iterator_s390x { + struct pgtable_iterator it; + struct pgtable_data pagetable[5]; + int levels; +}; + +struct dat_level { + int bits; + int shift; + int entries; +}; + +static const struct dat_level dat_levels[] = { + { .bits = 8, .shift = 12, .entries = 256 }, /* PTE */ + { .bits = 11, .shift = 20, .entries = 2048 }, /* STE */ + { .bits = 11, .shift = 31, .entries = 2048 }, /* RTTE */ + { .bits = 11, .shift = 42, .entries = 2048 }, /* RSTE */ + { .bits = 11, .shift = 53, .entries = 2048 }, /* RFTE */ +}; + +#define REGION_INVALID 0x20 +#define SEGMENT_INVALID 0x20 +#define PAGE_INVALID 0x400 + +#define REGION_LARGE 0x400 +#define SEGMENT_LARGE 0x400 +#define PAGE_LARGE 0x800 + +static struct drgn_error * +linux_kernel_pgtable_iterator_create_s390x(struct drgn_program *prog, + struct pgtable_iterator **ret) +{ + struct pgtable_iterator_s390x *it = malloc(sizeof(*it)); + if (!it) + return &drgn_enomem; + + *ret = &it->it; + return NULL; +} + +static void +linux_kernel_pgtable_iterator_destroy_s390x(struct pgtable_iterator *_it) +{ + free(container_of(_it, struct pgtable_iterator_s390x, it)); +} + +static void +linux_kernel_pgtable_iterator_init_s390x(struct drgn_program *prog, + struct pgtable_iterator *_it) +{ + struct pgtable_iterator_s390x *it = + container_of(_it, struct pgtable_iterator_s390x, it); + for (int i = 0; i < 5; i++) + it->pagetable[i].addr = UINT64_MAX; + it->levels = 3; +} + +static bool +entry_is_invalid(uint64_t entry, int level) +{ + switch (level) { + case 0: + return entry & PAGE_INVALID; + case 1: + return entry & SEGMENT_INVALID; + case 2: + case 3: + case 4: + return entry & REGION_INVALID; + default: + return true; + } +} + +static int +get_mask(struct pgtable_iterator_s390x *it, int level) +{ + return (1 << dat_levels[level].bits) - 1; +} + +static int +get_index(struct pgtable_iterator_s390x *it, int level, uint64_t va) +{ + return (va >> dat_levels[level].shift) & get_mask(it, level); +} + +static int +get_table_length(uint64_t entry, int level) +{ + switch(level) { + case 2: + case 3: + case 4: + return ((entry & 3) + 1) << 9; + default: + return 2048; + } +} + +static int +get_table_offset(uint64_t entry, int level) +{ + switch(level) { + case 2: + case 3: + case 4: + return ((entry >> 6) & 3) << 9; + default: + return 0; + } +} + +static uint64_t +get_table_address(uint64_t entry, int level, bool *is_large) +{ + *is_large = false; + switch (level) { + case 1: /* STE */ + if (entry & SEGMENT_LARGE) { + *is_large = true; + return entry & ~((UINT64_C(1) << 20) - 1); + } else { + return entry & ~UINT64_C(0x7ff); + } + case 2: /* RTTE */ + if (entry & REGION_LARGE) { + *is_large = true; + return entry & ~((UINT64_C(1) << 31) - 1); + } else { + return entry & ~UINT64_C(0xfff); + } + default: + return entry & ~UINT64_C(0xfff); + } +} + +static uint64_t get_level_mask(int level) +{ + return ~((UINT64_C(1) << dat_levels[level].shift) - 1); +} + +static struct drgn_error * +linux_kernel_pgtable_iterator_next_s390x(struct drgn_program *prog, + struct pgtable_iterator *_it, + uint64_t *virt_addr_ret, + uint64_t *phys_addr_ret) +{ + struct pgtable_iterator_s390x *it = + container_of(_it, struct pgtable_iterator_s390x, it); + const uint64_t va = it->it.virt_addr; + uint64_t table = _it->pgtable & ~UINT64_C(0xfff); + int level, length = 2048, offset = 0; + uint64_t entry; + + /* + * Note: we need the ASCE bits to determine the levels of paging in use, + * but we only get the pgd address from drgn. Therefore do the same what + * the linux kernel does: read the first level entry, and deduct the + * number of levels from the TT bits. + */ + struct drgn_error *err = drgn_program_read_u64(prog, table, true, + &entry); + if (err) + return err; + + level = 2 + ((entry >> 2) & 3); + + while(level-- > 0) { + int index = get_index(it, level, va); + + if (index < offset || index - offset > length) { + uint64_t mask = get_level_mask(level); + *phys_addr_ret = UINT64_MAX; + it->it.virt_addr = (va | ~mask) + 1; + return NULL; + } + + index -= offset; + if (it->pagetable[level].addr != table || + it->pagetable[level].length != length || + it->pagetable[level].offset != offset) { + /* + * It's only marginally more expensive to read 4096 + * bytes than 8 bytes, so we always read the full table. + */ + err = drgn_program_read_memory(prog, + it->pagetable[level].entries, + table, length * 8, true); + if (err) + return err; + + it->pagetable[level].addr = table; + it->pagetable[level].length = length; + it->pagetable[level].offset = offset; + } + + uint64_t entry = it->pagetable[level].entries[index]; + if (drgn_platform_bswap(&prog->platform)) + entry = bswap_64(entry); + + length = get_table_length(entry, level); + offset = get_table_offset(entry, level); + if (entry_is_invalid(entry, level)) { + *phys_addr_ret = UINT64_MAX; + return NULL; + } + + bool is_large; + table = get_table_address(entry, level, &is_large); + + if (level == 0 || is_large) { + uint64_t mask = get_level_mask(level); + *phys_addr_ret = table; + *virt_addr_ret = va & mask; + it->it.virt_addr = (va | ~mask) + 1; + return NULL; + } + } + return &drgn_enomem; +} + +const struct drgn_architecture_info arch_info_s390x = { + .name = "s390x", + .arch = DRGN_ARCH_S390X, + .default_flags = DRGN_PLATFORM_IS_64_BIT, + DRGN_ARCHITECTURE_REGISTERS, + .default_dwarf_cfi_row = &default_dwarf_cfi_row_s390x, + .fallback_unwind = fallback_unwind_s390x, + .pt_regs_get_initial_registers = pt_regs_get_initial_registers_s390x, + .prstatus_get_initial_registers = prstatus_get_initial_registers_s390x, + .linux_kernel_get_initial_registers = + linux_kernel_get_initial_registers_s390x, + .apply_elf_reloc = apply_elf_reloc_s390, + .linux_kernel_pgtable_iterator_create = + linux_kernel_pgtable_iterator_create_s390x, + .linux_kernel_pgtable_iterator_destroy = + linux_kernel_pgtable_iterator_destroy_s390x, + .linux_kernel_pgtable_iterator_init = + linux_kernel_pgtable_iterator_init_s390x, + .linux_kernel_pgtable_iterator_next = + linux_kernel_pgtable_iterator_next_s390x, +}; + +const struct drgn_architecture_info arch_info_s390 = { + .name = "s390", + .arch = DRGN_ARCH_S390, + .default_flags = 0, + .register_by_name = drgn_register_by_name_unknown, + .apply_elf_reloc = apply_elf_reloc_s390, +}; diff --git a/libdrgn/arch_s390x_defs.py b/libdrgn/arch_s390x_defs.py new file mode 100644 index 000000000..98dc1bd7e --- /dev/null +++ b/libdrgn/arch_s390x_defs.py @@ -0,0 +1,24 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +REGISTERS = [ + *[DrgnRegister(f"r{i}") for i in range(16)], + *[DrgnRegister(f"a{i}") for i in range(16)], + DrgnRegister("pswm"), + DrgnRegister("pswa"), +] + +REGISTER_LAYOUT = [ + # Callee-saved registers and return address (r14). + *[DrgnRegisterLayout(f"r{i}", size=8, dwarf_number=i) for i in range(6, 16)], + # Caller-saved registers. + *[DrgnRegisterLayout(f"r{i}", size=8, dwarf_number=i) for i in range(6)], + # These are typically only used for interrupted frames. + DrgnRegisterLayout("pswm", size=8, dwarf_number=64), + DrgnRegisterLayout("pswa", size=8, dwarf_number=65), + # Access control registers (ACRs) are only used in userspace, + # and not present in struct pt_regs. + *[DrgnRegisterLayout(f"a{i}", size=4, dwarf_number=48 + i) for i in range(16)], +] + +STACK_POINTER_REGISTER = "r15" diff --git a/libdrgn/arch_x86_64.c b/libdrgn/arch_x86_64.c index 5f4bc4d9f..feda02fc5 100644 --- a/libdrgn/arch_x86_64.c +++ b/libdrgn/arch_x86_64.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include @@ -57,13 +57,13 @@ orc_to_cfi_x86_64(const struct drgn_orc_entry *orc, if (!drgn_cfi_row_copy(row_ret, drgn_empty_cfi_row)) return &drgn_enomem; + if (drgn_orc_type(orc) == DRGN_ORC_TYPE_UNDEFINED) + return &drgn_not_found; + else if (drgn_orc_type(orc) == DRGN_ORC_TYPE_END_OF_STACK) + return NULL; + struct drgn_cfi_rule rule; switch (drgn_orc_sp_reg(orc)) { - case ORC_REG_UNDEFINED: - if (drgn_orc_is_end(orc)) - return NULL; - else - return &drgn_not_found; case ORC_REG_SP: rule.kind = DRGN_CFI_RULE_REGISTER_PLUS_OFFSET; rule.regno = DRGN_REGISTER_NUMBER(rsp); @@ -76,7 +76,7 @@ orc_to_cfi_x86_64(const struct drgn_orc_entry *orc, break; case ORC_REG_SP_INDIRECT: rule.kind = DRGN_CFI_RULE_AT_REGISTER_ADD_OFFSET; - rule.regno = DRGN_REGISTER_NUMBER(rbp); + rule.regno = DRGN_REGISTER_NUMBER(rsp); rule.offset = orc->sp_offset; break; case ORC_REG_BP_INDIRECT: @@ -126,7 +126,6 @@ orc_to_cfi_x86_64(const struct drgn_orc_entry *orc, DRGN_REGISTER_NUMBER(rsp), &rule)) return &drgn_enomem; - *interrupted_ret = false; break; #define SET_AT_CFA_RULE(reg, cfa_offset) do { \ rule.kind = DRGN_CFI_RULE_AT_CFA_PLUS_OFFSET; \ @@ -156,7 +155,6 @@ orc_to_cfi_x86_64(const struct drgn_orc_entry *orc, SET_AT_CFA_RULE(cs, 136); SET_AT_CFA_RULE(rflags, 144); SET_AT_CFA_RULE(ss, 160); - *interrupted_ret = true; break; case DRGN_ORC_TYPE_REGS_PARTIAL: SET_AT_CFA_RULE(rip, 0); @@ -185,7 +183,6 @@ orc_to_cfi_x86_64(const struct drgn_orc_entry *orc, SET_SAME_VALUE_RULE(rdi); SET_SAME_VALUE_RULE(rdx); #undef SET_SAME_VALUE_RULE - *interrupted_ret = true; break; default: return drgn_error_format(DRGN_ERROR_OTHER, @@ -216,6 +213,7 @@ orc_to_cfi_x86_64(const struct drgn_orc_entry *orc, if (!drgn_cfi_row_set_register(row_ret, DRGN_REGISTER_NUMBER(rbp), &rule)) return &drgn_enomem; + *interrupted_ret = drgn_orc_signal(orc); *ret_addr_regno_ret = DRGN_REGISTER_NUMBER(rip); return NULL; } @@ -232,16 +230,6 @@ get_registers_from_frame_pointer(struct drgn_program *prog, if (err) return err; - uint64_t unwound_frame_pointer = - drgn_platform_bswap(&prog->platform) ? bswap_64(frame[0]) : frame[0]; - if (unwound_frame_pointer <= frame_pointer) { - /* - * The next frame pointer isn't valid. Maybe frame pointers are - * not enabled or we're in the middle of a prologue or epilogue. - */ - return &drgn_stop; - } - struct drgn_register_state *regs = drgn_register_state_create(rbp, false); if (!regs) @@ -255,6 +243,44 @@ get_registers_from_frame_pointer(struct drgn_program *prog, return NULL; } +// Unwind from a call instruction, assuming that nothing else has been changed +// since. +static struct drgn_error *unwind_call(struct drgn_program *prog, + struct drgn_register_state *regs, + struct drgn_register_state **ret) +{ + struct drgn_error *err; + + struct optional_uint64 rsp = + drgn_register_state_get_u64(prog, regs, rsp); + if (!rsp.has_value) + return &drgn_stop; + + // Read the return address from the top of the stack. + uint64_t ret_addr; + err = drgn_program_read_u64(prog, rsp.value, false, &ret_addr); + if (err) { + if (err->code == DRGN_ERROR_FAULT) { + drgn_error_destroy(err); + err = &drgn_stop; + } + return err; + } + + // Most of the registers are unchanged. + struct drgn_register_state *tmp = drgn_register_state_dup(regs); + if (!tmp) + return &drgn_enomem; + + // The PC and rip are the return address we just read. + drgn_register_state_set_pc(prog, tmp, ret_addr); + drgn_register_state_set_from_u64(prog, tmp, rip, ret_addr); + // rsp is after the saved return address. + drgn_register_state_set_from_u64(prog, tmp, rsp, rsp.value + 8); + *ret = tmp; + return NULL; +} + static struct drgn_error * fallback_unwind_x86_64(struct drgn_program *prog, struct drgn_register_state *regs, @@ -262,6 +288,13 @@ fallback_unwind_x86_64(struct drgn_program *prog, { struct drgn_error *err; + // If the program counter is 0, it's likely that a NULL function pointer + // was called. Assume that the only thing we need to unwind is a single + // call instruction. + struct optional_uint64 pc = drgn_register_state_get_pc(regs); + if (pc.has_value && pc.value == 0) + return unwind_call(prog, regs, ret); + struct optional_uint64 rbp = drgn_register_state_get_u64(prog, regs, rbp); if (!rbp.has_value) diff --git a/libdrgn/arch_x86_64_defs.py b/libdrgn/arch_x86_64_defs.py index 55a8fe1b3..88dda9261 100644 --- a/libdrgn/arch_x86_64_defs.py +++ b/libdrgn/arch_x86_64_defs.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later REGISTERS = [ DrgnRegister("rax"), @@ -65,3 +65,5 @@ DrgnRegisterLayout("fs", size=8, dwarf_number=54), DrgnRegisterLayout("gs", size=8, dwarf_number=55), ] + +STACK_POINTER_REGISTER = "rsp" diff --git a/libdrgn/array.h b/libdrgn/array.h index a860427c8..c14c06404 100644 --- a/libdrgn/array.h +++ b/libdrgn/array.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -14,8 +14,6 @@ #include "util.h" /** @cond */ -#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0])) - #define array_for_each_impl(var, arr, unique_end) \ for (typeof((arr)[0]) *var = (arr), \ *unique_end = var + array_size(arr); \ @@ -27,7 +25,11 @@ * * @hideinitializer */ -#define array_size(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr)) +#define array_size(arr) \ +static_assert_expression( \ + !types_compatible((arr), &(arr)[0]), "not an array", \ + sizeof(arr) / sizeof((arr)[0]) \ +) /** * Iterate over every element in an array. diff --git a/libdrgn/binary_buffer.c b/libdrgn/binary_buffer.c index 7cc26f6dc..2ba8f8eca 100644 --- a/libdrgn/binary_buffer.c +++ b/libdrgn/binary_buffer.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include diff --git a/libdrgn/binary_buffer.h b/libdrgn/binary_buffer.h index cfab91bdd..e35fa18db 100644 --- a/libdrgn/binary_buffer.h +++ b/libdrgn/binary_buffer.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file diff --git a/libdrgn/binary_search_tree.h b/libdrgn/binary_search_tree.h index d83cf2a2b..e108586d0 100644 --- a/libdrgn/binary_search_tree.h +++ b/libdrgn/binary_search_tree.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -285,7 +285,8 @@ typedef typeof(entry_type) tree##_entry_type; \ \ struct tree { \ struct binary_tree_node *root; \ -}; +}; \ +struct DEFINE_BINARY_SEARCH_TREE_needs_semicolon /** * Define the functions for a binary search tree. @@ -559,7 +560,8 @@ static struct tree##_iterator tree##_next_post_order(struct tree##_iterator it) tree##_node_to_entry(node->parent), \ }; \ } \ -} +} \ +struct DEFINE_BINARY_SEARCH_TREE_needs_semicolon /** * Define a binary search tree interface. @@ -582,7 +584,7 @@ static struct tree##_iterator tree##_next_post_order(struct tree##_iterator it) */ #define DEFINE_BINARY_SEARCH_TREE(tree, entry_type, member, entry_to_key, \ cmp_func, variant) \ -DEFINE_BINARY_SEARCH_TREE_TYPE(tree, entry_type) \ +DEFINE_BINARY_SEARCH_TREE_TYPE(tree, entry_type); \ DEFINE_BINARY_SEARCH_TREE_FUNCTIONS(tree, member, entry_to_key, cmp_func, \ variant) diff --git a/libdrgn/bitops.h b/libdrgn/bitops.h index b79390d9b..5df1a0379 100644 --- a/libdrgn/bitops.h +++ b/libdrgn/bitops.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file diff --git a/libdrgn/build-aux/.gitignore b/libdrgn/build-aux/.gitignore index 86c00a8da..141006286 100644 --- a/libdrgn/build-aux/.gitignore +++ b/libdrgn/build-aux/.gitignore @@ -4,4 +4,5 @@ !/gen_arch_inc_strswitch.py !/gen_c_keywords_inc_strswitch.py !/gen_constants.py +!/gen_elf_sections.py !/gen_strswitch.py diff --git a/libdrgn/build-aux/codegen_utils.py b/libdrgn/build-aux/codegen_utils.py index 113c792a7..18308b69d 100644 --- a/libdrgn/build-aux/codegen_utils.py +++ b/libdrgn/build-aux/codegen_utils.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import ast from typing import Optional diff --git a/libdrgn/build-aux/gen_arch_inc_strswitch.py b/libdrgn/build-aux/gen_arch_inc_strswitch.py index 60c40dfb5..4b81f2184 100755 --- a/libdrgn/build-aux/gen_arch_inc_strswitch.py +++ b/libdrgn/build-aux/gen_arch_inc_strswitch.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later r""" This script reads a drgn architecture definition file ("arch_foo_defs.py") and outputs architecture definition code which must then be processed by gen_strswitch.py to produce the final "arch_foo.inc" file. -The definition file must define two global variables: +The definition file must define three global variables: * REGISTERS must be a sequence of DrgnRegister defining the names of all of the registers in the architecture. They should be defined in the architecture's @@ -25,6 +25,9 @@ REGISTERS and are thus not exposed to the API. See DrgnRegisterLayout below for more details. +* STACK_POINTER_REGISTER must be the identifier of the register which contains + the stack pointer. + The generated file includes "arch_register_layout.h" and defines several things: 1. An enum of register numbers used to implement DRGN_REGISTER_NUMBER(). @@ -46,8 +49,9 @@ static drgn_register_number dwarf_regno_to_internal(uint64_t dwarf_regno); 7. A macro containing initializers for the "register_layout", - "dwarf_regno_to_internal", "registers", "num_registers", and - "register_by_name" members of "struct drgn_architecture_info": + "dwarf_regno_to_internal", "registers", "num_registers", + "stack_pointer_regno", and "register_by_name" members of + "struct drgn_architecture_info": #define DRGN_ARCHITECTURE_REGISTERS ... """ @@ -114,14 +118,15 @@ def __init__( def validate_register_defs( registers: Sequence[DrgnRegister], register_layout: Sequence[DrgnRegisterLayout], + stack_pointer_register: str, ) -> None: - layout_identifiers = set() + layout_by_identifier = {} for layout in register_layout: - if layout.identifier in layout_identifiers: + if layout.identifier in layout_by_identifier: raise ArchDefsError( f"register identifier {layout.identifier!r} is duplicated in REGISTER_LAYOUT" ) - layout_identifiers.add(layout.identifier) + layout_by_identifier[layout.identifier] = layout names = set() register_identifiers = set() @@ -132,7 +137,7 @@ def validate_register_defs( f"register name {name!r} is duplicated in REGISTERS" ) names.add(name) - if register.identifier not in layout_identifiers: + if register.identifier not in layout_by_identifier: raise ArchDefsError( f"register identifier {register.identifier} in REGISTERS is not in REGISTER_LAYOUT" ) @@ -142,13 +147,23 @@ def validate_register_defs( ) register_identifiers.add(register.identifier) + if stack_pointer_register not in layout_by_identifier: + raise ArchDefsError( + f"stack pointer register identifier {stack_pointer_register!r} is not in REGISTER_LAYOUT" + ) + if layout_by_identifier[stack_pointer_register].size > 8: + raise ArchDefsError( + f"stack pointer register {stack_pointer_register!r} is too big" + ) + def gen_arch_inc_strswitch( registers: Sequence[DrgnRegister], register_layout: Sequence[DrgnRegisterLayout], + stack_pointer_register: str, out_file: TextIO, ) -> None: - validate_register_defs(registers, register_layout) + validate_register_defs(registers, register_layout, stack_pointer_register) out_file.write("/* Generated by libdrgn/build-aux/gen_arch_inc_strswitch.py. */\n") @@ -221,6 +236,9 @@ def gen_arch_inc_strswitch( out_file.write("\t.dwarf_regno_to_internal = dwarf_regno_to_internal, \\\n") out_file.write("\t.registers = registers, \\\n") out_file.write(f"\t.num_registers = {len(registers)}, \\\n") + out_file.write( + f"\t.stack_pointer_regno = DRGN_REGISTER_NUMBER__{stack_pointer_register}, \\\n" + ) out_file.write("\t.register_by_name = register_by_name\n") @@ -242,9 +260,12 @@ def main() -> None: try: registers = defs["REGISTERS"] register_layout = defs["REGISTER_LAYOUT"] + stack_pointer_register = defs["STACK_POINTER_REGISTER"] except KeyError as e: sys.exit(f"{e.args[0]} is not defined") - gen_arch_inc_strswitch(registers, register_layout, sys.stdout) + gen_arch_inc_strswitch( + registers, register_layout, stack_pointer_register, sys.stdout + ) except ArchDefsError as e: sys.exit(e) diff --git a/libdrgn/build-aux/gen_c_keywords_inc_strswitch.py b/libdrgn/build-aux/gen_c_keywords_inc_strswitch.py index 03e994412..f3e82a8c6 100755 --- a/libdrgn/build-aux/gen_c_keywords_inc_strswitch.py +++ b/libdrgn/build-aux/gen_c_keywords_inc_strswitch.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later C_KEYWORDS = ( "_Atomic", diff --git a/libdrgn/build-aux/gen_constants.py b/libdrgn/build-aux/gen_constants.py index 23d6ca55b..8e2180b1d 100644 --- a/libdrgn/build-aux/gen_constants.py +++ b/libdrgn/build-aux/gen_constants.py @@ -1,21 +1,52 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import re import sys +from typing import NamedTuple, Sequence, TextIO, Tuple -def gen_constant_class(drgn_h, output_file, class_name, enum_class, constants, regex): - constants = list(constants) +class ConstantClass(NamedTuple): + name: str + enum_class: str + regex: str + constants: Sequence[Tuple[str, str]] = () + + +CONSTANTS = ( + ConstantClass("Architecture", "Enum", r"DRGN_ARCH_([a-zA-Z0-9_]+)"), + ConstantClass("FindObjectFlags", "Flag", r"DRGN_FIND_OBJECT_([a-zA-Z0-9_]+)"), + ConstantClass( + "PlatformFlags", + "Flag", + r"DRGN_PLATFORM_([a-zA-Z0-9_]+)(? None: + constants = list(constant_class.constants) constants.extend( ("_".join(groups[1:]), groups[0]) for groups in re.findall( - r"^\s*(" + regex + r")\s*[=,]", drgn_h, flags=re.MULTILINE + r"^\s*(" + constant_class.regex + r")\s*[=,]", drgn_h, flags=re.MULTILINE ) ) output_file.write( f""" -static int add_{class_name}(PyObject *m, PyObject *enum_module) +static int add_{constant_class.name}(PyObject *m, PyObject *enum_module) {{ PyObject *tmp, *item; int ret = -1; @@ -28,7 +59,7 @@ def gen_constant_class(drgn_h, output_file, class_name, enum_class, constants, r for i, (name, value) in enumerate(constants): output_file.write( f"""\ - item = Py_BuildValue("sk", "{name}", {value}); + item = Py_BuildValue("sK", "{name}", (unsigned long long){value}); if (!item) goto out; PyList_SET_ITEM(tmp, {i}, item); @@ -36,18 +67,18 @@ def gen_constant_class(drgn_h, output_file, class_name, enum_class, constants, r ) output_file.write( f"""\ - {class_name}_class = PyObject_CallMethod(enum_module, "{enum_class}", "sO", "{class_name}", tmp); - if (!{class_name}_class) + {constant_class.name}_class = PyObject_CallMethod(enum_module, "{constant_class.enum_class}", "sO", "{constant_class.name}", tmp); + if (!{constant_class.name}_class) goto out; - if (PyModule_AddObject(m, "{class_name}", {class_name}_class) == -1) {{ - Py_CLEAR({class_name}_class); + if (PyModule_AddObject(m, "{constant_class.name}", {constant_class.name}_class) == -1) {{ + Py_CLEAR({constant_class.name}_class); goto out; }} Py_DECREF(tmp); - tmp = PyUnicode_FromString(drgn_{class_name}_DOC); + tmp = PyUnicode_FromString(drgn_{constant_class.name}_DOC); if (!tmp) goto out; - if (PyObject_SetAttrString({class_name}_class, "__doc__", tmp) == -1) + if (PyObject_SetAttrString({constant_class.name}_class, "__doc__", tmp) == -1) goto out; ret = 0; @@ -59,7 +90,7 @@ def gen_constant_class(drgn_h, output_file, class_name, enum_class, constants, r ) -def gen_constants(input_file, output_file): +def gen_constants(input_file: TextIO, output_file: TextIO) -> None: drgn_h = input_file.read() output_file.write( """\ @@ -67,79 +98,12 @@ def gen_constants(input_file, output_file): #include "drgnpy.h" -PyObject *Architecture_class; -PyObject *FindObjectFlags_class; -PyObject *PlatformFlags_class; -PyObject *PrimitiveType_class; -PyObject *ProgramFlags_class; -PyObject *Qualifiers_class; -PyObject *SymbolBinding_class; -PyObject *SymbolKind_class; -PyObject *TypeKind_class; """ ) - gen_constant_class( - drgn_h, output_file, "Architecture", "Enum", (), r"DRGN_ARCH_([a-zA-Z0-9_]+)" - ) - gen_constant_class( - drgn_h, - output_file, - "FindObjectFlags", - "Flag", - (), - r"DRGN_FIND_OBJECT_([a-zA-Z0-9_]+)", - ) - gen_constant_class( - drgn_h, - output_file, - "PlatformFlags", - "Flag", - (), - r"DRGN_PLATFORM_([a-zA-Z0-9_]+)(? str: + return "DRGN_SCN_" + section_name.lstrip(".").upper() + + +def gen_elf_sections_h(out_file: TextIO) -> None: + out_file.write( + """\ +/* Generated by libdrgn/build-aux/gen_elf_sections.py -H. */ + +#ifndef DRGN_ELF_SECTIONS_H +#define DRGN_ELF_SECTIONS_H + +/** + * Identifiers for important ELF sections so that they can be referenced by + * index rather than name. + */ +enum drgn_section_index { +""" + ) + for section_name in DWARF_INDEX_SECTIONS: + out_file.write(f"\t{section_enumerator_name(section_name)},\n") + out_file.write( + """\ + /** Indices less than this are cached when the module is loaded. */ + DRGN_SECTION_INDEX_NUM_PRECACHE, +""" + ) + + for i, section_name in enumerate(CACHED_SECTIONS): + if i == 0: + out_file.write( + f"\t{section_enumerator_name(section_name)} = DRGN_SECTION_INDEX_NUM_PRECACHE,\n" + ) + else: + out_file.write(f"\t{section_enumerator_name(section_name)},\n") + out_file.write( + """\ + /** Indices less than this may have their data cached. */ + DRGN_SECTION_INDEX_NUM_DATA, +""" + ) + + for i, section_name in enumerate(UNCACHED_SECTIONS): + if i == 0: + out_file.write( + f"\t{section_enumerator_name(section_name)} = DRGN_SECTION_INDEX_NUM_DATA,\n" + ) + else: + out_file.write(f"\t{section_enumerator_name(section_name)},\n") + out_file.write( + """\ + /** Number of section indices. */ + DRGN_SECTION_INDEX_NUM +}; + +#endif /* DRGN_ELF_SECTIONS_H */ +""" + ) + + +def gen_drgn_section_name_to_index_inc_strswitch(out_file: TextIO) -> None: + debug_sections = [] + non_debug_sections = [] + for section_name in itertools.chain( + DWARF_INDEX_SECTIONS, CACHED_SECTIONS, UNCACHED_SECTIONS + ): + if section_name.startswith(".debug_"): + debug_sections.append(section_name) + else: + non_debug_sections.append(section_name) + + out_file.write( + """\ +/* Generated by libdrgn/build-aux/gen_elf_sections.py. */ + +static enum drgn_section_index drgn_debug_section_name_to_index(const char *name, size_t len) +{ + @memswitch (name, len)@ +""" + ) + for section_name in debug_sections: + out_file.write(f"\t@case {c_string_literal(section_name[len('.debug_'):])}@\n") + out_file.write(f"\t\treturn {section_enumerator_name(section_name)};\n") + out_file.write( + """\ + @default@ + return DRGN_SECTION_INDEX_NUM; + @endswitch@ +} + +static enum drgn_section_index drgn_non_debug_section_name_to_index(const char *name) +{ + @strswitch (name)@ +""" + ) + for section_name in non_debug_sections: + out_file.write(f"\t@case {c_string_literal(section_name)}@\n") + out_file.write(f"\t\treturn {section_enumerator_name(section_name)};\n") + out_file.write( + """\ + @default@ + return DRGN_SECTION_INDEX_NUM; + @endswitch@ +} +""" + ) + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument( + "--header", "-H", action="store_true", help="generate header file" + ) + args = parser.parse_args() + + if args.header: + gen_elf_sections_h(sys.stdout) + else: + gen_drgn_section_name_to_index_inc_strswitch(sys.stdout) + + +if __name__ == "__main__": + main() diff --git a/libdrgn/build-aux/gen_strswitch.py b/libdrgn/build-aux/gen_strswitch.py index b9716f85c..424b47581 100755 --- a/libdrgn/build-aux/gen_strswitch.py +++ b/libdrgn/build-aux/gen_strswitch.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later """ This script generates code for a "string switch", i.e., a switch statement diff --git a/libdrgn/cfi.c b/libdrgn/cfi.c index 0f316b37a..a58f68a5e 100644 --- a/libdrgn/cfi.c +++ b/libdrgn/cfi.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include diff --git a/libdrgn/cfi.h b/libdrgn/cfi.h index ab0766dcb..69c94ca89 100644 --- a/libdrgn/cfi.h +++ b/libdrgn/cfi.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file diff --git a/libdrgn/cityhash.h b/libdrgn/cityhash.h index 9b4fdbb94..848a57711 100644 --- a/libdrgn/cityhash.h +++ b/libdrgn/cityhash.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #ifndef DRGN_CITYHASH_H #define DRGN_CITYHASH_H diff --git a/libdrgn/cleanup.h b/libdrgn/cleanup.h new file mode 100644 index 000000000..d47b0a921 --- /dev/null +++ b/libdrgn/cleanup.h @@ -0,0 +1,48 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// SPDX-License-Identifier: LGPL-2.1-or-later + +/** + * @file + * + * Cleanup functions. + */ + +#ifndef DRGN_CLEANUP_H +#define DRGN_CLEANUP_H + +#include +#include + +#define _cleanup_(x) __attribute__((__cleanup__(x))) + +/** Call @c free() when the variable goes out of scope. */ +#define _cleanup_free_ _cleanup_(freep) +static inline void freep(void *p) +{ + free(*(void **)p); +} + +/** Call @c fclose() when the variable goes out of scope. */ +#define _cleanup_fclose_ _cleanup_(fclosep) +static inline void fclosep(FILE **fp) +{ + if (*fp) + fclose(*fp); +} + +/** + * Get the value of a pointer variable and reset it to @c NULL. + * + * This can be used to avoid freeing a variable declared with @ref + * _cleanup_free_ or another scope guard that is a no-op for @c NULL. + */ +#define no_cleanup_ptr(p) ({ __auto_type __ptr = (p); (p) = NULL; __ptr; }) + +/** + * Return a pointer declared with @ref _cleanup_free_ without freeing it. + * + * This can also be used for other scope guards that are a no-op for @c NULL. + */ +#define return_ptr(p) return no_cleanup_ptr(p) + +#endif /* DRGN_CLEANUP_H */ diff --git a/libdrgn/configure.ac b/libdrgn/configure.ac index 76ba9229c..1c705948d 100644 --- a/libdrgn/configure.ac +++ b/libdrgn/configure.ac @@ -1,7 +1,7 @@ dnl Copyright (c) Meta Platforms, Inc. and affiliates. -dnl SPDX-License-Identifier: GPL-3.0-or-later +dnl SPDX-License-Identifier: LGPL-2.1-or-later -AC_INIT([libdrgn], [0.0.20], +AC_INIT([libdrgn], [0.0.24], [https://github.com/osandov/drgn/issues],, [https://github.com/osandov/drgn]) @@ -17,6 +17,8 @@ LT_INIT AC_SYS_LARGEFILE +MY_C_AUTO + AC_ARG_ENABLE([openmp], [AS_HELP_STRING([--enable-openmp@<:@=ARG@:>@], [use OpenMP. ARG may be yes, no, or the name of @@ -108,6 +110,7 @@ AS_IF([test "x$enable_compiler_warnings" != xno], -Wall dnl -Wformat-overflow=2 dnl -Wformat-truncation=2 dnl + -Wimplicit-fallthrough dnl ], [WARN_CFLAGS], [$compiler_flags_test])]) AS_IF([test "x$enable_compiler_warnings" = xerror], [AX_APPEND_FLAG([-Werror], [WARN_CFLAGS])]) diff --git a/libdrgn/debug_info.c b/libdrgn/debug_info.c index 3f4a98dd1..703a1d718 100644 --- a/libdrgn/debug_info.c +++ b/libdrgn/debug_info.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include @@ -16,66 +16,34 @@ #include #include +#include "binary_buffer.h" #include "debug_info.h" +#include "elf_file.h" #include "error.h" #include "linux_kernel.h" +#include "openmp.h" +#include "platform.h" #include "program.h" #include "util.h" -static const char * const drgn_debug_scn_names[] = { - [DRGN_SCN_DEBUG_INFO] = ".debug_info", - [DRGN_SCN_DEBUG_TYPES] = ".debug_types", - [DRGN_SCN_DEBUG_ABBREV] = ".debug_abbrev", - [DRGN_SCN_DEBUG_STR] = ".debug_str", - [DRGN_SCN_DEBUG_STR_OFFSETS] = ".debug_str_offsets", - [DRGN_SCN_DEBUG_LINE] = ".debug_line", - [DRGN_SCN_DEBUG_LINE_STR] = ".debug_line_str", - [DRGN_SCN_DEBUG_ADDR] = ".debug_addr", - [DRGN_SCN_DEBUG_FRAME] = ".debug_frame", - [DRGN_SCN_EH_FRAME] = ".eh_frame", - [DRGN_SCN_ORC_UNWIND_IP] = ".orc_unwind_ip", - [DRGN_SCN_ORC_UNWIND] = ".orc_unwind", - [DRGN_SCN_DEBUG_LOC] = ".debug_loc", - [DRGN_SCN_DEBUG_LOCLISTS] = ".debug_loclists", - [DRGN_SCN_TEXT] = ".text", - [DRGN_SCN_GOT] = ".got", -}; - -struct drgn_error * -drgn_error_debug_info_scn(struct drgn_debug_info_module *module, - enum drgn_debug_info_scn scn, const char *ptr, - const char *message) -{ - const char *name = dwfl_module_info(module->dwfl_module, NULL, NULL, - NULL, NULL, NULL, NULL, NULL); - return drgn_error_format(DRGN_ERROR_OTHER, "%s: %s+%#tx: %s", - name, drgn_debug_scn_names[scn], - ptr - (const char *)module->scn_data[scn]->d_buf, - message); -} - -struct drgn_error *drgn_debug_info_buffer_error(struct binary_buffer *bb, - const char *pos, - const char *message) +static inline Dwarf *drgn_elf_file_dwarf_key(struct drgn_elf_file * const *entry) { - struct drgn_debug_info_buffer *buffer = - container_of(bb, struct drgn_debug_info_buffer, bb); - return drgn_error_debug_info_scn(buffer->module, buffer->scn, pos, - message); + return (*entry)->dwarf; } +DEFINE_HASH_TABLE_FUNCTIONS(drgn_elf_file_dwarf_table, drgn_elf_file_dwarf_key, + ptr_key_hash_pair, scalar_key_eq); +DEFINE_VECTOR_FUNCTIONS(drgn_module_vector); -DEFINE_VECTOR_FUNCTIONS(drgn_debug_info_module_vector) - -struct drgn_debug_info_module_key { +struct drgn_module_key { const void *build_id; size_t build_id_len; uint64_t start, end; }; -static inline struct drgn_debug_info_module_key -drgn_debug_info_module_key(struct drgn_debug_info_module * const *entry) +static inline struct drgn_module_key +drgn_module_key(struct drgn_module * const *entry) { - return (struct drgn_debug_info_module_key){ + return (struct drgn_module_key){ .build_id = (*entry)->build_id, .build_id_len = (*entry)->build_id_len, .start = (*entry)->start, @@ -84,27 +52,25 @@ drgn_debug_info_module_key(struct drgn_debug_info_module * const *entry) } static inline struct hash_pair -drgn_debug_info_module_key_hash_pair(const struct drgn_debug_info_module_key *key) +drgn_module_key_hash_pair(const struct drgn_module_key *key) { size_t hash = hash_bytes(key->build_id, key->build_id_len); hash = hash_combine(hash, key->start); hash = hash_combine(hash, key->end); return hash_pair_from_avalanching_hash(hash); } -static inline bool -drgn_debug_info_module_key_eq(const struct drgn_debug_info_module_key *a, - const struct drgn_debug_info_module_key *b) +static inline bool drgn_module_key_eq(const struct drgn_module_key *a, + const struct drgn_module_key *b) { return (a->build_id_len == b->build_id_len && memcmp(a->build_id, b->build_id, a->build_id_len) == 0 && a->start == b->start && a->end == b->end); } -DEFINE_HASH_TABLE_FUNCTIONS(drgn_debug_info_module_table, - drgn_debug_info_module_key, - drgn_debug_info_module_key_hash_pair, - drgn_debug_info_module_key_eq) +DEFINE_HASH_TABLE_FUNCTIONS(drgn_module_table, drgn_module_key, + drgn_module_key_hash_pair, drgn_module_key_eq); -DEFINE_HASH_SET_FUNCTIONS(c_string_set, c_string_key_hash_pair, c_string_key_eq) +DEFINE_HASH_SET_FUNCTIONS(c_string_set, c_string_key_hash_pair, + c_string_key_eq); /** * @c Dwfl_Callbacks::find_elf() implementation. @@ -128,7 +94,7 @@ static int drgn_dwfl_find_elf(Dwfl_Module *dwfl_module, void **userdatap, const char *name, Dwarf_Addr base, char **file_name, Elf **elfp) { - struct drgn_debug_info_module *module = *userdatap; + struct drgn_module *module = *userdatap; /* * libdwfl consumes the returned path, file descriptor, and ELF handle, * so clear the fields. @@ -151,7 +117,7 @@ static int drgn_dwfl_linux_proc_find_elf(Dwfl_Module *dwfl_module, Dwarf_Addr base, char **file_name, Elf **elfp) { - struct drgn_debug_info_module *module = *userdatap; + struct drgn_module *module = *userdatap; if (module->elf) { return drgn_dwfl_find_elf(dwfl_module, userdatap, name, base, file_name, elfp); @@ -169,7 +135,7 @@ static int drgn_dwfl_build_id_find_elf(Dwfl_Module *dwfl_module, Dwarf_Addr base, char **file_name, Elf **elfp) { - struct drgn_debug_info_module *module = *userdatap; + struct drgn_module *module = *userdatap; if (module->elf) { return drgn_dwfl_find_elf(dwfl_module, userdatap, name, base, file_name, elfp); @@ -213,25 +179,32 @@ static const Dwfl_Callbacks drgn_userspace_core_dump_dwfl_callbacks = { .section_address = drgn_dwfl_section_address, }; -static void -drgn_debug_info_module_destroy(struct drgn_debug_info_module *module) +static void drgn_module_destroy(struct drgn_module *module) { if (module) { drgn_error_destroy(module->err); - drgn_orc_module_info_deinit(module); - drgn_dwarf_module_info_deinit(module); + drgn_module_orc_info_deinit(module); + drgn_module_dwarf_info_deinit(module); elf_end(module->elf); if (module->fd != -1) close(module->fd); free(module->path); + for (struct drgn_elf_file_dwarf_table_iterator it = + drgn_elf_file_dwarf_table_first(&module->split_dwarf_files); + it.entry; + it = drgn_elf_file_dwarf_table_next(it)) + drgn_elf_file_destroy(*it.entry); + drgn_elf_file_dwarf_table_deinit(&module->split_dwarf_files); + if (module->debug_file != module->loaded_file) + drgn_elf_file_destroy(module->debug_file); + drgn_elf_file_destroy(module->loaded_file); free(module->name); free(module); } } -static void -drgn_debug_info_module_finish_indexing(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module) +static void drgn_module_finish_indexing(struct drgn_debug_info *dbinfo, + struct drgn_module *module) { module->state = DRGN_DEBUG_INFO_MODULE_INDEXED; if (module->name) { @@ -283,13 +256,13 @@ static int drgn_dwfl_module_removed(Dwfl_Module *dwfl_module, void *userdatap, * but dwfl_report_end() has the wrong signature for the removed * callback. */ - struct drgn_debug_info_module *module = *(void **)userdatap; + struct drgn_module *module = *(void **)userdatap; if (arg->finish_indexing && module && module->state == DRGN_DEBUG_INFO_MODULE_INDEXING) - drgn_debug_info_module_finish_indexing(arg->dbinfo, module); + drgn_module_finish_indexing(arg->dbinfo, module); if (arg->free_all || !module || module->state != DRGN_DEBUG_INFO_MODULE_INDEXED) { - drgn_debug_info_module_destroy(module); + drgn_module_destroy(module); } else { /* * The module was already indexed. Report it again so libdwfl @@ -306,26 +279,24 @@ static int drgn_dwfl_module_removed(Dwfl_Module *dwfl_module, void *userdatap, static void drgn_debug_info_free_modules(struct drgn_debug_info *dbinfo, bool finish_indexing, bool free_all) { - for (struct drgn_debug_info_module_table_iterator it = - drgn_debug_info_module_table_first(&dbinfo->modules); it.entry; ) { - struct drgn_debug_info_module *module = *it.entry; - struct drgn_debug_info_module **nextp = it.entry; + for (struct drgn_module_table_iterator it = + drgn_module_table_first(&dbinfo->modules); it.entry; ) { + struct drgn_module *module = *it.entry; + struct drgn_module **nextp = it.entry; do { - struct drgn_debug_info_module *next = module->next; + struct drgn_module *next = module->next; if (finish_indexing && - module->state == DRGN_DEBUG_INFO_MODULE_INDEXING) { - drgn_debug_info_module_finish_indexing(dbinfo, - module); - } + module->state == DRGN_DEBUG_INFO_MODULE_INDEXING) + drgn_module_finish_indexing(dbinfo, module); if (free_all || module->state != DRGN_DEBUG_INFO_MODULE_INDEXED) { if (module == *nextp) { if (nextp == it.entry && !next) { - it = drgn_debug_info_module_table_delete_iterator(&dbinfo->modules, - it); + it = drgn_module_table_delete_iterator(&dbinfo->modules, + it); } else { if (!next) - it = drgn_debug_info_module_table_next(it); + it = drgn_module_table_next(it); *nextp = next; } } @@ -334,10 +305,10 @@ static void drgn_debug_info_free_modules(struct drgn_debug_info *dbinfo, &userdatap, NULL, NULL, NULL, NULL, NULL, NULL); *userdatap = NULL; - drgn_debug_info_module_destroy(module); + drgn_module_destroy(module); } else { if (!next) - it = drgn_debug_info_module_table_next(it); + it = drgn_module_table_next(it); nextp = &module->next; } module = next; @@ -409,17 +380,19 @@ drgn_debug_info_report_module(struct drgn_debug_info_load_state *load, *new_ret = false; struct hash_pair hp; - struct drgn_debug_info_module_table_iterator it; + // Silence -Wmaybe-uninitialized false positive last seen with GCC 12 on + // i386 and Arm. + struct drgn_module_table_iterator it = {}; if (build_id_len) { - struct drgn_debug_info_module_key key = { + struct drgn_module_key key = { .build_id = build_id, .build_id_len = build_id_len, .start = start, .end = end, }; - hp = drgn_debug_info_module_table_hash(&key); - it = drgn_debug_info_module_table_search_hashed(&dbinfo->modules, - &key, hp); + hp = drgn_module_table_hash(&key); + it = drgn_module_table_search_hashed(&dbinfo->modules, &key, + hp); if (it.entry && (*it.entry)->state == DRGN_DEBUG_INFO_MODULE_INDEXED) { /* We've already indexed this module. */ @@ -457,11 +430,12 @@ drgn_debug_info_report_module(struct drgn_debug_info_load_state *load, if (new_ret) *new_ret = true; - struct drgn_debug_info_module *module = calloc(1, sizeof(*module)); + struct drgn_module *module = calloc(1, sizeof(*module)); if (!module) { err = &drgn_enomem; goto free; } + module->prog = load->dbinfo->prog; module->state = DRGN_DEBUG_INFO_MODULE_NEW; module->build_id = build_id; module->build_id_len = build_id_len; @@ -479,12 +453,12 @@ drgn_debug_info_report_module(struct drgn_debug_info_load_state *load, module->path = path_key; module->fd = fd; module->elf = elf; + drgn_elf_file_dwarf_table_init(&module->split_dwarf_files); /* path_key, fd and elf are owned by the module now. */ - if (!drgn_debug_info_module_vector_append(&load->new_modules, - &module)) { - drgn_debug_info_module_destroy(module); + if (!drgn_module_vector_append(&load->new_modules, &module)) { + drgn_module_destroy(module); return &drgn_enomem; } if (build_id_len) { @@ -496,12 +470,11 @@ drgn_debug_info_report_module(struct drgn_debug_info_load_state *load, */ module->next = (*it.entry)->next; (*it.entry)->next = module; - } else if (drgn_debug_info_module_table_insert_searched(&dbinfo->modules, - &module, - hp, - NULL) < 0) { - load->new_modules.size--; - drgn_debug_info_module_destroy(module); + } else if (drgn_module_table_insert_searched(&dbinfo->modules, + &module, hp, + NULL) < 0) { + drgn_module_vector_pop(&load->new_modules); + drgn_module_destroy(module); return &drgn_enomem; } } @@ -627,11 +600,11 @@ struct drgn_mapped_file_segment { uint64_t file_offset; }; -DEFINE_VECTOR(drgn_mapped_file_segment_vector, struct drgn_mapped_file_segment) +DEFINE_VECTOR(drgn_mapped_file_segment_vector, struct drgn_mapped_file_segment); DEFINE_HASH_MAP(drgn_mapped_files, const char *, struct drgn_mapped_file_segment_vector, c_string_key_hash_pair, - c_string_key_eq) + c_string_key_eq); struct userspace_core_report_state { struct drgn_mapped_files files; @@ -752,10 +725,10 @@ userspace_core_get_mapped_files(struct drgn_debug_info_load_state *load, * mappings with different memory protections even though the * protection is not included in NT_FILE. Merge them if we can. */ - if (it.entry->value.size > 0 && - drgn_mapped_file_segments_contiguous(&it.entry->value.data[it.entry->value.size - 1], - &segment)) - it.entry->value.data[it.entry->value.size - 1].end = segment.end; + if (!drgn_mapped_file_segment_vector_empty(&it.entry->value) + && drgn_mapped_file_segments_contiguous(drgn_mapped_file_segment_vector_last(&it.entry->value), + &segment)) + drgn_mapped_file_segment_vector_last(&it.entry->value)->end = segment.end; else if (!drgn_mapped_file_segment_vector_append(&it.entry->value, &segment)) return &drgn_enomem; @@ -1344,8 +1317,8 @@ userspace_core_report_mapped_files(struct drgn_debug_info_load_state *load, it.entry; it = drgn_mapped_files_next(it)) { err = userspace_core_maybe_report_file(load, core, it.entry->key, - it.entry->value.data, - it.entry->value.size); + drgn_mapped_file_segment_vector_begin(&it.entry->value), + drgn_mapped_file_segment_vector_size(&it.entry->value)); if (err) return err; } @@ -1808,7 +1781,8 @@ static struct drgn_error *relocate_elf_file(Elf *elf) } static struct drgn_error * -drgn_debug_info_find_sections(struct drgn_debug_info_module *module) +drgn_module_find_files(struct drgn_debug_info_load_state *load, + struct drgn_module *module) { struct drgn_error *err; @@ -1818,60 +1792,67 @@ drgn_debug_info_find_sections(struct drgn_debug_info_module *module) return err; } - /* - * Note: not dwfl_module_getelf(), because then libdwfl applies - * ELF relocations to all sections, not just debug sections. - */ - Dwarf_Addr bias; + GElf_Addr loaded_file_bias; + Elf *loaded_elf = NULL; + Dwarf_Addr debug_file_bias; Dwarf *dwarf; - #pragma omp critical(drgn_dwfl_module_getdwarf) - dwarf = dwfl_module_getdwarf(module->dwfl_module, &bias); - if (!dwarf) - return drgn_error_libdwfl(); - Elf *elf = dwarf_getelf(dwarf); - if (!elf) - return drgn_error_libdw(); - GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr(elf, &ehdr_mem); - if (!ehdr) - return drgn_error_libelf(); - drgn_platform_from_elf(ehdr, &module->platform); - - size_t shstrndx; - if (elf_getshdrstrndx(elf, &shstrndx)) - return drgn_error_libelf(); - - Elf_Scn *scn = NULL; - while ((scn = elf_nextscn(elf, scn))) { - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr(scn, &shdr_mem); - if (!shdr) - return drgn_error_libelf(); - - if (shdr->sh_type != SHT_PROGBITS) - continue; - const char *scnname = elf_strptr(elf, shstrndx, shdr->sh_name); - if (!scnname) - return drgn_error_libelf(); - - for (size_t i = 0; i < DRGN_NUM_DEBUG_SCNS; i++) { - if (!module->scns[i] && - strcmp(scnname, drgn_debug_scn_names[i]) == 0) { - module->scns[i] = scn; - break; - } + err = NULL; + #pragma omp critical(drgn_module_find_files) + { + // We don't need the loaded file for the Linux kernel, and we + // always report the debug file as the main file to libdwfl. + if (!(load->dbinfo->prog->flags & DRGN_PROGRAM_IS_LINUX_KERNEL)) { + loaded_elf = dwfl_module_getelf(module->dwfl_module, + &loaded_file_bias); + if (!loaded_elf) + err = drgn_error_libdwfl(); + } + if (!err) { + dwarf = dwfl_module_getdwarf(module->dwfl_module, + &debug_file_bias); + if (!dwarf) + err = drgn_error_libdwfl(); } } + if (err) + return err; + + const char *loaded_file_path; + const char *debug_file_path; + dwfl_module_info(module->dwfl_module, NULL, NULL, NULL, NULL, NULL, + &loaded_file_path, &debug_file_path); + // If the loaded file also has debugging information, debug_file_path is + // NULL. (debug_file_path is also NULL if libdwfl got the debug file + // from debuginfod, so this isn't 100% correct, but it'll at least + // identify the module.) + if (!debug_file_path) + debug_file_path = loaded_file_path; + + module->debug_file_bias = debug_file_bias; + err = drgn_elf_file_create(module, debug_file_path, dwarf_getelf(dwarf), + &module->debug_file); + if (err) { + module->debug_file = NULL; + return err; + } + module->debug_file->dwarf = dwarf; + if (!module->debug_file->scns[DRGN_SCN_DEBUG_INFO] || + !module->debug_file->scns[DRGN_SCN_DEBUG_ABBREV]) { + return drgn_error_create(DRGN_ERROR_OTHER, + "missing debugging information sections"); + } Dwarf *altdwarf = dwarf_getalt(dwarf); if (altdwarf) { - elf = dwarf_getelf(altdwarf); - if (!elf) + Elf *altelf = dwarf_getelf(altdwarf); + if (!altelf) return drgn_error_libdw(); - if (elf_getshdrstrndx(elf, &shstrndx)) + size_t shstrndx; + if (elf_getshdrstrndx(altelf, &shstrndx)) return drgn_error_libelf(); - scn = NULL; - while ((scn = elf_nextscn(elf, scn))) { + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn(altelf, scn))) { GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr(scn, &shdr_mem); if (!shdr) @@ -1879,7 +1860,8 @@ drgn_debug_info_find_sections(struct drgn_debug_info_module *module) if (shdr->sh_type != SHT_PROGBITS) continue; - const char *scnname = elf_strptr(elf, shstrndx, shdr->sh_name); + const char *scnname = elf_strptr(altelf, shstrndx, + shdr->sh_name); if (!scnname) return drgn_error_libelf(); @@ -1887,98 +1869,56 @@ drgn_debug_info_find_sections(struct drgn_debug_info_module *module) * TODO: save more sections and support imported units. */ if (strcmp(scnname, ".debug_info") == 0 && - !module->alt_debug_info) - module->alt_debug_info = scn; - else if (strcmp(scnname, ".debug_str") == 0 && - !module->alt_debug_str) - module->alt_debug_str = scn; + !module->debug_file->alt_debug_info_data) { + err = read_elf_section(scn, + &module->debug_file->alt_debug_info_data); + if (err) + return err; + } else if (strcmp(scnname, ".debug_str") == 0 && + !module->debug_file->alt_debug_str_data) { + err = read_elf_section(scn, + &module->debug_file->alt_debug_str_data); + if (err) + return err; + } } } + err = drgn_elf_file_precache_sections(module->debug_file); + if (err) + return err; - return NULL; -} - -static void truncate_null_terminated_section(Elf_Data *data) -{ - if (data) { - const char *buf = data->d_buf; - const char *nul = memrchr(buf, '\0', data->d_size); - if (nul) - data->d_size = nul - buf + 1; - else - data->d_size = 0; - } -} - -static struct drgn_error * -drgn_debug_info_precache_sections(struct drgn_debug_info_module *module) -{ - struct drgn_error *err; - - for (size_t i = 0; i < DRGN_NUM_DEBUG_SCN_DATA_PRECACHE; i++) { - if (module->scns[i]) { - err = read_elf_section(module->scns[i], - &module->scn_data[i]); - if (err) + if (loaded_elf) { + module->loaded_file_bias = loaded_file_bias; + if (loaded_elf == module->debug_file->elf) { + module->loaded_file = module->debug_file; + } else { + err = drgn_elf_file_create(module, loaded_file_path, + loaded_elf, + &module->loaded_file); + if (err) { + module->loaded_file = NULL; return err; + } } } - if (module->alt_debug_info) { - err = read_elf_section(module->alt_debug_info, - &module->alt_debug_info_data); - if (err) - return err; - } - if (module->alt_debug_str) { - err = read_elf_section(module->alt_debug_str, - &module->alt_debug_str_data); - if (err) - return err; - } - - /* - * Truncate any extraneous bytes so that we can assume that a pointer - * within .debug_{,line_}str is always null-terminated. - */ - truncate_null_terminated_section(module->scn_data[DRGN_SCN_DEBUG_STR]); - truncate_null_terminated_section(module->scn_data[DRGN_SCN_DEBUG_LINE_STR]); - truncate_null_terminated_section(module->alt_debug_str_data); return NULL; } -struct drgn_error * -drgn_debug_info_module_cache_section(struct drgn_debug_info_module *module, - enum drgn_debug_info_scn scn) -{ - if (module->scn_data[scn]) - return NULL; - return read_elf_section(module->scns[scn], &module->scn_data[scn]); -} - static struct drgn_error * drgn_debug_info_read_module(struct drgn_debug_info_load_state *load, struct drgn_dwarf_index_state *index, - struct drgn_debug_info_module *head) + struct drgn_module *head) { struct drgn_error *err; - struct drgn_debug_info_module *module; + struct drgn_module *module; for (module = head; module; module = module->next) { - err = drgn_debug_info_find_sections(module); + err = drgn_module_find_files(load, module); if (err) { module->err = err; continue; } - if (module->scns[DRGN_SCN_DEBUG_INFO] && - module->scns[DRGN_SCN_DEBUG_ABBREV]) { - err = drgn_debug_info_precache_sections(module); - if (err) { - module->err = err; - continue; - } - module->state = DRGN_DEBUG_INFO_MODULE_INDEXING; - return drgn_dwarf_index_read_module(index, - module); - } + module->state = DRGN_DEBUG_INFO_MODULE_INDEXING; + return drgn_dwarf_index_read_module(index, module); } /* * We checked all of the files and didn't find debugging information. @@ -2011,25 +1951,26 @@ drgn_debug_info_read_module(struct drgn_debug_info_load_state *load, static struct drgn_error * drgn_debug_info_update_index(struct drgn_debug_info_load_state *load) { - if (!load->new_modules.size) + if (drgn_module_vector_empty(&load->new_modules)) return NULL; struct drgn_debug_info *dbinfo = load->dbinfo; if (!c_string_set_reserve(&dbinfo->module_names, - c_string_set_size(&dbinfo->module_names) + - load->new_modules.size)) + c_string_set_size(&dbinfo->module_names) + + drgn_module_vector_size(&load->new_modules))) return &drgn_enomem; struct drgn_dwarf_index_state index; if (!drgn_dwarf_index_state_init(&index, dbinfo)) return &drgn_enomem; struct drgn_error *err = NULL; - #pragma omp parallel for schedule(dynamic) - for (size_t i = 0; i < load->new_modules.size; i++) { + #pragma omp parallel for schedule(dynamic) num_threads(drgn_num_threads) + for (size_t i = 0; i < drgn_module_vector_size(&load->new_modules); i++) { if (err) continue; + struct drgn_module *module = + *drgn_module_vector_at(&load->new_modules, i); struct drgn_error *module_err = - drgn_debug_info_read_module(load, &index, - load->new_modules.data[i]); + drgn_debug_info_read_module(load, &index, module); if (module_err) { #pragma omp critical(drgn_debug_info_update_index_error) if (err) @@ -2038,11 +1979,11 @@ drgn_debug_info_update_index(struct drgn_debug_info_load_state *load) err = module_err; } } - if (!err) + if (!err) { + drgn_debug_info_free_modules(dbinfo, true, false); err = drgn_dwarf_info_update_index(&index); + } drgn_dwarf_index_state_deinit(&index); - if (!err) - drgn_debug_info_free_modules(dbinfo, true, false); return err; } @@ -2055,7 +1996,7 @@ drgn_debug_info_report_flush(struct drgn_debug_info_load_state *load) dwfl_report_begin_add(dbinfo->dwfl); if (err) return err; - load->new_modules.size = 0; + drgn_module_vector_clear(&load->new_modules); return NULL; } @@ -2095,6 +2036,7 @@ struct drgn_error *drgn_debug_info_load(struct drgn_debug_info *dbinfo, .load_default = load_default, .load_main = load_main, .new_modules = VECTOR_INIT, + .errors = STRING_BUILDER_INIT, .max_errors = max_errors ? atoi(max_errors) : 5, }; dwfl_report_begin_add(dbinfo->dwfl); @@ -2127,13 +2069,9 @@ struct drgn_error *drgn_debug_info_load(struct drgn_debug_info *dbinfo, * the core dump. */ - /* - * If this fails, it's too late to roll back. This can only fail with - * enomem, so it's not a big deal. - */ err = drgn_debug_info_report_finalize_errors(&load); out: - drgn_debug_info_module_vector_deinit(&load.new_modules); + drgn_module_vector_deinit(&load.new_modules); return err; err: @@ -2167,7 +2105,7 @@ struct drgn_error *drgn_debug_info_create(struct drgn_program *prog, free(dbinfo); return drgn_error_libdwfl(); } - drgn_debug_info_module_table_init(&dbinfo->modules); + drgn_module_table_init(&dbinfo->modules); c_string_set_init(&dbinfo->module_names); drgn_dwarf_info_init(dbinfo); *ret = dbinfo; @@ -2181,45 +2119,114 @@ void drgn_debug_info_destroy(struct drgn_debug_info *dbinfo) drgn_dwarf_info_deinit(dbinfo); c_string_set_deinit(&dbinfo->module_names); drgn_debug_info_free_modules(dbinfo, false, true); - assert(drgn_debug_info_module_table_empty(&dbinfo->modules)); - drgn_debug_info_module_table_deinit(&dbinfo->modules); + assert(drgn_module_table_empty(&dbinfo->modules)); + drgn_module_table_deinit(&dbinfo->modules); dwfl_end(dbinfo->dwfl); free(dbinfo); } +struct drgn_elf_file *drgn_module_find_dwarf_file(struct drgn_module *module, + Dwarf *dwarf) +{ + if (!module->debug_file) + return NULL; + if (dwarf == module->debug_file->dwarf) + return module->debug_file; + struct drgn_elf_file_dwarf_table_iterator it = + drgn_elf_file_dwarf_table_search(&module->split_dwarf_files, + &dwarf); + return it.entry ? *it.entry : NULL; +} + struct drgn_error * -drgn_debug_info_module_find_cfi(struct drgn_program *prog, - struct drgn_debug_info_module *module, - uint64_t pc, struct drgn_cfi_row **row_ret, - bool *interrupted_ret, - drgn_register_number *ret_addr_regno_ret) +drgn_module_create_split_dwarf_file(struct drgn_module *module, + const char *name, Dwarf *dwarf, + struct drgn_elf_file **ret) { struct drgn_error *err; + err = drgn_elf_file_create(module, name, dwarf_getelf(dwarf), ret); + if (err) + return err; + err = drgn_elf_file_precache_sections(*ret); + if (err) { + drgn_elf_file_destroy(*ret); + return err; + } + (*ret)->dwarf = dwarf; + int r = drgn_elf_file_dwarf_table_insert(&module->split_dwarf_files, + ret, NULL); + if (r < 0) { + drgn_elf_file_destroy(*ret); + return &drgn_enomem; + } + assert(r > 0); + return NULL; +} - Dwarf_Addr bias; - dwfl_module_info(module->dwfl_module, NULL, NULL, NULL, &bias, NULL, - NULL, NULL); - uint64_t unbiased_pc = pc - bias; +struct drgn_error * +drgn_module_find_cfi(struct drgn_program *prog, struct drgn_module *module, + uint64_t pc, struct drgn_elf_file **file_ret, + struct drgn_cfi_row **row_ret, bool *interrupted_ret, + drgn_register_number *ret_addr_regno_ret) +{ + struct drgn_error *err; + + // If the file's platform doesn't match the program's, we can't use its + // CFI. + const bool can_use_loaded_file = + (module->loaded_file && + drgn_platforms_equal(&module->loaded_file->platform, + &prog->platform)); + const bool can_use_debug_file = + (module->debug_file && + drgn_platforms_equal(&module->debug_file->platform, + &prog->platform)); if (prog->prefer_orc_unwinder) { - err = drgn_debug_info_find_orc_cfi(module, unbiased_pc, row_ret, - interrupted_ret, - ret_addr_regno_ret); - if (err != &drgn_not_found) - return err; - return drgn_debug_info_find_dwarf_cfi(module, unbiased_pc, - row_ret, interrupted_ret, - ret_addr_regno_ret); + if (can_use_debug_file) { + *file_ret = module->debug_file; + err = drgn_module_find_orc_cfi(module, pc, row_ret, + interrupted_ret, + ret_addr_regno_ret); + if (err != &drgn_not_found) + return err; + err = drgn_module_find_dwarf_cfi(module, pc, row_ret, + interrupted_ret, + ret_addr_regno_ret); + if (err != &drgn_not_found) + return err; + } + if (can_use_loaded_file) { + *file_ret = module->loaded_file; + return drgn_module_find_eh_cfi(module, pc, row_ret, + interrupted_ret, + ret_addr_regno_ret); + } } else { - err = drgn_debug_info_find_dwarf_cfi(module, unbiased_pc, - row_ret, interrupted_ret, - ret_addr_regno_ret); - if (err != &drgn_not_found) - return err; - return drgn_debug_info_find_orc_cfi(module, unbiased_pc, - row_ret, interrupted_ret, - ret_addr_regno_ret); + if (can_use_debug_file) { + *file_ret = module->debug_file; + err = drgn_module_find_dwarf_cfi(module, pc, row_ret, + interrupted_ret, + ret_addr_regno_ret); + if (err != &drgn_not_found) + return err; + } + if (can_use_loaded_file) { + *file_ret = module->loaded_file; + err = drgn_module_find_eh_cfi(module, pc, row_ret, + interrupted_ret, + ret_addr_regno_ret); + if (err != &drgn_not_found) + return err; + } + if (can_use_debug_file) { + *file_ret = module->debug_file; + return drgn_module_find_orc_cfi(module, pc, row_ret, + interrupted_ret, + ret_addr_regno_ret); + } } + return &drgn_not_found; } #if !_ELFUTILS_PREREQ(0, 175) @@ -2302,21 +2309,6 @@ struct drgn_error *find_elf_file(char **path_ret, int *fd_ret, Elf **elf_ret, return NULL; } -struct drgn_error *read_elf_section(Elf_Scn *scn, Elf_Data **ret) -{ - GElf_Shdr shdr_mem, *shdr; - shdr = gelf_getshdr(scn, &shdr_mem); - if (!shdr) - return drgn_error_libelf(); - if ((shdr->sh_flags & SHF_COMPRESSED) && elf_compress(scn, 0, 0) < 0) - return drgn_error_libelf(); - Elf_Data *data = elf_rawdata(scn, NULL); - if (!data) - return drgn_error_libelf(); - *ret = data; - return NULL; -} - /* * Get the start address from the first loadable segment and the end address * from the last loadable segment. diff --git a/libdrgn/debug_info.h b/libdrgn/debug_info.h index 4976af62c..5449aaca3 100644 --- a/libdrgn/debug_info.h +++ b/libdrgn/debug_info.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -16,16 +16,16 @@ #include #include -#include "binary_buffer.h" #include "cfi.h" #include "drgn.h" #include "dwarf_info.h" #include "hash_table.h" #include "orc_info.h" -#include "platform.h" #include "string_builder.h" #include "vector.h" +struct drgn_elf_file; + /** * @ingroup Internals * @@ -39,8 +39,8 @@ * @{ */ -/** State of a @ref drgn_debug_info_module. */ -enum drgn_debug_info_module_state { +/** State of a @ref drgn_module. */ +enum drgn_module_state { /** Reported but not indexed. */ DRGN_DEBUG_INFO_MODULE_NEW, /** Reported and will be indexed on success. */ @@ -49,35 +49,7 @@ enum drgn_debug_info_module_state { DRGN_DEBUG_INFO_MODULE_INDEXED, } __attribute__((__packed__)); -enum drgn_debug_info_scn { - /* Sections whose data we should cache when loading the module. */ - DRGN_SCN_DEBUG_INFO, - DRGN_SCN_DEBUG_TYPES, - DRGN_SCN_DEBUG_ABBREV, - DRGN_SCN_DEBUG_STR, - DRGN_SCN_DEBUG_STR_OFFSETS, - DRGN_SCN_DEBUG_LINE, - DRGN_SCN_DEBUG_LINE_STR, - - DRGN_NUM_DEBUG_SCN_DATA_PRECACHE, - - /* Sections whose data we should cache when it is first used. */ - DRGN_SCN_DEBUG_ADDR = DRGN_NUM_DEBUG_SCN_DATA_PRECACHE, - DRGN_SCN_DEBUG_FRAME, - DRGN_SCN_EH_FRAME, - DRGN_SCN_ORC_UNWIND_IP, - DRGN_SCN_ORC_UNWIND, - DRGN_SCN_DEBUG_LOC, - DRGN_SCN_DEBUG_LOCLISTS, - - DRGN_NUM_DEBUG_SCN_DATA, - - /* Sections whose data doesn't need to be cached. */ - DRGN_SCN_TEXT = DRGN_NUM_DEBUG_SCN_DATA, - DRGN_SCN_GOT, - - DRGN_NUM_DEBUG_SCNS, -}; +DEFINE_HASH_TABLE_TYPE(drgn_elf_file_dwarf_table, struct drgn_elf_file *); /** * A module reported to a @ref drgn_debug_info. @@ -86,9 +58,11 @@ enum drgn_debug_info_scn { * not loaded). * * Files are identified by canonical path and, if present, build ID. Each (path, - * address range) is uniquely represented by a @ref drgn_debug_info_module. + * address range) is uniquely represented by a @ref drgn_module. */ -struct drgn_debug_info_module { +struct drgn_module { + struct drgn_program *prog; + /** @c NULL if the module does not have a build ID. */ const void *build_id; /** Zero if the module does not have a build ID. */ @@ -99,21 +73,32 @@ struct drgn_debug_info_module { char *name; Dwfl_Module *dwfl_module; - struct drgn_platform platform; - Elf_Scn *scns[DRGN_NUM_DEBUG_SCNS]; - Elf_Scn *alt_debug_info; - Elf_Scn *alt_debug_str; - Elf_Data *scn_data[DRGN_NUM_DEBUG_SCN_DATA]; - Elf_Data *alt_debug_info_data; - Elf_Data *alt_debug_str_data; + /** File that is loaded into the program. */ + struct drgn_elf_file *loaded_file; + /** File containing debugging information. */ + struct drgn_elf_file *debug_file; + /** + * Difference between addresses in program and addresses in @ref + * drgn_module::loaded_file. + */ + uint64_t loaded_file_bias; + /** + * Difference between addresses in program and addresses in @ref + * drgn_module::debug_file. + */ + uint64_t debug_file_bias; + + struct drgn_elf_file_dwarf_table split_dwarf_files; /** DWARF debugging information. */ - struct drgn_dwarf_module_info dwarf; + struct drgn_module_dwarf_info dwarf; /** ORC unwinder information. */ - struct drgn_orc_module_info orc; + struct drgn_module_orc_info orc; - /** Whether .debug_frame and .eh_frame have been parsed. */ - bool parsed_frames; + /** Whether DWARF CFI from .debug_frame has been parsed. */ + bool parsed_debug_frame; + /** Whether EH CFI from .eh_frame has been parsed. */ + bool parsed_eh_frame; /** Whether ORC unwinder data has been parsed. */ bool parsed_orc; @@ -125,7 +110,7 @@ struct drgn_debug_info_module { char *path; Elf *elf; int fd; - enum drgn_debug_info_module_state state; + enum drgn_module_state state; /** Error while loading. */ struct drgn_error *err; /** @@ -136,45 +121,12 @@ struct drgn_debug_info_module { * loading, all files with the same build ID and address range are * linked in a list. Only one is indexed; the rest are destroyed. */ - struct drgn_debug_info_module *next; + struct drgn_module *next; }; -struct drgn_error * -drgn_debug_info_module_cache_section(struct drgn_debug_info_module *module, - enum drgn_debug_info_scn scn); +DEFINE_HASH_TABLE_TYPE(drgn_module_table, struct drgn_module *); -struct drgn_error * -drgn_error_debug_info_scn(struct drgn_debug_info_module *module, - enum drgn_debug_info_scn scn, const char *ptr, - const char *message); - -struct drgn_debug_info_buffer { - struct binary_buffer bb; - struct drgn_debug_info_module *module; - enum drgn_debug_info_scn scn; -}; - -struct drgn_error *drgn_debug_info_buffer_error(struct binary_buffer *bb, - const char *pos, - const char *message); - -static inline void -drgn_debug_info_buffer_init(struct drgn_debug_info_buffer *buffer, - struct drgn_debug_info_module *module, - enum drgn_debug_info_scn scn) -{ - binary_buffer_init(&buffer->bb, module->scn_data[scn]->d_buf, - module->scn_data[scn]->d_size, - drgn_platform_is_little_endian(&module->platform), - drgn_debug_info_buffer_error); - buffer->module = module; - buffer->scn = scn; -} - -DEFINE_HASH_TABLE_TYPE(drgn_debug_info_module_table, - struct drgn_debug_info_module *) - -DEFINE_HASH_SET_TYPE(c_string_set, const char *) +DEFINE_HASH_SET_TYPE(c_string_set, const char *); /** Cache of debugging information. */ struct drgn_debug_info { @@ -184,12 +136,12 @@ struct drgn_debug_info { /** DWARF frontend library handle. */ Dwfl *dwfl; /** Modules keyed by build ID and address range. */ - struct drgn_debug_info_module_table modules; + struct drgn_module_table modules; /** * Names of indexed modules. * - * The entries in this set are @ref drgn_debug_info_module::name, so - * they should not be freed. + * The entries in this set are @ref drgn_module::name, so they should + * not be freed. */ struct c_string_set module_names; /** DWARF debugging information. */ @@ -203,8 +155,7 @@ struct drgn_error *drgn_debug_info_create(struct drgn_program *prog, /** Destroy a @ref drgn_debug_info. */ void drgn_debug_info_destroy(struct drgn_debug_info *dbinfo); -DEFINE_VECTOR_TYPE(drgn_debug_info_module_vector, - struct drgn_debug_info_module *) +DEFINE_VECTOR_TYPE(drgn_module_vector, struct drgn_module *); /** State tracked while loading debugging information. */ struct drgn_debug_info_load_state { @@ -214,7 +165,7 @@ struct drgn_debug_info_load_state { const bool load_default; const bool load_main; /** Newly added modules to be indexed. */ - struct drgn_debug_info_module_vector new_modules; + struct drgn_module_vector new_modules; /** Formatted errors reported by @ref drgn_debug_info_report_error(). */ struct string_builder errors; /** Number of errors reported by @ref drgn_debug_info_report_error(). */ @@ -296,8 +247,8 @@ drgn_debug_info_main_language(struct drgn_debug_info *dbinfo, const struct drgn_language **ret); /** @ref drgn_type_find_fn() that uses debugging information. */ -struct drgn_error *drgn_debug_info_find_type(enum drgn_type_kind kind, - const char *name, size_t name_len, +struct drgn_error *drgn_debug_info_find_type(uint64_t kinds, const char *name, + size_t name_len, const char *filename, void *arg, struct drgn_qualified_type *ret); @@ -308,12 +259,21 @@ drgn_debug_info_find_object(const char *name, size_t name_len, enum drgn_find_object_flags flags, void *arg, struct drgn_object *ret); +struct drgn_elf_file *drgn_module_find_dwarf_file(struct drgn_module *module, + Dwarf *dwarf); + +struct drgn_error * +drgn_module_create_split_dwarf_file(struct drgn_module *module, + const char *name, Dwarf *dwarf, + struct drgn_elf_file **ret); + /** - * Get the Call Frame Information in a @ref drgn_debug_info_module at a given - * program counter. + * Get the Call Frame Information in a @ref drgn_module at a given program + * counter. * * @param[in] module Module containing @p pc. * @param[in] pc Program counter. + * @param[out] file_ret Returned file containing CFI. * @param[in,out] row_ret Returned CFI row. * @param[out] interrupted_ret Whether the found frame interrupted its caller. * @param[out] ret_addr_regno_ret Returned return address register number. @@ -321,23 +281,16 @@ drgn_debug_info_find_object(const char *name, size_t name_len, * drgn_not_found if CFI wasn't found. */ struct drgn_error * -drgn_debug_info_module_find_cfi(struct drgn_program *prog, - struct drgn_debug_info_module *module, - uint64_t pc, struct drgn_cfi_row **row_ret, - bool *interrupted_ret, - drgn_register_number *ret_addr_regno_ret); +drgn_module_find_cfi(struct drgn_program *prog, struct drgn_module *module, + uint64_t pc, struct drgn_elf_file **file_ret, + struct drgn_cfi_row **row_ret, bool *interrupted_ret, + drgn_register_number *ret_addr_regno_ret); struct drgn_error *open_elf_file(const char *path, int *fd_ret, Elf **elf_ret); struct drgn_error *find_elf_file(char **path_ret, int *fd_ret, Elf **elf_ret, const char * const *path_formats, ...); -/* - * NB: if the section is SHT_NOBITS, this returns an Elf_Data with d_buf = NULL - * and d_size >= 0. - */ -struct drgn_error *read_elf_section(Elf_Scn *scn, Elf_Data **ret); - struct drgn_error *elf_address_range(Elf *elf, uint64_t bias, uint64_t *start_ret, uint64_t *end_ret); diff --git a/libdrgn/drgn.h.in b/libdrgn/drgn.h.in index ae8734815..2d0863e5f 100644 --- a/libdrgn/drgn.h.in +++ b/libdrgn/drgn.h.in @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -25,11 +26,18 @@ /** * @mainpage * - * libdrgn provides the functionality of the drgn programmable debugger as a - * library. It implements the main drgn abstractions: @ref Programs, @ref Types, - * and @ref Objects. See Modules for detailed + * libdrgn provides the functionality of the + * [drgn](https://github.com/osandov/drgn) programmable debugger as a library. + * It implements the main drgn abstractions: @ref Programs, @ref Types, @ref + * Objects, and @ref StackTraces. See [Modules](modules.html) for detailed * documentation. + * + * @subsection ThreadSafety Thread Safety + * + * Only one thread at a time should access the same @ref drgn_program (including + * @ref drgn_object, @ref drgn_type, @ref drgn_stack_trace, etc. from that + * program). It is safe to use different @ref drgn_program%s from concurrent + * threads. */ /** Major version of drgn. */ @@ -230,6 +238,15 @@ struct drgn_error *drgn_error_format_fault(uint64_t address, struct drgn_error *drgn_error_copy(struct drgn_error *src) __attribute__((__returns_nonnull__)); +/** + * Return a string representation of a @ref drgn_error. + * + * @param[in] err Error to write. + * @return Returned string, or @c NULL if memory could not be allocated. On + * success, must be freed with @c free(). + */ +char *drgn_error_string(struct drgn_error *err); + /** * Write a @ref drgn_error followed by a newline to a @c stdio stream. * @@ -269,6 +286,8 @@ void drgn_error_destroy(struct drgn_error *err); * @{ */ +struct drgn_language; // IWYU pragma: export + /** C */ extern const struct drgn_language drgn_language_c; /** C++ */ @@ -375,6 +394,8 @@ enum drgn_architecture { DRGN_ARCH_PPC64, DRGN_ARCH_RISCV64, DRGN_ARCH_RISCV32, + DRGN_ARCH_S390X, + DRGN_ARCH_S390, }; /** Flags describing a @ref drgn_platform. */ @@ -394,14 +415,14 @@ enum drgn_platform_flags { * * The environment that a program runs on. */ -struct drgn_platform; +struct drgn_platform; // IWYU pragma: export /** * @struct drgn_register * * A processor register. */ -struct drgn_register; +struct drgn_register; // IWYU pragma: export /** * Create a @ref drgn_platform. @@ -478,7 +499,7 @@ struct drgn_thread; * drgn_program_from_kernel(), or @ref drgn_program_from_pid(). It must be freed * with @ref drgn_program_destroy(). */ -struct drgn_program; +struct drgn_program; // IWYU pragma: export /** Flags which apply to a @ref drgn_program. */ enum drgn_program_flags { @@ -560,7 +581,9 @@ bool drgn_filename_matches(const char *haystack, const char *needle); /** * Callback for finding a type. * - * @param[in] kind Kind of type. + * @param[in] kinds Kinds of types to find, as a bitmask of bits shifted by @ref + * drgn_type_kind. E.g., `(1 << DRGN_TYPE_STRUCT) | (1 << DRGN_TYPE_CLASS)` + * means to find a structure or class type. * @param[in] name Name of type (or tag, for structs, unions, and enums). This * is @em not null-terminated. * @param[in] name_len Length of @p name. @@ -573,8 +596,8 @@ bool drgn_filename_matches(const char *haystack, const char *needle); * considered fatal. */ typedef struct drgn_error * -(*drgn_type_find_fn)(enum drgn_type_kind kind, const char *name, - size_t name_len, const char *filename, void *arg, +(*drgn_type_find_fn)(uint64_t kinds, const char *name, size_t name_len, + const char *filename, void *arg, struct drgn_qualified_type *ret); /** @@ -830,7 +853,7 @@ struct drgn_error *drgn_program_find_object(struct drgn_program *prog, * * A @ref drgn_symbol represents an entry in a program's symbol table. */ -struct drgn_symbol; +struct drgn_symbol; // IWYU pragma: export /** * Get the symbol containing the given address. @@ -911,6 +934,155 @@ struct drgn_error *drgn_program_element_info(struct drgn_program *prog, /** @} */ +/** + * @defgroup Logging Logging + * + * Logging configuration. + * + * drgn can log to a file (@ref drgn_program_set_log_file()) or an arbitrary + * callback (@ref drgn_program_set_log_callback()). Messages can be filtered + * based on the log level (@ref drgn_program_set_log_level()). + * + * By default, the log file is set to `stderr` and the log level is @ref + * DRGN_LOG_NONE, so logging is disabled. + * + * @{ + */ + +/** Log levels. */ +enum drgn_log_level { + DRGN_LOG_DEBUG = 0, + DRGN_LOG_INFO = 1, + DRGN_LOG_WARNING = 2, + DRGN_LOG_ERROR = 3, + DRGN_LOG_CRITICAL = 4, +}; +/** Don't log anything. */ +#define DRGN_LOG_NONE (DRGN_LOG_CRITICAL + 1) + +/** + * Set the minimum log level. + * + * Messages below this level will not be logged. + * + * @param[in] level Minimum @ref drgn_log_level to log, or @ref DRGN_LOG_NONE to + * disable logging. + */ +void drgn_program_set_log_level(struct drgn_program *prog, int level); + +/** + * Get the minimum log level. + * + * @return Minimum @ref drgn_log_level being logged, or @ref DRGN_LOG_NONE if + * logging is disabled. + */ +int drgn_program_get_log_level(struct drgn_program *prog); + +/** Write logs to the given file. */ +void drgn_program_set_log_file(struct drgn_program *prog, FILE *file); + +/** + * Log callback. + * + * @param[in] prog Program message was logged to. + * @param[in] arg `callback_arg` passed to @ref drgn_program_set_log_callback(). + * @param[in] level Message level. + * @param[in] format printf-style format of message. + * @param[in] ap Arguments for @p format. + * @param[in] err Error to append after formatted message if non-@c NULL. This + * can be formatted with @ref drgn_error_string(), @ref drgn_error_fwrite(), or + * @ref drgn_error_dwrite(). + */ +typedef void drgn_log_fn(struct drgn_program *prog, void *arg, + enum drgn_log_level level, const char *format, + va_list ap, struct drgn_error *err); + +/** + * Set a callback to log to. + * + * @param[in] callback Callback to call for each log message. This is only + * called if the message's level is at least the current log level. + * @param[in] callback_arg Argument to pass to callback. + */ +void drgn_program_set_log_callback(struct drgn_program *prog, + drgn_log_fn *callback, void *callback_arg); + +/** + * Get the current log callback. + * + * @param[out] callback_ret Returned callback. + * @param[out] callback_arg_ret Returned callback argument. + */ +void drgn_program_get_log_callback(struct drgn_program *prog, + drgn_log_fn **callback_ret, + void **callback_arg_ret); + +/** @} */ + +/** + * @defgroup Embedding Embedding + * + * Embedding drgn in another runtime. + * + * @{ + */ + +/** + * Callback before a blocking operation. + * + * @param[in] arg @c callback_arg passed to @ref + * drgn_program_set_blocking_callback(). + * @return Opaque pointer to pass to @ref drgn_program_end_blocking_fn(). + */ +typedef void *drgn_program_begin_blocking_fn(struct drgn_program *prog, + void *arg); + +/** + * Callback after a blocking operation. + * + * @param[in] arg @c callback_arg passed to @ref + * drgn_program_set_blocking_callback(). + * @param[in] state Return value of @ref drgn_program_begin_blocking_fn(). + */ +typedef void drgn_program_end_blocking_fn(struct drgn_program *prog, + void *arg, void *state); + +/** + * Set callbacks around blocking operations. + * + * These callbacks will be called around blocking I/O operations and + * long-running computations. They are intended for things like releasing the + * [global interpreter + * lock](https://docs.python.org/3/glossary.html#term-global-interpreter-lock). + * + * @param[in] begin_callback Callback called before a blocking operation. Can be + * @c NULL to unset. + * @param[in] end_callback Callback called after a blocking operation. Can be @c + * NULL to unset. + * @param[in] callback_arg Argument passed to @p begin_callback and @p + * end_callback. + */ +void +drgn_program_set_blocking_callback(struct drgn_program *prog, + drgn_program_begin_blocking_fn *begin_callback, + drgn_program_end_blocking_fn *end_callback, + void *callback_arg); + +/** + * Get callbacks set by @ref drgn_program_set_blocking_callback(). + * + * @param[out] begin_callback_ret Returned @c begin_callback. + * @param[out] end_callback_ret Returned @c end_callback. + * @param[out] callback_arg_ret Returned @c callback_arg. + */ +void +drgn_program_get_blocking_callback(struct drgn_program *prog, + drgn_program_begin_blocking_fn **begin_callback_ret, + drgn_program_end_blocking_fn **end_callback_ret, + void **callback_arg_ret); + +/** @} */ + /** * @defgroup Objects Objects * @@ -968,24 +1140,37 @@ enum drgn_object_encoding { * Memory buffer. * * This is used for objects with a structure, union, class, or array - * type. The value is a buffer of the contents of that object's memory - * in the program. + * type. */ DRGN_OBJECT_ENCODING_BUFFER, /** * Signed integer. * * This is used for objects with a signed integer or signed enumerated - * type. + * type no larger than 64 bits. */ DRGN_OBJECT_ENCODING_SIGNED, /** * Unsigned integer. * * This is used for objects with a unsigned integer, boolean, or pointer - * type. + * type no larger than 64 bits. */ DRGN_OBJECT_ENCODING_UNSIGNED, + /** + * Big signed integer. + * + * This is used for objects with a signed integer or signed enumerated + * type larger than 64 bits. + */ + DRGN_OBJECT_ENCODING_SIGNED_BIG, + /** + * Big unsigned integer. + * + * This is used for objects with a unsigned integer, boolean, or pointer + * type larger than 64 bits. + */ + DRGN_OBJECT_ENCODING_UNSIGNED_BIG, /** * Floating-point value. * @@ -1028,23 +1213,30 @@ drgn_object_encoding_is_complete(enum drgn_object_encoding encoding) /** Value of a @ref drgn_object. */ union drgn_value { /** - * Pointer to an external buffer for a @ref - * drgn_object_encoding::DRGN_OBJECT_ENCODING_BUFFER value. + * Pointer to an external buffer for a @ref DRGN_OBJECT_ENCODING_BUFFER, + * @ref DRGN_OBJECT_ENCODING_SIGNED_BIG, or @ref + * DRGN_OBJECT_ENCODING_UNSIGNED_BIG value. + * + * For @ref DRGN_OBJECT_ENCODING_BUFFER, this contains the object's + * representation in the memory of the program. + * + * For @ref DRGN_OBJECT_ENCODING_SIGNED_BIG and @ref + * DRGN_OBJECT_ENCODING_UNSIGNED_BIG, the representation of the value is + * an implementation detail which may change. */ char *bufp; /** - * Inline buffer for a @ref - * drgn_object_encoding::DRGN_OBJECT_ENCODING_BUFFER value. + * Inline buffer for a @ref DRGN_OBJECT_ENCODING_BUFFER value. * * Tiny buffers (see @ref drgn_value_is_inline()) are stored inline here * instead of in a separate allocation. */ char ibuf[8]; - /** @ref drgn_object_encoding::DRGN_OBJECT_ENCODING_SIGNED value. */ + /** @ref DRGN_OBJECT_ENCODING_SIGNED value. */ int64_t svalue; - /** @ref drgn_object_encoding::DRGN_OBJECT_ENCODING_UNSIGNED value. */ + /** @ref DRGN_OBJECT_ENCODING_UNSIGNED value. */ uint64_t uvalue; - /** @ref drgn_object_encoding::DRGN_OBJECT_ENCODING_FLOAT value. */ + /** @ref DRGN_OBJECT_ENCODING_FLOAT value. */ double fvalue; }; @@ -1107,11 +1299,11 @@ struct drgn_object { /** * Whether this object is little-endian. * - * Valid only for scalars (i.e., @ref - * drgn_object_encoding::DRGN_OBJECT_ENCODING_SIGNED, @ref - * drgn_object_encoding::DRGN_OBJECT_ENCODING_UNSIGNED, @ref - * drgn_object_encoding::DRGN_OBJECT_ENCODING_FLOAT, or @ref - * drgn_object_encoding::DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER). + * Valid only for scalars (i.e., @ref DRGN_OBJECT_ENCODING_SIGNED, @ref + * DRGN_OBJECT_ENCODING_UNSIGNED, @ref DRGN_OBJECT_ENCODING_SIGNED_BIG, + * @ref DRGN_OBJECT_ENCODING_UNSIGNED_BIG, @ref + * DRGN_OBJECT_ENCODING_FLOAT, or @ref + * DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER). */ bool little_endian; /** @@ -2709,7 +2901,7 @@ bool drgn_symbol_eq(struct drgn_symbol *a, struct drgn_symbol *b); * @{ */ -struct drgn_stack_trace; +struct drgn_stack_trace; // IWYU pragma: export /** Destroy a @ref drgn_stack_trace. */ void drgn_stack_trace_destroy(struct drgn_stack_trace *trace); @@ -2778,6 +2970,15 @@ const char *drgn_stack_frame_source(struct drgn_stack_trace *trace, bool drgn_stack_frame_pc(struct drgn_stack_trace *trace, size_t frame, uint64_t *ret); +/** + * Get the stack pointer at a stack frame. + * + * @param[out] ret Returned stack pointer. + * @return @c true if the stack pointer is known, @c false if it is not. + */ +bool drgn_stack_frame_sp(struct drgn_stack_trace *trace, size_t frame, + uint64_t *ret); + /** * Get the function symbol at a stack frame. * @@ -2789,6 +2990,31 @@ struct drgn_error *drgn_stack_frame_symbol(struct drgn_stack_trace *trace, size_t frame, struct drgn_symbol **ret); +/** + * Get the names of local objects in the scope of this frame. + * + * The array of names must be freed with @ref drgn_stack_frame_locals_destroy(). + * + * @param[out] names_ret Returned array of names. On success, must be freed with + * @ref drgn_stack_frame_locals_destroy(). + * @param[out] count_ret Returned number of names in @p names_ret. + * @return @c NULL on success, non-@c NULL on error + */ +struct drgn_error * +drgn_stack_frame_locals(struct drgn_stack_trace *trace, size_t frame, + const char ***names_ret, size_t *count_ret); + +/** + * Free an array of names returned by @ref drgn_stack_frame_locals(). + * + * The individual names from this array are invalid once this function is + * called. Any string which will be used later should be copied. + * + * @param names Array of names returned by @ref drgn_stack_frame_locals(). + * @param count Count returned by @ref drgn_stack_frame_locals(). + */ +void drgn_stack_frame_locals_destroy(const char **names, size_t count); + /** * Find an object in the scope of a stack frame. * diff --git a/libdrgn/drgn_program_parse_vmcoreinfo.inc.strswitch b/libdrgn/drgn_program_parse_vmcoreinfo.inc.strswitch index 0efc34453..146599455 100644 --- a/libdrgn/drgn_program_parse_vmcoreinfo.inc.strswitch +++ b/libdrgn/drgn_program_parse_vmcoreinfo.inc.strswitch @@ -1,3 +1,6 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// SPDX-License-Identifier: LGPL-2.1-or-later + static struct drgn_error *parse_vmcoreinfo_u64(const char *value, const char *newline, int base, uint64_t *ret) @@ -20,6 +23,11 @@ struct drgn_error *drgn_program_parse_vmcoreinfo(struct drgn_program *prog, size_t descsz) { struct drgn_error *err; + + prog->vmcoreinfo.raw_size = descsz; + prog->vmcoreinfo.raw = memdup(desc, descsz); + if (!prog->vmcoreinfo.raw) + return &drgn_enomem; for (const char *line = desc, *end = &desc[descsz], *newline; (newline = memchr(line, '\n', end - line)); line = newline + 1) { diff --git a/libdrgn/dwarf_constants.c b/libdrgn/dwarf_constants.c new file mode 100644 index 000000000..797bfc262 --- /dev/null +++ b/libdrgn/dwarf_constants.c @@ -0,0 +1,21 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// SPDX-License-Identifier: LGPL-2.1-or-later +// Generated by scripts/gen_dwarf_constants.py. + +#include + +#include "dwarf_constants.h" + +#define X(name, value) case name: return #name; + +const char *dw_tag_str(int value, char buf[static DW_TAG_STR_BUF_LEN]) +{ + switch (value) { + DW_TAG_DEFINITIONS + default: + snprintf(buf, DW_TAG_STR_BUF_LEN, DW_TAG_STR_UNKNOWN_FORMAT, value); + return buf; + } +} + +#undef X diff --git a/libdrgn/dwarf_constants.h b/libdrgn/dwarf_constants.h new file mode 100644 index 000000000..8e79f4101 --- /dev/null +++ b/libdrgn/dwarf_constants.h @@ -0,0 +1,1134 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// SPDX-License-Identifier: LGPL-2.1-or-later +// Generated by scripts/gen_dwarf_constants.py. + +/** + * @file + * + * DWARF constant definitions. + * + * This file defines the following for each known DWARF constant type: + * + * 1. An X macro defining all of the known names and values of the type: + * `DW_FOO_DEFINITIONS`. + * 2. Enumerators defining the constants: `DW_FOO_a`, `DW_FOO_b`, etc. + * 3. For select types, a function to translate a value to its name: + * `dw_foo_str()`. + */ + +#ifndef DWARF_CONSTANTS_H +#define DWARF_CONSTANTS_H + +#define X(name, value) name = value, + +#define DW_ACCESS_DEFINITIONS \ + X(DW_ACCESS_public, 0x1) \ + X(DW_ACCESS_protected, 0x2) \ + X(DW_ACCESS_private, 0x3) +enum { DW_ACCESS_DEFINITIONS }; + +#define DW_ADDR_DEFINITIONS \ + X(DW_ADDR_none, 0x0) +enum { DW_ADDR_DEFINITIONS }; + +#define DW_AT_DEFINITIONS \ + X(DW_AT_sibling, 0x1) \ + X(DW_AT_location, 0x2) \ + X(DW_AT_name, 0x3) \ + X(DW_AT_ordering, 0x9) \ + X(DW_AT_subscr_data, 0xa) \ + X(DW_AT_byte_size, 0xb) \ + X(DW_AT_bit_offset, 0xc) \ + X(DW_AT_bit_size, 0xd) \ + X(DW_AT_element_list, 0xf) \ + X(DW_AT_stmt_list, 0x10) \ + X(DW_AT_low_pc, 0x11) \ + X(DW_AT_high_pc, 0x12) \ + X(DW_AT_language, 0x13) \ + X(DW_AT_member, 0x14) \ + X(DW_AT_discr, 0x15) \ + X(DW_AT_discr_value, 0x16) \ + X(DW_AT_visibility, 0x17) \ + X(DW_AT_import, 0x18) \ + X(DW_AT_string_length, 0x19) \ + X(DW_AT_common_reference, 0x1a) \ + X(DW_AT_comp_dir, 0x1b) \ + X(DW_AT_const_value, 0x1c) \ + X(DW_AT_containing_type, 0x1d) \ + X(DW_AT_default_value, 0x1e) \ + X(DW_AT_inline, 0x20) \ + X(DW_AT_is_optional, 0x21) \ + X(DW_AT_lower_bound, 0x22) \ + X(DW_AT_producer, 0x25) \ + X(DW_AT_prototyped, 0x27) \ + X(DW_AT_return_addr, 0x2a) \ + X(DW_AT_start_scope, 0x2c) \ + X(DW_AT_bit_stride, 0x2e) \ + X(DW_AT_stride_size, 0x2e) \ + X(DW_AT_upper_bound, 0x2f) \ + X(DW_AT_abstract_origin, 0x31) \ + X(DW_AT_accessibility, 0x32) \ + X(DW_AT_address_class, 0x33) \ + X(DW_AT_artificial, 0x34) \ + X(DW_AT_base_types, 0x35) \ + X(DW_AT_calling_convention, 0x36) \ + X(DW_AT_count, 0x37) \ + X(DW_AT_data_member_location, 0x38) \ + X(DW_AT_decl_column, 0x39) \ + X(DW_AT_decl_file, 0x3a) \ + X(DW_AT_decl_line, 0x3b) \ + X(DW_AT_declaration, 0x3c) \ + X(DW_AT_discr_list, 0x3d) \ + X(DW_AT_encoding, 0x3e) \ + X(DW_AT_external, 0x3f) \ + X(DW_AT_frame_base, 0x40) \ + X(DW_AT_friend, 0x41) \ + X(DW_AT_identifier_case, 0x42) \ + X(DW_AT_macro_info, 0x43) \ + X(DW_AT_namelist_item, 0x44) \ + X(DW_AT_priority, 0x45) \ + X(DW_AT_segment, 0x46) \ + X(DW_AT_specification, 0x47) \ + X(DW_AT_static_link, 0x48) \ + X(DW_AT_type, 0x49) \ + X(DW_AT_use_location, 0x4a) \ + X(DW_AT_variable_parameter, 0x4b) \ + X(DW_AT_virtuality, 0x4c) \ + X(DW_AT_vtable_elem_location, 0x4d) \ + X(DW_AT_allocated, 0x4e) \ + X(DW_AT_associated, 0x4f) \ + X(DW_AT_data_location, 0x50) \ + X(DW_AT_byte_stride, 0x51) \ + X(DW_AT_entry_pc, 0x52) \ + X(DW_AT_use_UTF8, 0x53) \ + X(DW_AT_extension, 0x54) \ + X(DW_AT_ranges, 0x55) \ + X(DW_AT_trampoline, 0x56) \ + X(DW_AT_call_column, 0x57) \ + X(DW_AT_call_file, 0x58) \ + X(DW_AT_call_line, 0x59) \ + X(DW_AT_description, 0x5a) \ + X(DW_AT_binary_scale, 0x5b) \ + X(DW_AT_decimal_scale, 0x5c) \ + X(DW_AT_small, 0x5d) \ + X(DW_AT_decimal_sign, 0x5e) \ + X(DW_AT_digit_count, 0x5f) \ + X(DW_AT_picture_string, 0x60) \ + X(DW_AT_mutable, 0x61) \ + X(DW_AT_threads_scaled, 0x62) \ + X(DW_AT_explicit, 0x63) \ + X(DW_AT_object_pointer, 0x64) \ + X(DW_AT_endianity, 0x65) \ + X(DW_AT_elemental, 0x66) \ + X(DW_AT_pure, 0x67) \ + X(DW_AT_recursive, 0x68) \ + X(DW_AT_signature, 0x69) \ + X(DW_AT_main_subprogram, 0x6a) \ + X(DW_AT_data_bit_offset, 0x6b) \ + X(DW_AT_const_expr, 0x6c) \ + X(DW_AT_enum_class, 0x6d) \ + X(DW_AT_linkage_name, 0x6e) \ + X(DW_AT_string_length_bit_size, 0x6f) \ + X(DW_AT_string_length_byte_size, 0x70) \ + X(DW_AT_rank, 0x71) \ + X(DW_AT_str_offsets_base, 0x72) \ + X(DW_AT_addr_base, 0x73) \ + X(DW_AT_rnglists_base, 0x74) \ + X(DW_AT_dwo_id, 0x75) \ + X(DW_AT_dwo_name, 0x76) \ + X(DW_AT_reference, 0x77) \ + X(DW_AT_rvalue_reference, 0x78) \ + X(DW_AT_macros, 0x79) \ + X(DW_AT_call_all_calls, 0x7a) \ + X(DW_AT_call_all_source_calls, 0x7b) \ + X(DW_AT_call_all_tail_calls, 0x7c) \ + X(DW_AT_call_return_pc, 0x7d) \ + X(DW_AT_call_value, 0x7e) \ + X(DW_AT_call_origin, 0x7f) \ + X(DW_AT_call_parameter, 0x80) \ + X(DW_AT_call_pc, 0x81) \ + X(DW_AT_call_tail_call, 0x82) \ + X(DW_AT_call_target, 0x83) \ + X(DW_AT_call_target_clobbered, 0x84) \ + X(DW_AT_call_data_location, 0x85) \ + X(DW_AT_call_data_value, 0x86) \ + X(DW_AT_noreturn, 0x87) \ + X(DW_AT_alignment, 0x88) \ + X(DW_AT_export_symbols, 0x89) \ + X(DW_AT_deleted, 0x8a) \ + X(DW_AT_defaulted, 0x8b) \ + X(DW_AT_loclists_base, 0x8c) \ + X(DW_AT_ghs_namespace_alias, 0x806) \ + X(DW_AT_ghs_using_namespace, 0x807) \ + X(DW_AT_ghs_using_declaration, 0x808) \ + X(DW_AT_HP_block_index, 0x2000) \ + X(DW_AT_lo_user, 0x2000) \ + X(DW_AT_MIPS_fde, 0x2001) \ + X(DW_AT_MIPS_loop_begin, 0x2002) \ + X(DW_AT_MIPS_tail_loop_begin, 0x2003) \ + X(DW_AT_MIPS_epilog_begin, 0x2004) \ + X(DW_AT_MIPS_loop_unroll_factor, 0x2005) \ + X(DW_AT_MIPS_software_pipeline_depth, 0x2006) \ + X(DW_AT_MIPS_linkage_name, 0x2007) \ + X(DW_AT_MIPS_stride, 0x2008) \ + X(DW_AT_MIPS_abstract_name, 0x2009) \ + X(DW_AT_MIPS_clone_origin, 0x200a) \ + X(DW_AT_MIPS_has_inlines, 0x200b) \ + X(DW_AT_MIPS_stride_byte, 0x200c) \ + X(DW_AT_MIPS_stride_elem, 0x200d) \ + X(DW_AT_MIPS_ptr_dopetype, 0x200e) \ + X(DW_AT_MIPS_allocatable_dopetype, 0x200f) \ + X(DW_AT_MIPS_assumed_shape_dopetype, 0x2010) \ + X(DW_AT_MIPS_assumed_size, 0x2011) \ + X(DW_AT_HP_unmodifiable, 0x2001) \ + X(DW_AT_HP_prologue, 0x2005) \ + X(DW_AT_HP_epilogue, 0x2008) \ + X(DW_AT_HP_actuals_stmt_list, 0x2010) \ + X(DW_AT_HP_proc_per_section, 0x2011) \ + X(DW_AT_HP_raw_data_ptr, 0x2012) \ + X(DW_AT_HP_pass_by_reference, 0x2013) \ + X(DW_AT_HP_opt_level, 0x2014) \ + X(DW_AT_HP_prof_version_id, 0x2015) \ + X(DW_AT_HP_opt_flags, 0x2016) \ + X(DW_AT_HP_cold_region_low_pc, 0x2017) \ + X(DW_AT_HP_cold_region_high_pc, 0x2018) \ + X(DW_AT_HP_all_variables_modifiable, 0x2019) \ + X(DW_AT_HP_linkage_name, 0x201a) \ + X(DW_AT_HP_prof_flags, 0x201b) \ + X(DW_AT_HP_unit_name, 0x201f) \ + X(DW_AT_HP_unit_size, 0x2020) \ + X(DW_AT_HP_widened_byte_size, 0x2021) \ + X(DW_AT_HP_definition_points, 0x2022) \ + X(DW_AT_HP_default_location, 0x2023) \ + X(DW_AT_HP_is_result_param, 0x2029) \ + X(DW_AT_CPQ_discontig_ranges, 0x2001) \ + X(DW_AT_CPQ_semantic_events, 0x2002) \ + X(DW_AT_CPQ_split_lifetimes_var, 0x2003) \ + X(DW_AT_CPQ_split_lifetimes_rtn, 0x2004) \ + X(DW_AT_CPQ_prologue_length, 0x2005) \ + X(DW_AT_ghs_mangled, 0x2007) \ + X(DW_AT_ghs_rsm, 0x2083) \ + X(DW_AT_ghs_frsm, 0x2085) \ + X(DW_AT_ghs_frames, 0x2086) \ + X(DW_AT_ghs_rso, 0x2087) \ + X(DW_AT_ghs_subcpu, 0x2092) \ + X(DW_AT_ghs_lbrace_line, 0x2093) \ + X(DW_AT_INTEL_other_endian, 0x2026) \ + X(DW_AT_sf_names, 0x2101) \ + X(DW_AT_src_info, 0x2102) \ + X(DW_AT_mac_info, 0x2103) \ + X(DW_AT_src_coords, 0x2104) \ + X(DW_AT_body_begin, 0x2105) \ + X(DW_AT_body_end, 0x2106) \ + X(DW_AT_GNU_vector, 0x2107) \ + X(DW_AT_GNU_guarded_by, 0x2108) \ + X(DW_AT_GNU_pt_guarded_by, 0x2109) \ + X(DW_AT_GNU_guarded, 0x210a) \ + X(DW_AT_GNU_pt_guarded, 0x210b) \ + X(DW_AT_GNU_locks_excluded, 0x210c) \ + X(DW_AT_GNU_exclusive_locks_required, 0x210d) \ + X(DW_AT_GNU_shared_locks_required, 0x210e) \ + X(DW_AT_GNU_odr_signature, 0x210f) \ + X(DW_AT_GNU_template_name, 0x2110) \ + X(DW_AT_GNU_call_site_value, 0x2111) \ + X(DW_AT_GNU_call_site_data_value, 0x2112) \ + X(DW_AT_GNU_call_site_target, 0x2113) \ + X(DW_AT_GNU_call_site_target_clobbered, 0x2114) \ + X(DW_AT_GNU_tail_call, 0x2115) \ + X(DW_AT_GNU_all_tail_call_sites, 0x2116) \ + X(DW_AT_GNU_all_call_sites, 0x2117) \ + X(DW_AT_GNU_all_source_call_sites, 0x2118) \ + X(DW_AT_GNU_macros, 0x2119) \ + X(DW_AT_GNU_deleted, 0x211a) \ + X(DW_AT_GNU_dwo_name, 0x2130) \ + X(DW_AT_GNU_dwo_id, 0x2131) \ + X(DW_AT_GNU_ranges_base, 0x2132) \ + X(DW_AT_GNU_addr_base, 0x2133) \ + X(DW_AT_GNU_pubnames, 0x2134) \ + X(DW_AT_GNU_pubtypes, 0x2135) \ + X(DW_AT_GNU_discriminator, 0x2136) \ + X(DW_AT_GNU_locviews, 0x2137) \ + X(DW_AT_GNU_entry_view, 0x2138) \ + X(DW_AT_GNU_bias, 0x2305) \ + X(DW_AT_SUN_template, 0x2201) \ + X(DW_AT_VMS_rtnbeg_pd_address, 0x2201) \ + X(DW_AT_SUN_alignment, 0x2202) \ + X(DW_AT_SUN_vtable, 0x2203) \ + X(DW_AT_SUN_count_guarantee, 0x2204) \ + X(DW_AT_SUN_command_line, 0x2205) \ + X(DW_AT_SUN_vbase, 0x2206) \ + X(DW_AT_SUN_compile_options, 0x2207) \ + X(DW_AT_SUN_language, 0x2208) \ + X(DW_AT_SUN_browser_file, 0x2209) \ + X(DW_AT_SUN_vtable_abi, 0x2210) \ + X(DW_AT_SUN_func_offsets, 0x2211) \ + X(DW_AT_SUN_cf_kind, 0x2212) \ + X(DW_AT_SUN_vtable_index, 0x2213) \ + X(DW_AT_SUN_omp_tpriv_addr, 0x2214) \ + X(DW_AT_SUN_omp_child_func, 0x2215) \ + X(DW_AT_SUN_func_offset, 0x2216) \ + X(DW_AT_SUN_memop_type_ref, 0x2217) \ + X(DW_AT_SUN_profile_id, 0x2218) \ + X(DW_AT_SUN_memop_signature, 0x2219) \ + X(DW_AT_SUN_obj_dir, 0x2220) \ + X(DW_AT_SUN_obj_file, 0x2221) \ + X(DW_AT_SUN_original_name, 0x2222) \ + X(DW_AT_SUN_hwcprof_signature, 0x2223) \ + X(DW_AT_SUN_amd64_parmdump, 0x2224) \ + X(DW_AT_SUN_part_link_name, 0x2225) \ + X(DW_AT_SUN_link_name, 0x2226) \ + X(DW_AT_SUN_pass_with_const, 0x2227) \ + X(DW_AT_SUN_return_with_const, 0x2228) \ + X(DW_AT_SUN_import_by_name, 0x2229) \ + X(DW_AT_SUN_f90_pointer, 0x222a) \ + X(DW_AT_SUN_pass_by_ref, 0x222b) \ + X(DW_AT_SUN_f90_allocatable, 0x222c) \ + X(DW_AT_SUN_f90_assumed_shape_array, 0x222d) \ + X(DW_AT_SUN_c_vla, 0x222e) \ + X(DW_AT_SUN_return_value_ptr, 0x2230) \ + X(DW_AT_SUN_dtor_start, 0x2231) \ + X(DW_AT_SUN_dtor_length, 0x2232) \ + X(DW_AT_SUN_dtor_state_initial, 0x2233) \ + X(DW_AT_SUN_dtor_state_final, 0x2234) \ + X(DW_AT_SUN_dtor_state_deltas, 0x2235) \ + X(DW_AT_SUN_import_by_lname, 0x2236) \ + X(DW_AT_SUN_f90_use_only, 0x2237) \ + X(DW_AT_SUN_namelist_spec, 0x2238) \ + X(DW_AT_SUN_is_omp_child_func, 0x2239) \ + X(DW_AT_SUN_fortran_main_alias, 0x223a) \ + X(DW_AT_SUN_fortran_based, 0x223b) \ + X(DW_AT_ALTIUM_loclist, 0x2300) \ + X(DW_AT_use_GNAT_descriptive_type, 0x2301) \ + X(DW_AT_GNAT_descriptive_type, 0x2302) \ + X(DW_AT_GNU_numerator, 0x2303) \ + X(DW_AT_GNU_denominator, 0x2304) \ + X(DW_AT_go_kind, 0x2900) \ + X(DW_AT_go_key, 0x2901) \ + X(DW_AT_go_elem, 0x2902) \ + X(DW_AT_go_embedded_field, 0x2903) \ + X(DW_AT_go_runtime_type, 0x2904) \ + X(DW_AT_upc_threads_scaled, 0x3210) \ + X(DW_AT_IBM_wsa_addr, 0x393e) \ + X(DW_AT_IBM_home_location, 0x393f) \ + X(DW_AT_IBM_alt_srcview, 0x3940) \ + X(DW_AT_PGI_lbase, 0x3a00) \ + X(DW_AT_PGI_soffset, 0x3a01) \ + X(DW_AT_PGI_lstride, 0x3a02) \ + X(DW_AT_BORLAND_property_read, 0x3b11) \ + X(DW_AT_BORLAND_property_write, 0x3b12) \ + X(DW_AT_BORLAND_property_implements, 0x3b13) \ + X(DW_AT_BORLAND_property_index, 0x3b14) \ + X(DW_AT_BORLAND_property_default, 0x3b15) \ + X(DW_AT_BORLAND_Delphi_unit, 0x3b20) \ + X(DW_AT_BORLAND_Delphi_class, 0x3b21) \ + X(DW_AT_BORLAND_Delphi_record, 0x3b22) \ + X(DW_AT_BORLAND_Delphi_metaclass, 0x3b23) \ + X(DW_AT_BORLAND_Delphi_constructor, 0x3b24) \ + X(DW_AT_BORLAND_Delphi_destructor, 0x3b25) \ + X(DW_AT_BORLAND_Delphi_anonymous_method, 0x3b26) \ + X(DW_AT_BORLAND_Delphi_interface, 0x3b27) \ + X(DW_AT_BORLAND_Delphi_ABI, 0x3b28) \ + X(DW_AT_BORLAND_Delphi_frameptr, 0x3b30) \ + X(DW_AT_BORLAND_closure, 0x3b31) \ + X(DW_AT_LLVM_include_path, 0x3e00) \ + X(DW_AT_LLVM_config_macros, 0x3e01) \ + X(DW_AT_LLVM_sysroot, 0x3e02) \ + X(DW_AT_LLVM_tag_offset, 0x3e03) \ + X(DW_AT_LLVM_apinotes, 0x3e07) \ + X(DW_AT_LLVM_active_lane, 0x3e08) \ + X(DW_AT_LLVM_augmentation, 0x3e09) \ + X(DW_AT_LLVM_lanes, 0x3e0a) \ + X(DW_AT_LLVM_lane_pc, 0x3e0b) \ + X(DW_AT_LLVM_vector_size, 0x3e0c) \ + X(DW_AT_APPLE_optimized, 0x3fe1) \ + X(DW_AT_APPLE_flags, 0x3fe2) \ + X(DW_AT_APPLE_isa, 0x3fe3) \ + X(DW_AT_APPLE_block, 0x3fe4) \ + X(DW_AT_APPLE_major_runtime_vers, 0x3fe5) \ + X(DW_AT_APPLE_runtime_class, 0x3fe6) \ + X(DW_AT_APPLE_omit_frame_ptr, 0x3fe7) \ + X(DW_AT_APPLE_property_name, 0x3fe8) \ + X(DW_AT_APPLE_property_getter, 0x3fe9) \ + X(DW_AT_APPLE_property_setter, 0x3fea) \ + X(DW_AT_APPLE_property_attribute, 0x3feb) \ + X(DW_AT_APPLE_objc_complete_type, 0x3fec) \ + X(DW_AT_APPLE_property, 0x3fed) \ + X(DW_AT_APPLE_objc_direct, 0x3fee) \ + X(DW_AT_APPLE_sdk, 0x3fef) \ + X(DW_AT_hi_user, 0x3fff) +enum { DW_AT_DEFINITIONS }; + +#define DW_ATE_DEFINITIONS \ + X(DW_ATE_address, 0x1) \ + X(DW_ATE_boolean, 0x2) \ + X(DW_ATE_complex_float, 0x3) \ + X(DW_ATE_float, 0x4) \ + X(DW_ATE_signed, 0x5) \ + X(DW_ATE_signed_char, 0x6) \ + X(DW_ATE_unsigned, 0x7) \ + X(DW_ATE_unsigned_char, 0x8) \ + X(DW_ATE_imaginary_float, 0x9) \ + X(DW_ATE_packed_decimal, 0xa) \ + X(DW_ATE_numeric_string, 0xb) \ + X(DW_ATE_edited, 0xc) \ + X(DW_ATE_signed_fixed, 0xd) \ + X(DW_ATE_unsigned_fixed, 0xe) \ + X(DW_ATE_decimal_float, 0xf) \ + X(DW_ATE_UTF, 0x10) \ + X(DW_ATE_UCS, 0x11) \ + X(DW_ATE_ASCII, 0x12) \ + X(DW_ATE_ALTIUM_fract, 0x80) \ + X(DW_ATE_lo_user, 0x80) \ + X(DW_ATE_ALTIUM_accum, 0x81) \ + X(DW_ATE_HP_float80, 0x80) \ + X(DW_ATE_HP_complex_float80, 0x81) \ + X(DW_ATE_HP_float128, 0x82) \ + X(DW_ATE_HP_complex_float128, 0x83) \ + X(DW_ATE_HP_floathpintel, 0x84) \ + X(DW_ATE_HP_imaginary_float80, 0x85) \ + X(DW_ATE_HP_imaginary_float128, 0x86) \ + X(DW_ATE_HP_VAX_float, 0x88) \ + X(DW_ATE_HP_VAX_float_d, 0x89) \ + X(DW_ATE_HP_packed_decimal, 0x8a) \ + X(DW_ATE_HP_zoned_decimal, 0x8b) \ + X(DW_ATE_HP_edited, 0x8c) \ + X(DW_ATE_HP_signed_fixed, 0x8d) \ + X(DW_ATE_HP_unsigned_fixed, 0x8e) \ + X(DW_ATE_HP_VAX_complex_float, 0x8f) \ + X(DW_ATE_HP_VAX_complex_float_d, 0x90) \ + X(DW_ATE_SUN_interval_float, 0x91) \ + X(DW_ATE_SUN_imaginary_float, 0x92) \ + X(DW_ATE_hi_user, 0xff) +enum { DW_ATE_DEFINITIONS }; + +#define DW_CC_DEFINITIONS \ + X(DW_CC_normal, 0x1) \ + X(DW_CC_program, 0x2) \ + X(DW_CC_nocall, 0x3) \ + X(DW_CC_pass_by_reference, 0x4) \ + X(DW_CC_pass_by_value, 0x5) \ + X(DW_CC_lo_user, 0x40) \ + X(DW_CC_GNU_renesas_sh, 0x40) \ + X(DW_CC_GNU_borland_fastcall_i386, 0x41) \ + X(DW_CC_ALTIUM_interrupt, 0x65) \ + X(DW_CC_ALTIUM_near_system_stack, 0x66) \ + X(DW_CC_ALTIUM_near_user_stack, 0x67) \ + X(DW_CC_ALTIUM_huge_user_stack, 0x68) \ + X(DW_CC_GNU_BORLAND_safecall, 0xb0) \ + X(DW_CC_GNU_BORLAND_stdcall, 0xb1) \ + X(DW_CC_GNU_BORLAND_pascal, 0xb2) \ + X(DW_CC_GNU_BORLAND_msfastcall, 0xb3) \ + X(DW_CC_GNU_BORLAND_msreturn, 0xb4) \ + X(DW_CC_GNU_BORLAND_thiscall, 0xb5) \ + X(DW_CC_GNU_BORLAND_fastcall, 0xb6) \ + X(DW_CC_LLVM_vectorcall, 0xc0) \ + X(DW_CC_LLVM_Win64, 0xc1) \ + X(DW_CC_LLVM_X86_64SysV, 0xc2) \ + X(DW_CC_LLVM_AAPCS, 0xc3) \ + X(DW_CC_LLVM_AAPCS_VFP, 0xc4) \ + X(DW_CC_LLVM_IntelOclBicc, 0xc5) \ + X(DW_CC_LLVM_SpirFunction, 0xc6) \ + X(DW_CC_LLVM_OpenCLKernel, 0xc7) \ + X(DW_CC_LLVM_Swift, 0xc8) \ + X(DW_CC_LLVM_PreserveMost, 0xc9) \ + X(DW_CC_LLVM_PreserveAll, 0xca) \ + X(DW_CC_LLVM_X86RegCall, 0xcb) \ + X(DW_CC_GDB_IBM_OpenCL, 0xff) \ + X(DW_CC_hi_user, 0xff) +enum { DW_CC_DEFINITIONS }; + +#define DW_CFA_DEFINITIONS \ + X(DW_CFA_advance_loc, 0x40) \ + X(DW_CFA_offset, 0x80) \ + X(DW_CFA_restore, 0xc0) \ + X(DW_CFA_nop, 0x0) \ + X(DW_CFA_set_loc, 0x1) \ + X(DW_CFA_advance_loc1, 0x2) \ + X(DW_CFA_advance_loc2, 0x3) \ + X(DW_CFA_advance_loc4, 0x4) \ + X(DW_CFA_offset_extended, 0x5) \ + X(DW_CFA_restore_extended, 0x6) \ + X(DW_CFA_undefined, 0x7) \ + X(DW_CFA_same_value, 0x8) \ + X(DW_CFA_register, 0x9) \ + X(DW_CFA_remember_state, 0xa) \ + X(DW_CFA_restore_state, 0xb) \ + X(DW_CFA_def_cfa, 0xc) \ + X(DW_CFA_def_cfa_register, 0xd) \ + X(DW_CFA_def_cfa_offset, 0xe) \ + X(DW_CFA_def_cfa_expression, 0xf) \ + X(DW_CFA_expression, 0x10) \ + X(DW_CFA_offset_extended_sf, 0x11) \ + X(DW_CFA_def_cfa_sf, 0x12) \ + X(DW_CFA_def_cfa_offset_sf, 0x13) \ + X(DW_CFA_val_offset, 0x14) \ + X(DW_CFA_val_offset_sf, 0x15) \ + X(DW_CFA_val_expression, 0x16) \ + X(DW_CFA_lo_user, 0x1c) \ + X(DW_CFA_MIPS_advance_loc8, 0x1d) \ + X(DW_CFA_GNU_window_save, 0x2d) \ + X(DW_CFA_AARCH64_negate_ra_state, 0x2d) \ + X(DW_CFA_GNU_args_size, 0x2e) \ + X(DW_CFA_GNU_negative_offset_extended, 0x2f) \ + X(DW_CFA_LLVM_def_aspace_cfa, 0x30) \ + X(DW_CFA_LLVM_def_aspace_cfa_sf, 0x31) \ + X(DW_CFA_METAWARE_info, 0x34) \ + X(DW_CFA_hi_user, 0x3f) +enum { DW_CFA_DEFINITIONS }; + +#define DW_CHILDREN_DEFINITIONS \ + X(DW_CHILDREN_no, 0x0) \ + X(DW_CHILDREN_yes, 0x1) +enum { DW_CHILDREN_DEFINITIONS }; + +#define DW_DEFAULTED_DEFINITIONS \ + X(DW_DEFAULTED_no, 0x0) \ + X(DW_DEFAULTED_in_class, 0x1) \ + X(DW_DEFAULTED_out_of_class, 0x2) +enum { DW_DEFAULTED_DEFINITIONS }; + +#define DW_DS_DEFINITIONS \ + X(DW_DS_unsigned, 0x1) \ + X(DW_DS_leading_overpunch, 0x2) \ + X(DW_DS_trailing_overpunch, 0x3) \ + X(DW_DS_leading_separate, 0x4) \ + X(DW_DS_trailing_separate, 0x5) +enum { DW_DS_DEFINITIONS }; + +#define DW_DSC_DEFINITIONS \ + X(DW_DSC_label, 0x0) \ + X(DW_DSC_range, 0x1) +enum { DW_DSC_DEFINITIONS }; + +#define DW_EH_PE_DEFINITIONS \ + X(DW_EH_PE_absptr, 0x0) \ + X(DW_EH_PE_uleb128, 0x1) \ + X(DW_EH_PE_udata2, 0x2) \ + X(DW_EH_PE_udata4, 0x3) \ + X(DW_EH_PE_udata8, 0x4) \ + X(DW_EH_PE_sleb128, 0x9) \ + X(DW_EH_PE_sdata2, 0xa) \ + X(DW_EH_PE_sdata4, 0xb) \ + X(DW_EH_PE_sdata8, 0xc) \ + X(DW_EH_PE_signed, 0x8) \ + X(DW_EH_PE_pcrel, 0x10) \ + X(DW_EH_PE_textrel, 0x20) \ + X(DW_EH_PE_datarel, 0x30) \ + X(DW_EH_PE_funcrel, 0x40) \ + X(DW_EH_PE_aligned, 0x50) \ + X(DW_EH_PE_indirect, 0x80) \ + X(DW_EH_PE_omit, 0xff) +enum { DW_EH_PE_DEFINITIONS }; + +#define DW_END_DEFINITIONS \ + X(DW_END_default, 0x0) \ + X(DW_END_big, 0x1) \ + X(DW_END_little, 0x2) \ + X(DW_END_lo_user, 0x40) \ + X(DW_END_hi_user, 0xff) +enum { DW_END_DEFINITIONS }; + +#define DW_FORM_DEFINITIONS \ + X(DW_FORM_addr, 0x1) \ + X(DW_FORM_block2, 0x3) \ + X(DW_FORM_block4, 0x4) \ + X(DW_FORM_data2, 0x5) \ + X(DW_FORM_data4, 0x6) \ + X(DW_FORM_data8, 0x7) \ + X(DW_FORM_string, 0x8) \ + X(DW_FORM_block, 0x9) \ + X(DW_FORM_block1, 0xa) \ + X(DW_FORM_data1, 0xb) \ + X(DW_FORM_flag, 0xc) \ + X(DW_FORM_sdata, 0xd) \ + X(DW_FORM_strp, 0xe) \ + X(DW_FORM_udata, 0xf) \ + X(DW_FORM_ref_addr, 0x10) \ + X(DW_FORM_ref1, 0x11) \ + X(DW_FORM_ref2, 0x12) \ + X(DW_FORM_ref4, 0x13) \ + X(DW_FORM_ref8, 0x14) \ + X(DW_FORM_ref_udata, 0x15) \ + X(DW_FORM_indirect, 0x16) \ + X(DW_FORM_sec_offset, 0x17) \ + X(DW_FORM_exprloc, 0x18) \ + X(DW_FORM_flag_present, 0x19) \ + X(DW_FORM_strx, 0x1a) \ + X(DW_FORM_addrx, 0x1b) \ + X(DW_FORM_ref_sup4, 0x1c) \ + X(DW_FORM_strp_sup, 0x1d) \ + X(DW_FORM_data16, 0x1e) \ + X(DW_FORM_line_strp, 0x1f) \ + X(DW_FORM_ref_sig8, 0x20) \ + X(DW_FORM_implicit_const, 0x21) \ + X(DW_FORM_loclistx, 0x22) \ + X(DW_FORM_rnglistx, 0x23) \ + X(DW_FORM_ref_sup8, 0x24) \ + X(DW_FORM_strx1, 0x25) \ + X(DW_FORM_strx2, 0x26) \ + X(DW_FORM_strx3, 0x27) \ + X(DW_FORM_strx4, 0x28) \ + X(DW_FORM_addrx1, 0x29) \ + X(DW_FORM_addrx2, 0x2a) \ + X(DW_FORM_addrx3, 0x2b) \ + X(DW_FORM_addrx4, 0x2c) \ + X(DW_FORM_GNU_addr_index, 0x1f01) \ + X(DW_FORM_GNU_str_index, 0x1f02) \ + X(DW_FORM_GNU_ref_alt, 0x1f20) \ + X(DW_FORM_GNU_strp_alt, 0x1f21) \ + X(DW_FORM_LLVM_addrx_offset, 0x2001) +enum { DW_FORM_DEFINITIONS }; + +#define DW_ID_DEFINITIONS \ + X(DW_ID_case_sensitive, 0x0) \ + X(DW_ID_up_case, 0x1) \ + X(DW_ID_down_case, 0x2) \ + X(DW_ID_case_insensitive, 0x3) +enum { DW_ID_DEFINITIONS }; + +#define DW_IDX_DEFINITIONS \ + X(DW_IDX_compile_unit, 0x1) \ + X(DW_IDX_type_unit, 0x2) \ + X(DW_IDX_die_offset, 0x3) \ + X(DW_IDX_parent, 0x4) \ + X(DW_IDX_type_hash, 0x5) \ + X(DW_IDX_GNU_internal, 0x2000) \ + X(DW_IDX_lo_user, 0x2000) \ + X(DW_IDX_GNU_external, 0x2001) \ + X(DW_IDX_hi_user, 0x3fff) +enum { DW_IDX_DEFINITIONS }; + +#define DW_INL_DEFINITIONS \ + X(DW_INL_not_inlined, 0x0) \ + X(DW_INL_inlined, 0x1) \ + X(DW_INL_declared_not_inlined, 0x2) \ + X(DW_INL_declared_inlined, 0x3) +enum { DW_INL_DEFINITIONS }; + +#define DW_LANG_DEFINITIONS \ + X(DW_LANG_C89, 0x1) \ + X(DW_LANG_C, 0x2) \ + X(DW_LANG_Ada83, 0x3) \ + X(DW_LANG_C_plus_plus, 0x4) \ + X(DW_LANG_Cobol74, 0x5) \ + X(DW_LANG_Cobol85, 0x6) \ + X(DW_LANG_Fortran77, 0x7) \ + X(DW_LANG_Fortran90, 0x8) \ + X(DW_LANG_Pascal83, 0x9) \ + X(DW_LANG_Modula2, 0xa) \ + X(DW_LANG_Java, 0xb) \ + X(DW_LANG_C99, 0xc) \ + X(DW_LANG_Ada95, 0xd) \ + X(DW_LANG_Fortran95, 0xe) \ + X(DW_LANG_PLI, 0xf) \ + X(DW_LANG_ObjC, 0x10) \ + X(DW_LANG_ObjC_plus_plus, 0x11) \ + X(DW_LANG_UPC, 0x12) \ + X(DW_LANG_D, 0x13) \ + X(DW_LANG_Python, 0x14) \ + X(DW_LANG_OpenCL, 0x15) \ + X(DW_LANG_Go, 0x16) \ + X(DW_LANG_Modula3, 0x17) \ + X(DW_LANG_Haskell, 0x18) \ + X(DW_LANG_C_plus_plus_03, 0x19) \ + X(DW_LANG_C_plus_plus_11, 0x1a) \ + X(DW_LANG_OCaml, 0x1b) \ + X(DW_LANG_Rust, 0x1c) \ + X(DW_LANG_C11, 0x1d) \ + X(DW_LANG_Swift, 0x1e) \ + X(DW_LANG_Julia, 0x1f) \ + X(DW_LANG_Dylan, 0x20) \ + X(DW_LANG_C_plus_plus_14, 0x21) \ + X(DW_LANG_Fortran03, 0x22) \ + X(DW_LANG_Fortran08, 0x23) \ + X(DW_LANG_RenderScript, 0x24) \ + X(DW_LANG_BLISS, 0x25) \ + X(DW_LANG_lo_user, 0x8000) \ + X(DW_LANG_Mips_Assembler, 0x8001) \ + X(DW_LANG_Upc, 0x8765) \ + X(DW_LANG_GOOGLE_RenderScript, 0x8001) \ + X(DW_LANG_ALTIUM_Assembler, 0x9101) \ + X(DW_LANG_BORLAND_Delphi, 0xb000) \ + X(DW_LANG_SUN_Assembler, 0x9001) \ + X(DW_LANG_hi_user, 0xffff) +enum { DW_LANG_DEFINITIONS }; + +#define DW_LLE_DEFINITIONS \ + X(DW_LLE_end_of_list, 0x0) \ + X(DW_LLE_base_addressx, 0x1) \ + X(DW_LLE_startx_endx, 0x2) \ + X(DW_LLE_startx_length, 0x3) \ + X(DW_LLE_offset_pair, 0x4) \ + X(DW_LLE_default_location, 0x5) \ + X(DW_LLE_base_address, 0x6) \ + X(DW_LLE_start_end, 0x7) \ + X(DW_LLE_start_length, 0x8) +enum { DW_LLE_DEFINITIONS }; + +#define DW_LNCT_DEFINITIONS \ + X(DW_LNCT_path, 0x1) \ + X(DW_LNCT_directory_index, 0x2) \ + X(DW_LNCT_timestamp, 0x3) \ + X(DW_LNCT_size, 0x4) \ + X(DW_LNCT_MD5, 0x5) \ + X(DW_LNCT_GNU_subprogram_name, 0x6) \ + X(DW_LNCT_GNU_decl_file, 0x7) \ + X(DW_LNCT_GNU_decl_line, 0x8) \ + X(DW_LNCT_lo_user, 0x2000) \ + X(DW_LNCT_LLVM_source, 0x2001) \ + X(DW_LNCT_LLVM_is_MD5, 0x2002) \ + X(DW_LNCT_hi_user, 0x3fff) +enum { DW_LNCT_DEFINITIONS }; + +#define DW_LNE_DEFINITIONS \ + X(DW_LNE_end_sequence, 0x1) \ + X(DW_LNE_set_address, 0x2) \ + X(DW_LNE_define_file, 0x3) \ + X(DW_LNE_set_discriminator, 0x4) \ + X(DW_LNE_HP_negate_is_UV_update, 0x11) \ + X(DW_LNE_HP_push_context, 0x12) \ + X(DW_LNE_HP_pop_context, 0x13) \ + X(DW_LNE_HP_set_file_line_column, 0x14) \ + X(DW_LNE_HP_set_routine_name, 0x15) \ + X(DW_LNE_HP_set_sequence, 0x16) \ + X(DW_LNE_HP_negate_post_semantics, 0x17) \ + X(DW_LNE_HP_negate_function_exit, 0x18) \ + X(DW_LNE_HP_negate_front_end_logical, 0x19) \ + X(DW_LNE_HP_define_proc, 0x20) \ + X(DW_LNE_HP_source_file_correlation, 0x80) \ + X(DW_LNE_lo_user, 0x80) \ + X(DW_LNE_hi_user, 0xff) +enum { DW_LNE_DEFINITIONS }; + +#define DW_LNS_DEFINITIONS \ + X(DW_LNS_copy, 0x1) \ + X(DW_LNS_advance_pc, 0x2) \ + X(DW_LNS_advance_line, 0x3) \ + X(DW_LNS_set_file, 0x4) \ + X(DW_LNS_set_column, 0x5) \ + X(DW_LNS_negate_stmt, 0x6) \ + X(DW_LNS_set_basic_block, 0x7) \ + X(DW_LNS_const_add_pc, 0x8) \ + X(DW_LNS_fixed_advance_pc, 0x9) \ + X(DW_LNS_set_prologue_end, 0xa) \ + X(DW_LNS_set_epilogue_begin, 0xb) \ + X(DW_LNS_set_isa, 0xc) \ + X(DW_LNS_set_address_from_logical, 0xd) \ + X(DW_LNS_set_subprogram, 0xd) \ + X(DW_LNS_inlined_call, 0xe) \ + X(DW_LNS_pop_context, 0xf) +enum { DW_LNS_DEFINITIONS }; + +#define DW_MACINFO_DEFINITIONS \ + X(DW_MACINFO_define, 0x1) \ + X(DW_MACINFO_undef, 0x2) \ + X(DW_MACINFO_start_file, 0x3) \ + X(DW_MACINFO_end_file, 0x4) \ + X(DW_MACINFO_vendor_ext, 0xff) +enum { DW_MACINFO_DEFINITIONS }; + +#define DW_MACRO_DEFINITIONS \ + X(DW_MACRO_define, 0x1) \ + X(DW_MACRO_undef, 0x2) \ + X(DW_MACRO_start_file, 0x3) \ + X(DW_MACRO_end_file, 0x4) \ + X(DW_MACRO_define_strp, 0x5) \ + X(DW_MACRO_undef_strp, 0x6) \ + X(DW_MACRO_import, 0x7) \ + X(DW_MACRO_define_sup, 0x8) \ + X(DW_MACRO_undef_sup, 0x9) \ + X(DW_MACRO_import_sup, 0xa) \ + X(DW_MACRO_define_strx, 0xb) \ + X(DW_MACRO_undef_strx, 0xc) \ + X(DW_MACRO_lo_user, 0xe0) \ + X(DW_MACRO_hi_user, 0xff) +enum { DW_MACRO_DEFINITIONS }; + +#define DW_OP_DEFINITIONS \ + X(DW_OP_addr, 0x3) \ + X(DW_OP_deref, 0x6) \ + X(DW_OP_const1u, 0x8) \ + X(DW_OP_const1s, 0x9) \ + X(DW_OP_const2u, 0xa) \ + X(DW_OP_const2s, 0xb) \ + X(DW_OP_const4u, 0xc) \ + X(DW_OP_const4s, 0xd) \ + X(DW_OP_const8u, 0xe) \ + X(DW_OP_const8s, 0xf) \ + X(DW_OP_constu, 0x10) \ + X(DW_OP_consts, 0x11) \ + X(DW_OP_dup, 0x12) \ + X(DW_OP_drop, 0x13) \ + X(DW_OP_over, 0x14) \ + X(DW_OP_pick, 0x15) \ + X(DW_OP_swap, 0x16) \ + X(DW_OP_rot, 0x17) \ + X(DW_OP_xderef, 0x18) \ + X(DW_OP_abs, 0x19) \ + X(DW_OP_and, 0x1a) \ + X(DW_OP_div, 0x1b) \ + X(DW_OP_minus, 0x1c) \ + X(DW_OP_mod, 0x1d) \ + X(DW_OP_mul, 0x1e) \ + X(DW_OP_neg, 0x1f) \ + X(DW_OP_not, 0x20) \ + X(DW_OP_or, 0x21) \ + X(DW_OP_plus, 0x22) \ + X(DW_OP_plus_uconst, 0x23) \ + X(DW_OP_shl, 0x24) \ + X(DW_OP_shr, 0x25) \ + X(DW_OP_shra, 0x26) \ + X(DW_OP_xor, 0x27) \ + X(DW_OP_bra, 0x28) \ + X(DW_OP_eq, 0x29) \ + X(DW_OP_ge, 0x2a) \ + X(DW_OP_gt, 0x2b) \ + X(DW_OP_le, 0x2c) \ + X(DW_OP_lt, 0x2d) \ + X(DW_OP_ne, 0x2e) \ + X(DW_OP_skip, 0x2f) \ + X(DW_OP_lit0, 0x30) \ + X(DW_OP_lit1, 0x31) \ + X(DW_OP_lit2, 0x32) \ + X(DW_OP_lit3, 0x33) \ + X(DW_OP_lit4, 0x34) \ + X(DW_OP_lit5, 0x35) \ + X(DW_OP_lit6, 0x36) \ + X(DW_OP_lit7, 0x37) \ + X(DW_OP_lit8, 0x38) \ + X(DW_OP_lit9, 0x39) \ + X(DW_OP_lit10, 0x3a) \ + X(DW_OP_lit11, 0x3b) \ + X(DW_OP_lit12, 0x3c) \ + X(DW_OP_lit13, 0x3d) \ + X(DW_OP_lit14, 0x3e) \ + X(DW_OP_lit15, 0x3f) \ + X(DW_OP_lit16, 0x40) \ + X(DW_OP_lit17, 0x41) \ + X(DW_OP_lit18, 0x42) \ + X(DW_OP_lit19, 0x43) \ + X(DW_OP_lit20, 0x44) \ + X(DW_OP_lit21, 0x45) \ + X(DW_OP_lit22, 0x46) \ + X(DW_OP_lit23, 0x47) \ + X(DW_OP_lit24, 0x48) \ + X(DW_OP_lit25, 0x49) \ + X(DW_OP_lit26, 0x4a) \ + X(DW_OP_lit27, 0x4b) \ + X(DW_OP_lit28, 0x4c) \ + X(DW_OP_lit29, 0x4d) \ + X(DW_OP_lit30, 0x4e) \ + X(DW_OP_lit31, 0x4f) \ + X(DW_OP_reg0, 0x50) \ + X(DW_OP_reg1, 0x51) \ + X(DW_OP_reg2, 0x52) \ + X(DW_OP_reg3, 0x53) \ + X(DW_OP_reg4, 0x54) \ + X(DW_OP_reg5, 0x55) \ + X(DW_OP_reg6, 0x56) \ + X(DW_OP_reg7, 0x57) \ + X(DW_OP_reg8, 0x58) \ + X(DW_OP_reg9, 0x59) \ + X(DW_OP_reg10, 0x5a) \ + X(DW_OP_reg11, 0x5b) \ + X(DW_OP_reg12, 0x5c) \ + X(DW_OP_reg13, 0x5d) \ + X(DW_OP_reg14, 0x5e) \ + X(DW_OP_reg15, 0x5f) \ + X(DW_OP_reg16, 0x60) \ + X(DW_OP_reg17, 0x61) \ + X(DW_OP_reg18, 0x62) \ + X(DW_OP_reg19, 0x63) \ + X(DW_OP_reg20, 0x64) \ + X(DW_OP_reg21, 0x65) \ + X(DW_OP_reg22, 0x66) \ + X(DW_OP_reg23, 0x67) \ + X(DW_OP_reg24, 0x68) \ + X(DW_OP_reg25, 0x69) \ + X(DW_OP_reg26, 0x6a) \ + X(DW_OP_reg27, 0x6b) \ + X(DW_OP_reg28, 0x6c) \ + X(DW_OP_reg29, 0x6d) \ + X(DW_OP_reg30, 0x6e) \ + X(DW_OP_reg31, 0x6f) \ + X(DW_OP_breg0, 0x70) \ + X(DW_OP_breg1, 0x71) \ + X(DW_OP_breg2, 0x72) \ + X(DW_OP_breg3, 0x73) \ + X(DW_OP_breg4, 0x74) \ + X(DW_OP_breg5, 0x75) \ + X(DW_OP_breg6, 0x76) \ + X(DW_OP_breg7, 0x77) \ + X(DW_OP_breg8, 0x78) \ + X(DW_OP_breg9, 0x79) \ + X(DW_OP_breg10, 0x7a) \ + X(DW_OP_breg11, 0x7b) \ + X(DW_OP_breg12, 0x7c) \ + X(DW_OP_breg13, 0x7d) \ + X(DW_OP_breg14, 0x7e) \ + X(DW_OP_breg15, 0x7f) \ + X(DW_OP_breg16, 0x80) \ + X(DW_OP_breg17, 0x81) \ + X(DW_OP_breg18, 0x82) \ + X(DW_OP_breg19, 0x83) \ + X(DW_OP_breg20, 0x84) \ + X(DW_OP_breg21, 0x85) \ + X(DW_OP_breg22, 0x86) \ + X(DW_OP_breg23, 0x87) \ + X(DW_OP_breg24, 0x88) \ + X(DW_OP_breg25, 0x89) \ + X(DW_OP_breg26, 0x8a) \ + X(DW_OP_breg27, 0x8b) \ + X(DW_OP_breg28, 0x8c) \ + X(DW_OP_breg29, 0x8d) \ + X(DW_OP_breg30, 0x8e) \ + X(DW_OP_breg31, 0x8f) \ + X(DW_OP_regx, 0x90) \ + X(DW_OP_fbreg, 0x91) \ + X(DW_OP_bregx, 0x92) \ + X(DW_OP_piece, 0x93) \ + X(DW_OP_deref_size, 0x94) \ + X(DW_OP_xderef_size, 0x95) \ + X(DW_OP_nop, 0x96) \ + X(DW_OP_push_object_address, 0x97) \ + X(DW_OP_call2, 0x98) \ + X(DW_OP_call4, 0x99) \ + X(DW_OP_call_ref, 0x9a) \ + X(DW_OP_form_tls_address, 0x9b) \ + X(DW_OP_call_frame_cfa, 0x9c) \ + X(DW_OP_bit_piece, 0x9d) \ + X(DW_OP_implicit_value, 0x9e) \ + X(DW_OP_stack_value, 0x9f) \ + X(DW_OP_implicit_pointer, 0xa0) \ + X(DW_OP_addrx, 0xa1) \ + X(DW_OP_constx, 0xa2) \ + X(DW_OP_entry_value, 0xa3) \ + X(DW_OP_const_type, 0xa4) \ + X(DW_OP_regval_type, 0xa5) \ + X(DW_OP_deref_type, 0xa6) \ + X(DW_OP_xderef_type, 0xa7) \ + X(DW_OP_convert, 0xa8) \ + X(DW_OP_reinterpret, 0xa9) \ + X(DW_OP_GNU_push_tls_address, 0xe0) \ + X(DW_OP_WASM_location, 0xed) \ + X(DW_OP_WASM_location_int, 0xee) \ + X(DW_OP_lo_user, 0xe0) \ + X(DW_OP_LLVM_form_aspace_address, 0xe1) \ + X(DW_OP_LLVM_push_lane, 0xe2) \ + X(DW_OP_LLVM_offset, 0xe3) \ + X(DW_OP_LLVM_offset_uconst, 0xe4) \ + X(DW_OP_LLVM_bit_offset, 0xe5) \ + X(DW_OP_LLVM_call_frame_entry_reg, 0xe6) \ + X(DW_OP_LLVM_undefined, 0xe7) \ + X(DW_OP_LLVM_aspace_bregx, 0xe8) \ + X(DW_OP_LLVM_aspace_implicit_pointer, 0xe9) \ + X(DW_OP_LLVM_piece_end, 0xea) \ + X(DW_OP_LLVM_extend, 0xeb) \ + X(DW_OP_LLVM_select_bit_piece, 0xec) \ + X(DW_OP_HP_unknown, 0xe0) \ + X(DW_OP_HP_is_value, 0xe1) \ + X(DW_OP_HP_fltconst4, 0xe2) \ + X(DW_OP_HP_fltconst8, 0xe3) \ + X(DW_OP_HP_mod_range, 0xe4) \ + X(DW_OP_HP_unmod_range, 0xe5) \ + X(DW_OP_HP_tls, 0xe6) \ + X(DW_OP_INTEL_bit_piece, 0xe8) \ + X(DW_OP_GNU_uninit, 0xf0) \ + X(DW_OP_APPLE_uninit, 0xf0) \ + X(DW_OP_GNU_encoded_addr, 0xf1) \ + X(DW_OP_GNU_implicit_pointer, 0xf2) \ + X(DW_OP_GNU_entry_value, 0xf3) \ + X(DW_OP_GNU_const_type, 0xf4) \ + X(DW_OP_GNU_regval_type, 0xf5) \ + X(DW_OP_GNU_deref_type, 0xf6) \ + X(DW_OP_GNU_convert, 0xf7) \ + X(DW_OP_GNU_reinterpret, 0xf9) \ + X(DW_OP_GNU_parameter_ref, 0xfa) \ + X(DW_OP_GNU_addr_index, 0xfb) \ + X(DW_OP_GNU_const_index, 0xfc) \ + X(DW_OP_GNU_variable_value, 0xfd) \ + X(DW_OP_PGI_omp_thread_num, 0xf8) \ + X(DW_OP_hi_user, 0xff) +enum { DW_OP_DEFINITIONS }; + +#define DW_ORD_DEFINITIONS \ + X(DW_ORD_row_major, 0x0) \ + X(DW_ORD_col_major, 0x1) +enum { DW_ORD_DEFINITIONS }; + +#define DW_RLE_DEFINITIONS \ + X(DW_RLE_end_of_list, 0x0) \ + X(DW_RLE_base_addressx, 0x1) \ + X(DW_RLE_startx_endx, 0x2) \ + X(DW_RLE_startx_length, 0x3) \ + X(DW_RLE_offset_pair, 0x4) \ + X(DW_RLE_base_address, 0x5) \ + X(DW_RLE_start_end, 0x6) \ + X(DW_RLE_start_length, 0x7) +enum { DW_RLE_DEFINITIONS }; + +#define DW_SECT_DEFINITIONS \ + X(DW_SECT_INFO, 0x1) \ + X(DW_SECT_TYPES, 0x2) \ + X(DW_SECT_ABBREV, 0x3) \ + X(DW_SECT_LINE, 0x4) \ + X(DW_SECT_LOCLISTS, 0x5) \ + X(DW_SECT_STR_OFFSETS, 0x6) \ + X(DW_SECT_MACRO, 0x7) \ + X(DW_SECT_RNGLISTS, 0x8) +enum { DW_SECT_DEFINITIONS }; + +#define DW_TAG_DEFINITIONS \ + X(DW_TAG_array_type, 0x1) \ + X(DW_TAG_class_type, 0x2) \ + X(DW_TAG_entry_point, 0x3) \ + X(DW_TAG_enumeration_type, 0x4) \ + X(DW_TAG_formal_parameter, 0x5) \ + X(DW_TAG_imported_declaration, 0x8) \ + X(DW_TAG_label, 0xa) \ + X(DW_TAG_lexical_block, 0xb) \ + X(DW_TAG_member, 0xd) \ + X(DW_TAG_pointer_type, 0xf) \ + X(DW_TAG_reference_type, 0x10) \ + X(DW_TAG_compile_unit, 0x11) \ + X(DW_TAG_string_type, 0x12) \ + X(DW_TAG_structure_type, 0x13) \ + X(DW_TAG_subroutine_type, 0x15) \ + X(DW_TAG_typedef, 0x16) \ + X(DW_TAG_union_type, 0x17) \ + X(DW_TAG_unspecified_parameters, 0x18) \ + X(DW_TAG_variant, 0x19) \ + X(DW_TAG_common_block, 0x1a) \ + X(DW_TAG_common_inclusion, 0x1b) \ + X(DW_TAG_inheritance, 0x1c) \ + X(DW_TAG_inlined_subroutine, 0x1d) \ + X(DW_TAG_module, 0x1e) \ + X(DW_TAG_ptr_to_member_type, 0x1f) \ + X(DW_TAG_set_type, 0x20) \ + X(DW_TAG_subrange_type, 0x21) \ + X(DW_TAG_with_stmt, 0x22) \ + X(DW_TAG_access_declaration, 0x23) \ + X(DW_TAG_base_type, 0x24) \ + X(DW_TAG_catch_block, 0x25) \ + X(DW_TAG_const_type, 0x26) \ + X(DW_TAG_constant, 0x27) \ + X(DW_TAG_enumerator, 0x28) \ + X(DW_TAG_file_type, 0x29) \ + X(DW_TAG_friend, 0x2a) \ + X(DW_TAG_namelist, 0x2b) \ + X(DW_TAG_namelist_item, 0x2c) \ + X(DW_TAG_packed_type, 0x2d) \ + X(DW_TAG_subprogram, 0x2e) \ + X(DW_TAG_template_type_parameter, 0x2f) \ + X(DW_TAG_template_value_parameter, 0x30) \ + X(DW_TAG_thrown_type, 0x31) \ + X(DW_TAG_try_block, 0x32) \ + X(DW_TAG_variant_part, 0x33) \ + X(DW_TAG_variable, 0x34) \ + X(DW_TAG_volatile_type, 0x35) \ + X(DW_TAG_dwarf_procedure, 0x36) \ + X(DW_TAG_restrict_type, 0x37) \ + X(DW_TAG_interface_type, 0x38) \ + X(DW_TAG_namespace, 0x39) \ + X(DW_TAG_imported_module, 0x3a) \ + X(DW_TAG_unspecified_type, 0x3b) \ + X(DW_TAG_partial_unit, 0x3c) \ + X(DW_TAG_imported_unit, 0x3d) \ + X(DW_TAG_mutable_type, 0x3e) \ + X(DW_TAG_condition, 0x3f) \ + X(DW_TAG_shared_type, 0x40) \ + X(DW_TAG_type_unit, 0x41) \ + X(DW_TAG_rvalue_reference_type, 0x42) \ + X(DW_TAG_template_alias, 0x43) \ + X(DW_TAG_coarray_type, 0x44) \ + X(DW_TAG_generic_subrange, 0x45) \ + X(DW_TAG_dynamic_type, 0x46) \ + X(DW_TAG_atomic_type, 0x47) \ + X(DW_TAG_call_site, 0x48) \ + X(DW_TAG_call_site_parameter, 0x49) \ + X(DW_TAG_skeleton_unit, 0x4a) \ + X(DW_TAG_immutable_type, 0x4b) \ + X(DW_TAG_lo_user, 0x4080) \ + X(DW_TAG_MIPS_loop, 0x4081) \ + X(DW_TAG_HP_array_descriptor, 0x4090) \ + X(DW_TAG_format_label, 0x4101) \ + X(DW_TAG_function_template, 0x4102) \ + X(DW_TAG_class_template, 0x4103) \ + X(DW_TAG_GNU_BINCL, 0x4104) \ + X(DW_TAG_GNU_EINCL, 0x4105) \ + X(DW_TAG_GNU_template_template_param, 0x4106) \ + X(DW_TAG_GNU_template_parameter_pack, 0x4107) \ + X(DW_TAG_GNU_formal_parameter_pack, 0x4108) \ + X(DW_TAG_GNU_call_site, 0x4109) \ + X(DW_TAG_GNU_call_site_parameter, 0x410a) \ + X(DW_TAG_SUN_function_template, 0x4201) \ + X(DW_TAG_SUN_class_template, 0x4202) \ + X(DW_TAG_SUN_struct_template, 0x4203) \ + X(DW_TAG_SUN_union_template, 0x4204) \ + X(DW_TAG_SUN_indirect_inheritance, 0x4205) \ + X(DW_TAG_SUN_codeflags, 0x4206) \ + X(DW_TAG_SUN_memop_info, 0x4207) \ + X(DW_TAG_SUN_omp_child_func, 0x4208) \ + X(DW_TAG_SUN_rtti_descriptor, 0x4209) \ + X(DW_TAG_SUN_dtor_info, 0x420a) \ + X(DW_TAG_SUN_dtor, 0x420b) \ + X(DW_TAG_SUN_f90_interface, 0x420c) \ + X(DW_TAG_SUN_fortran_vax_structure, 0x420d) \ + X(DW_TAG_SUN_hi, 0x42ff) \ + X(DW_TAG_ALTIUM_circ_type, 0x5101) \ + X(DW_TAG_ALTIUM_mwa_circ_type, 0x5102) \ + X(DW_TAG_ALTIUM_rev_carry_type, 0x5103) \ + X(DW_TAG_ALTIUM_rom, 0x5111) \ + X(DW_TAG_LLVM_annotation, 0x6000) \ + X(DW_TAG_ghs_namespace, 0x8004) \ + X(DW_TAG_ghs_using_namespace, 0x8005) \ + X(DW_TAG_ghs_using_declaration, 0x8006) \ + X(DW_TAG_ghs_template_templ_param, 0x8007) \ + X(DW_TAG_upc_shared_type, 0x8765) \ + X(DW_TAG_upc_strict_type, 0x8766) \ + X(DW_TAG_upc_relaxed_type, 0x8767) \ + X(DW_TAG_PGI_kanji_type, 0xa000) \ + X(DW_TAG_PGI_interface_block, 0xa020) \ + X(DW_TAG_BORLAND_property, 0xb000) \ + X(DW_TAG_BORLAND_Delphi_string, 0xb001) \ + X(DW_TAG_BORLAND_Delphi_dynamic_array, 0xb002) \ + X(DW_TAG_BORLAND_Delphi_set, 0xb003) \ + X(DW_TAG_BORLAND_Delphi_variant, 0xb004) \ + X(DW_TAG_hi_user, 0xffff) +enum { DW_TAG_DEFINITIONS }; +#define DW_TAG_STR_UNKNOWN_FORMAT "DW_TAG_<0x%x>" +#define DW_TAG_STR_BUF_LEN (sizeof(DW_TAG_STR_UNKNOWN_FORMAT) - 2 + 2 * sizeof(int)) +/** + * Get the name of a `DW_TAG` value. + * + * @return Static string if the value is known or @p buf if the value is + * unknown. + */ +const char *dw_tag_str(int value, char buf[static DW_TAG_STR_BUF_LEN]); + +#define DW_UT_DEFINITIONS \ + X(DW_UT_compile, 0x1) \ + X(DW_UT_type, 0x2) \ + X(DW_UT_partial, 0x3) \ + X(DW_UT_skeleton, 0x4) \ + X(DW_UT_split_compile, 0x5) \ + X(DW_UT_split_type, 0x6) \ + X(DW_UT_lo_user, 0x80) \ + X(DW_UT_hi_user, 0xff) +enum { DW_UT_DEFINITIONS }; + +#define DW_VIRTUALITY_DEFINITIONS \ + X(DW_VIRTUALITY_none, 0x0) \ + X(DW_VIRTUALITY_virtual, 0x1) \ + X(DW_VIRTUALITY_pure_virtual, 0x2) +enum { DW_VIRTUALITY_DEFINITIONS }; + +#define DW_VIS_DEFINITIONS \ + X(DW_VIS_local, 0x1) \ + X(DW_VIS_exported, 0x2) \ + X(DW_VIS_qualified, 0x3) +enum { DW_VIS_DEFINITIONS }; + +#undef X + +#endif /* DWARF_CONSTANTS_H */ diff --git a/libdrgn/dwarf_info.c b/libdrgn/dwarf_info.c index 67c52c039..0afbc43ae 100644 --- a/libdrgn/dwarf_info.c +++ b/libdrgn/dwarf_info.c @@ -1,76 +1,68 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include -#include #include -#include #include #include #include #include -#include #include #include -#ifdef _OPENMP -#include -#else -typedef struct {} omp_lock_t; -#define omp_init_lock(lock) do {} while (0) -#define omp_destroy_lock(lock) do {} while (0) -#define omp_set_lock(lock) do {} while (0) -#define omp_unset_lock(lock) do {} while (0) -static inline int omp_get_thread_num(void) -{ - return 0; -} -static inline int omp_get_max_threads(void) -{ - return 1; -} -#endif - #include "array.h" +#include "binary_buffer.h" +#include "cleanup.h" #include "debug_info.h" // IWYU pragma: associated +#include "dwarf_constants.h" +#include "elf_file.h" #include "error.h" #include "language.h" #include "lazy_object.h" +#include "log.h" #include "minmax.h" #include "object.h" +#include "openmp.h" #include "path.h" #include "program.h" +#include "platform.h" #include "register_state.h" #include "serialize.h" #include "type.h" #include "util.h" -void drgn_dwarf_module_info_deinit(struct drgn_debug_info_module *module) +void drgn_module_dwarf_info_deinit(struct drgn_module *module) { - free(module->dwarf.fdes); - free(module->dwarf.cies); + free(module->dwarf.eh_frame.fdes); + free(module->dwarf.eh_frame.cies); + free(module->dwarf.debug_frame.fdes); + free(module->dwarf.debug_frame.cies); } -static inline uintptr_t -drgn_dwarf_specification_to_key(const struct drgn_dwarf_specification *entry) +DEFINE_VECTOR_FUNCTIONS(drgn_dwarf_index_die_vector); + +DEFINE_HASH_MAP_FUNCTIONS(drgn_dwarf_index_die_map, nstring_hash_pair, + nstring_eq); + +static inline struct nstring +drgn_namespace_key(struct drgn_namespace_dwarf_index * const *entry) { - return entry->declaration; + return (struct nstring){ (*entry)->name, (*entry)->name_len }; } -DEFINE_HASH_TABLE_FUNCTIONS(drgn_dwarf_specification_map, - drgn_dwarf_specification_to_key, int_key_hash_pair, - scalar_key_eq) +DEFINE_HASH_TABLE_FUNCTIONS(drgn_namespace_table, drgn_namespace_key, + nstring_hash_pair, nstring_eq); -/** - * Placeholder for drgn_dwarf_index_cu::file_name_hashes if the CU has no - * filenames. - */ -static const uint64_t no_file_name_hashes[1] = { 0 }; +DEFINE_HASH_MAP_FUNCTIONS(drgn_dwarf_base_type_map, nstring_hash_pair, + nstring_eq); + +DEFINE_HASH_MAP_FUNCTIONS(drgn_dwarf_specification_map, int_key_hash_pair, + scalar_key_eq); /** DWARF compilation unit indexed in a @ref drgn_namespace_dwarf_index. */ struct drgn_dwarf_index_cu { - /** Module containing CU. */ - struct drgn_debug_info_module *module; + /** File containing CU. */ + struct drgn_elf_file *file; /** Address of CU data. */ const char *buf; /** Length of CU data. */ @@ -87,7 +79,7 @@ struct drgn_dwarf_index_cu { * Section containing CU (@ref DRGN_SCN_DEBUG_INFO or @ref * DRGN_SCN_DEBUG_TYPES). */ - enum drgn_debug_info_scn scn; + enum drgn_section_index scn; /** * Mapping from DWARF abbreviation code to instructions for that * abbreviation. @@ -101,8 +93,20 @@ struct drgn_dwarf_index_cu { * starting at one, so we can get away with a flat array. */ uint32_t *abbrev_decls; - /** Number of abbreviation codes. */ - size_t num_abbrev_decls; + union { + /** Number of abbreviation codes. */ + size_t num_abbrev_decls; + /** + * Pointer in .debug_abbrev for this CU. + * + * This is only used before indexing, then it is replaced by @c + * abbrev_decls, @c num_abbrev_decls, and @c abbrev_insns. It is + * a union with @c num_abbrev_decls rather than one of the other + * two fields because that way we don't need to worry about + * accidentally freeing it. + */ + const char *pending_abbrev; + }; /** * Buffer of @ref drgn_dwarf_index_abbrev_insn instructions for all * abbreviation codes. @@ -110,100 +114,34 @@ struct drgn_dwarf_index_cu { * These are all stored in one array for cache locality. */ uint8_t *abbrev_insns; - /** - * Hashes of file names from line number program header for this CU, - * indexed by the line number program file numbers. - */ - uint64_t *file_name_hashes; - /** Number of file names in the line number program header. */ - size_t num_file_names; /** * Pointer in `.debug_str_offsets` section to string offset entries for * this CU. */ const char *str_offsets; + /** libdw structure for this CU. */ + Dwarf_CU *libdw_cu; }; -DEFINE_VECTOR_FUNCTIONS(drgn_dwarf_index_cu_vector) - -DEFINE_HASH_MAP_FUNCTIONS(drgn_dwarf_type_map, ptr_key_hash_pair, scalar_key_eq) - -/** DIE which needs to be indexed. */ -struct drgn_dwarf_index_pending_die { - /** - * CU containing DIE (as an index into @ref drgn_dwarf_info::index_cus). - */ - size_t cu; - /** Address of DIE */ - uintptr_t addr; -}; - -DEFINE_VECTOR_FUNCTIONS(drgn_dwarf_index_pending_die_vector) - -/** DIE indexed in a @ref drgn_namespace_dwarf_index. */ -struct drgn_dwarf_index_die { - /** - * The next DIE with the same name (as an index into @ref - * drgn_dwarf_index_shard::dies), or `UINT32_MAX` if this is the last - * DIE. - */ - uint32_t next; - /** DIE tag. */ - uint8_t tag; - union { - /** - * Hash of filename containing declaration. - * - * DIEs with the same name but different tags or files are - * considered distinct. We only compare the hash of the file - * name, not the string value, because a 64-bit collision is - * unlikely enough, especially when also considering the name - * and tag. - * - * This is used if `tag != DW_TAG_namespace` (namespaces are - * merged, so they don't need this). - */ - uint64_t file_name_hash; - /** Nested namespace if `tag == DW_TAG_namespace`. */ - struct drgn_namespace_dwarf_index *namespace; - }; - /** Module containing this DIE. */ - struct drgn_debug_info_module *module; - /** Address of this DIE. */ - uintptr_t addr; -}; - -DEFINE_HASH_MAP(drgn_dwarf_index_die_map, struct nstring, uint32_t, - nstring_hash_pair, nstring_eq) -DEFINE_VECTOR(drgn_dwarf_index_die_vector, struct drgn_dwarf_index_die) +DEFINE_VECTOR_FUNCTIONS(drgn_dwarf_index_cu_vector); -#define DRGN_DWARF_INDEX_SHARD_BITS 8 -static const size_t DRGN_DWARF_INDEX_NUM_SHARDS = 1 << DRGN_DWARF_INDEX_SHARD_BITS; - -/** Shard of a @ref drgn_namespace_dwarf_index. */ -struct drgn_dwarf_index_shard { - /** Mutex for this shard. */ - omp_lock_t lock; - /** - * Map from name to list of DIEs with that name (as the index in @ref - * drgn_dwarf_index_shard::dies of the first DIE with that name). - */ - struct drgn_dwarf_index_die_map map; - /** - * Entries in @ref drgn_dwarf_index_shard::map. - * - * These are stored in one array for cache locality. - */ - struct drgn_dwarf_index_die_vector dies; -}; +DEFINE_HASH_MAP_FUNCTIONS(drgn_dwarf_type_map, ptr_key_hash_pair, + scalar_key_eq); static void drgn_namespace_dwarf_index_init(struct drgn_namespace_dwarf_index *dindex, - struct drgn_debug_info *dbinfo) -{ - dindex->shards = NULL; - dindex->dbinfo = dbinfo; - drgn_dwarf_index_pending_die_vector_init(&dindex->pending_dies); + const char *name, size_t name_len, + struct drgn_namespace_dwarf_index *parent) +{ + dindex->dbinfo = parent->dbinfo; + dindex->name = name; + dindex->name_len = name_len; + dindex->parent = parent; + drgn_namespace_table_init(&dindex->children); + array_for_each(tag_map, dindex->map) + drgn_dwarf_index_die_map_init(tag_map); + dindex->cus_indexed = 0; + memset(dindex->dies_indexed, 0, sizeof(dindex->dies_indexed)); dindex->saved_err = NULL; } @@ -211,28 +149,27 @@ static void drgn_namespace_dwarf_index_deinit(struct drgn_namespace_dwarf_index *dindex) { drgn_error_destroy(dindex->saved_err); - drgn_dwarf_index_pending_die_vector_deinit(&dindex->pending_dies); - if (dindex->shards) { - for (size_t i = 0; i < DRGN_DWARF_INDEX_NUM_SHARDS; i++) { - struct drgn_dwarf_index_shard *shard = &dindex->shards[i]; - for (size_t j = 0; j < shard->dies.size; j++) { - struct drgn_dwarf_index_die *die = &shard->dies.data[j]; - if (die->tag == DW_TAG_namespace) { - drgn_namespace_dwarf_index_deinit(die->namespace); - free(die->namespace); - } - } - drgn_dwarf_index_die_vector_deinit(&shard->dies); - drgn_dwarf_index_die_map_deinit(&shard->map); - omp_destroy_lock(&shard->lock); - } - free(dindex->shards); + array_for_each(tag_map, dindex->map) { + for (auto it = drgn_dwarf_index_die_map_first(tag_map); it.entry; + it = drgn_dwarf_index_die_map_next(it)) + drgn_dwarf_index_die_vector_deinit(&it.entry->value); + drgn_dwarf_index_die_map_deinit(tag_map); + } + for (auto it = drgn_namespace_table_first(&dindex->children); it.entry; + it = drgn_namespace_table_next(it)) { + drgn_namespace_dwarf_index_deinit(*it.entry); + free(*it.entry); } + drgn_namespace_table_deinit(&dindex->children); } void drgn_dwarf_info_init(struct drgn_debug_info *dbinfo) { - drgn_namespace_dwarf_index_init(&dbinfo->dwarf.global, dbinfo); + dbinfo->dwarf.global.dbinfo = dbinfo; + drgn_namespace_dwarf_index_init(&dbinfo->dwarf.global, "", 0, + &dbinfo->dwarf.global); + dbinfo->dwarf.global.parent = NULL; + drgn_dwarf_base_type_map_init(&dbinfo->dwarf.base_types); drgn_dwarf_specification_map_init(&dbinfo->dwarf.specifications); drgn_dwarf_index_cu_vector_init(&dbinfo->dwarf.index_cus); drgn_dwarf_type_map_init(&dbinfo->dwarf.types); @@ -242,8 +179,6 @@ void drgn_dwarf_info_init(struct drgn_debug_info *dbinfo) static void drgn_dwarf_index_cu_deinit(struct drgn_dwarf_index_cu *cu) { - if (cu->file_name_hashes != no_file_name_hashes) - free(cu->file_name_hashes); free(cu->abbrev_insns); free(cu->abbrev_decls); } @@ -252,10 +187,12 @@ void drgn_dwarf_info_deinit(struct drgn_debug_info *dbinfo) { drgn_dwarf_type_map_deinit(&dbinfo->dwarf.cant_be_incomplete_array_types); drgn_dwarf_type_map_deinit(&dbinfo->dwarf.types); - for (size_t i = 0; i < dbinfo->dwarf.index_cus.size; i++) - drgn_dwarf_index_cu_deinit(&dbinfo->dwarf.index_cus.data[i]); + vector_for_each(drgn_dwarf_index_cu_vector, cu, + &dbinfo->dwarf.index_cus) + drgn_dwarf_index_cu_deinit(cu); drgn_dwarf_index_cu_vector_deinit(&dbinfo->dwarf.index_cus); drgn_dwarf_specification_map_deinit(&dbinfo->dwarf.specifications); + drgn_dwarf_base_type_map_deinit(&dbinfo->dwarf.base_types); drgn_namespace_dwarf_index_deinit(&dbinfo->dwarf.global); } @@ -263,67 +200,12 @@ void drgn_dwarf_info_deinit(struct drgn_debug_info *dbinfo) * Diagnostics. */ -#define DW_TAG_UNKNOWN_FORMAT "unknown DWARF tag 0x%02x" -#define DW_TAG_BUF_LEN (sizeof(DW_TAG_UNKNOWN_FORMAT) - 4 + 2 * sizeof(int)) - -/** - * Get the name of a DWARF tag. - * - * @return Static string if the tag is known or @p buf if the tag is unknown - * (populated with a description). - */ -static const char *dw_tag_str(int tag, char buf[DW_TAG_BUF_LEN]) -{ - switch (tag) { -#define DWARF_ONE_KNOWN_DW_TAG(name, value) case value: return "DW_TAG_" #name; - DWARF_ALL_KNOWN_DW_TAG -#undef DWARF_ONE_KNOWN_DW_TAG - default: - sprintf(buf, DW_TAG_UNKNOWN_FORMAT, tag); - return buf; - } -} - /** Like @ref dw_tag_str(), but takes a @c Dwarf_Die. */ -static const char *dwarf_tag_str(Dwarf_Die *die, char buf[DW_TAG_BUF_LEN]) +static const char *dwarf_tag_str(Dwarf_Die *die, char buf[DW_TAG_STR_BUF_LEN]) { return dw_tag_str(dwarf_tag(die), buf); } -static struct drgn_error * -drgn_error_debug_info(struct drgn_debug_info_module *module, const char *ptr, - const char *message) -{ - uintptr_t p = (uintptr_t)ptr; - int end_match = -1; - for (int i = 0; i < array_size(module->scn_data); i++) { - if (!module->scn_data[i]) - continue; - uintptr_t start = (uintptr_t)module->scn_data[i]->d_buf; - uintptr_t end = start + module->scn_data[i]->d_size; - if (start <= p) { - if (p < end) { - return drgn_error_debug_info_scn(module, i, ptr, - message); - } else if (p == end) { - end_match = i; - } - } - } - if (end_match != -1) { - /* - * The pointer doesn't lie within a section, but it does point - * to the end of a section. - */ - return drgn_error_debug_info_scn(module, end_match, ptr, - message); - } - /* We couldn't find the section containing the pointer. */ - const char *name = dwfl_module_info(module->dwfl_module, NULL, NULL, - NULL, NULL, NULL, NULL, NULL); - return drgn_error_format(DRGN_ERROR_OTHER, "%s: %s", name, message); -} - static inline struct drgn_error *drgn_check_address_size(uint8_t address_size) { if (address_size < 1 || address_size > 8) { @@ -355,16 +237,6 @@ static inline struct drgn_error *drgn_check_address_size(uint8_t address_size) * Other namespaces are indexed when they are first accessed. */ -struct drgn_dwarf_index_pending_cu { - struct drgn_debug_info_module *module; - const char *buf; - size_t len; - bool is_64_bit; - enum drgn_debug_info_scn scn; -}; - -DEFINE_VECTOR_FUNCTIONS(drgn_dwarf_index_pending_cu_vector) - /** * DWARF abbreviation table instructions. * @@ -379,7 +251,7 @@ enum drgn_dwarf_index_abbrev_insn { * Instructions > 0 and <= INSN_MAX_SKIP indicate a number of bytes to * be skipped over. */ - INSN_MAX_SKIP = 193, + INSN_MAX_SKIP = 219, /* These instructions indicate an attribute that can be skipped over. */ INSN_SKIP_BLOCK, @@ -405,32 +277,6 @@ enum drgn_dwarf_index_abbrev_insn { INSN_NAME_STRX4, INSN_NAME_STRP_ALT4, INSN_NAME_STRP_ALT8, - INSN_COMP_DIR_STRP4, - INSN_COMP_DIR_STRP8, - INSN_COMP_DIR_LINE_STRP4, - INSN_COMP_DIR_LINE_STRP8, - INSN_COMP_DIR_STRING, - INSN_COMP_DIR_STRX, - INSN_COMP_DIR_STRX1, - INSN_COMP_DIR_STRX2, - INSN_COMP_DIR_STRX3, - INSN_COMP_DIR_STRX4, - INSN_COMP_DIR_STRP_ALT4, - INSN_COMP_DIR_STRP_ALT8, - INSN_STR_OFFSETS_BASE4, - INSN_STR_OFFSETS_BASE8, - INSN_STMT_LIST_LINEPTR4, - INSN_STMT_LIST_LINEPTR8, - INSN_DECL_FILE_DATA1, - INSN_DECL_FILE_DATA2, - INSN_DECL_FILE_DATA4, - INSN_DECL_FILE_DATA8, - INSN_DECL_FILE_UDATA, - /* - * This instruction is the only one with an operand: the ULEB128 - * implicit constant. - */ - INSN_DECL_FILE_IMPLICIT, INSN_DECLARATION_FLAG, INSN_SPECIFICATION_REF1, INSN_SPECIFICATION_REF2, @@ -444,10 +290,6 @@ enum drgn_dwarf_index_abbrev_insn { INSN_INDIRECT, INSN_SIBLING_INDIRECT, INSN_NAME_INDIRECT, - INSN_COMP_DIR_INDIRECT, - INSN_STR_OFFSETS_BASE_INDIRECT, - INSN_STMT_LIST_INDIRECT, - INSN_DECL_FILE_INDIRECT, INSN_DECLARATION_INDIRECT, INSN_SPECIFICATION_INDIRECT, @@ -461,8 +303,7 @@ enum drgn_dwarf_index_abbrev_insn { /* * The byte after INSN_END contains the DIE flags, which are a bitmask - * of flags combined with the DWARF tag (which is zero if the DIE does - * not need to be indexed). + * of flags combined with the drgn_dwarf_index_tag. */ INSN_DIE_FLAG_TAG_MASK = 0x3f, /* DIE is a declaration. */ @@ -471,13 +312,18 @@ enum drgn_dwarf_index_abbrev_insn { INSN_DIE_FLAG_CHILDREN = 0x80, }; +// We use INSN_DIE_FLAG_TAG_MASK as a sentinel when the DIE shouldn't be +// indexed, so this is < and not <=. +static_assert((int)DRGN_DWARF_INDEX_NUM_TAGS < (int)INSN_DIE_FLAG_TAG_MASK, + "too many instruction DIE tags"); + /* Instructions are 8 bits. */ static_assert(NUM_INSNS - 1 == UINT8_MAX, "maximum DWARF index instruction is invalid"); -DEFINE_VECTOR(uint8_vector, uint8_t) -DEFINE_VECTOR(uint32_vector, uint32_t) -DEFINE_VECTOR(uint64_vector, uint64_t) +DEFINE_VECTOR(uint8_vector, uint8_t); +DEFINE_VECTOR(uint32_vector, uint32_t); +DEFINE_VECTOR(uint64_vector, uint64_t); struct drgn_dwarf_index_cu_buffer { struct binary_buffer bb; @@ -490,8 +336,10 @@ drgn_dwarf_index_cu_buffer_error(struct binary_buffer *bb, const char *pos, { struct drgn_dwarf_index_cu_buffer *buffer = container_of(bb, struct drgn_dwarf_index_cu_buffer, bb); - return drgn_error_debug_info_scn(buffer->cu->module, - DRGN_SCN_DEBUG_INFO, pos, message); + return drgn_elf_file_section_error(buffer->cu->file, + buffer->cu->file->scns[buffer->cu->scn], + buffer->cu->file->scn_data[buffer->cu->scn], + pos, message); } static void @@ -499,113 +347,295 @@ drgn_dwarf_index_cu_buffer_init(struct drgn_dwarf_index_cu_buffer *buffer, struct drgn_dwarf_index_cu *cu) { binary_buffer_init(&buffer->bb, cu->buf, cu->len, - drgn_platform_is_little_endian(&cu->module->platform), + drgn_elf_file_is_little_endian(cu->file), drgn_dwarf_index_cu_buffer_error); buffer->cu = cu; } -static inline size_t hash_pair_to_shard(struct hash_pair hp) -{ - /* - * The 8 most significant bits of the hash are used as the F14 tag, so - * we don't want to use those for sharding. - */ - return ((hp.first >> - (8 * sizeof(size_t) - 8 - DRGN_DWARF_INDEX_SHARD_BITS)) & - (DRGN_DWARF_INDEX_NUM_SHARDS - 1)); -} - -static bool -drgn_namespace_dwarf_index_alloc_shards(struct drgn_namespace_dwarf_index *dindex) -{ - if (dindex->shards) - return true; - dindex->shards = malloc_array(DRGN_DWARF_INDEX_NUM_SHARDS, - sizeof(*dindex->shards)); - if (!dindex->shards) - return false; - for (size_t i = 0; i < DRGN_DWARF_INDEX_NUM_SHARDS; i++) { - struct drgn_dwarf_index_shard *shard = &dindex->shards[i]; - omp_init_lock(&shard->lock); - drgn_dwarf_index_die_map_init(&shard->map); - drgn_dwarf_index_die_vector_init(&shard->dies); - } - return true; -} - bool drgn_dwarf_index_state_init(struct drgn_dwarf_index_state *state, struct drgn_debug_info *dbinfo) { state->dbinfo = dbinfo; - state->max_threads = omp_get_max_threads(); - state->cus = malloc_array(state->max_threads, sizeof(*state->cus)); + state->cus = malloc_array(drgn_num_threads, sizeof(*state->cus)); if (!state->cus) return false; - for (size_t i = 0; i < state->max_threads; i++) - drgn_dwarf_index_pending_cu_vector_init(&state->cus[i]); + for (int i = 0; i < drgn_num_threads; i++) + drgn_dwarf_index_cu_vector_init(&state->cus[i]); return true; } void drgn_dwarf_index_state_deinit(struct drgn_dwarf_index_state *state) { - for (size_t i = 0; i < state->max_threads; i++) - drgn_dwarf_index_pending_cu_vector_deinit(&state->cus[i]); + for (int i = 0; i < drgn_num_threads; i++) + drgn_dwarf_index_cu_vector_deinit(&state->cus[i]); free(state->cus); } +static const char *drgn_dwarf_dwo_name(Dwarf_Die *die) +{ + Dwarf_Attribute attr_mem, *attr; + if ((attr = dwarf_attr(die, DW_AT_dwo_name, &attr_mem)) + || (attr = dwarf_attr(die, DW_AT_GNU_dwo_name, &attr_mem))) + return dwarf_formstring(attr); + return NULL; +} + static struct drgn_error * drgn_dwarf_index_read_cus(struct drgn_dwarf_index_state *state, - struct drgn_debug_info_module *module, - enum drgn_debug_info_scn scn) + struct drgn_elf_file *file, + enum drgn_section_index scn) { - struct drgn_dwarf_index_pending_cu_vector *cus = + struct drgn_error *err; + struct drgn_dwarf_index_cu_vector *cus = &state->cus[omp_get_thread_num()]; - struct drgn_error *err; - struct drgn_debug_info_buffer buffer; - drgn_debug_info_buffer_init(&buffer, module, scn); - while (binary_buffer_has_next(&buffer.bb)) { - struct drgn_dwarf_index_pending_cu *cu = - drgn_dwarf_index_pending_cu_vector_append_entry(cus); - if (!cu) - return &drgn_enomem; - cu->module = module; - cu->buf = buffer.bb.pos; - uint32_t unit_length32; - if ((err = binary_buffer_next_u32(&buffer.bb, &unit_length32))) - return err; - cu->is_64_bit = unit_length32 == UINT32_C(0xffffffff); - if (cu->is_64_bit) { - uint64_t unit_length64; - if ((err = binary_buffer_next_u64(&buffer.bb, - &unit_length64)) || - (err = binary_buffer_skip(&buffer.bb, - unit_length64))) - return err; + Dwarf_Off off = 0; + Dwarf_Off next_off; + size_t header_size; + Dwarf_Half version; + Dwarf_Off abbrev_offset; + uint8_t address_size; + uint8_t offset_size; + uint64_t v4_type_signature; + uint64_t *v4_type_signaturep = + scn == DRGN_SCN_DEBUG_TYPES ? &v4_type_signature : NULL; + int ret; + while ((ret = dwarf_next_unit(file->dwarf, off, &next_off, &header_size, + &version, &abbrev_offset, &address_size, + &offset_size, v4_type_signaturep, + NULL)) == 0) { + Dwarf_Die skeldie, cudie; + if (scn == DRGN_SCN_DEBUG_TYPES) { + if (!dwarf_offdie_types(file->dwarf, off + header_size, + &skeldie)) + return drgn_error_libdw(); } else { - if ((err = binary_buffer_skip(&buffer.bb, - unit_length32))) - return err; + if (!dwarf_offdie(file->dwarf, off + header_size, + &skeldie)) + return drgn_error_libdw(); } - cu->len = buffer.bb.pos - cu->buf; - cu->scn = scn; - } + struct drgn_elf_file *cu_file = file; + Dwarf_Off cu_start_off = off, cu_end_off = next_off; + uint8_t unit_type; +#if _ELFUTILS_PREREQ(0, 171) + if (dwarf_cu_info(skeldie.cu, NULL, &unit_type, &skeldie, + &cudie, NULL, NULL, NULL)) + return drgn_error_libdw(); + + if (unit_type == DW_UT_skeleton && cudie.cu) { + Dwarf *split_dwarf = dwarf_cu_getdwarf(cudie.cu); + cu_file = drgn_module_find_dwarf_file(file->module, + split_dwarf); + if (!cu_file) { + const char *dwo_name = + drgn_dwarf_dwo_name(&skeldie); + if (!dwo_name) + dwo_name = ""; + err = drgn_module_create_split_dwarf_file(file->module, + dwo_name, + split_dwarf, + &cu_file); + if (err) + return err; + } + + cu_start_off = (dwarf_dieoffset(&cudie) + - dwarf_cuoffset(&cudie)); + if (dwarf_next_unit(split_dwarf, cu_start_off, + &cu_end_off, NULL, &version, + &abbrev_offset, &address_size, + &offset_size, v4_type_signaturep, + NULL) + || dwarf_cu_info(cudie.cu, NULL, &unit_type, NULL, + NULL, NULL, NULL, NULL)) + return drgn_error_libdw(); + } else { + if (unit_type == DW_UT_skeleton + && drgn_log_is_enabled(state->dbinfo->prog, + DRGN_LOG_WARNING)) { + const char *dwo_name = + drgn_dwarf_dwo_name(&skeldie); + drgn_log_warning(state->dbinfo->prog, + "%s: split DWARF file%s%s not found", + file->path ?: "", + dwo_name ? " " : "", + dwo_name ? dwo_name : ""); + } + cudie = skeldie; + } +#else + unit_type = (scn == DRGN_SCN_DEBUG_TYPES + ? DW_UT_type : DW_UT_compile); + cudie = skeldie; +#endif + + if (!elf_data_contains_ptr(cu_file->scn_data[scn], + cudie.addr)) { + return drgn_elf_file_section_error(cu_file, NULL, NULL, + cudie.addr, + "unit DIE from unexpected section"); + } + if (cu_end_off > cu_file->scn_data[scn]->d_size) + cu_end_off = cu_file->scn_data[scn]->d_size; + const char *cu_buf = + (char *)cu_file->scn_data[scn]->d_buf + cu_start_off; + if (version < 2 || version > 5) { + return drgn_elf_file_section_errorf(cu_file, + cu_file->scns[scn], + cu_file->scn_data[scn], + cu_buf, + "unknown DWARF unit version %" PRIu16, + version); + } + if (address_size > 8) { + return drgn_elf_file_section_errorf(cu_file, + cu_file->scns[scn], + cu_file->scn_data[scn], + cu_buf, + "unsupported DWARF unit address size %" PRIu8, + address_size); + } + + Elf_Data *debug_abbrev = + cu_file->scn_data[DRGN_SCN_DEBUG_ABBREV]; + if (abbrev_offset > debug_abbrev->d_size) { + return drgn_elf_file_section_error(cu_file, + cu_file->scns[scn], + cu_file->scn_data[scn], + cu_buf, + "debug_abbrev_offset is out of bounds"); + } + const char *pending_abbrev = + (char *)debug_abbrev->d_buf + abbrev_offset; + + Elf_Data *debug_str_offsets = + cu_file->scn_data[DRGN_SCN_DEBUG_STR_OFFSETS]; + const char *str_offsets = NULL; + if (debug_str_offsets) { + Dwarf_Word str_offsets_base; + if (version >= 5) { + Dwarf_Attribute attr_mem, *attr; + if ((attr = dwarf_attr(&cudie, + DW_AT_str_offsets_base, + &attr_mem))) { + if (dwarf_formudata(attr, + &str_offsets_base)) + return drgn_error_libdw(); + if (str_offsets_base + > debug_str_offsets->d_size) { + return drgn_elf_file_section_error(cu_file, + cu_file->scns[scn], + cu_file->scn_data[scn], + (char *)attr->valp, + "DW_AT_str_offsets_base is out of bounds"); + } + } else { + // The default str_offsets_base is the + // first entry in .debug_str_offsets + // after the first header. (This isn't + // explicit in the DWARF 5 + // specification, but it seems to be the + // consensus.) + str_offsets_base = 2 * offset_size; + if (str_offsets_base + > debug_str_offsets->d_size) { + return drgn_elf_file_section_error(cu_file, + cu_file->scns[DRGN_SCN_DEBUG_STR_OFFSETS], + debug_str_offsets, + (char *)debug_str_offsets->d_buf + + debug_str_offsets->d_size, + ".debug_str_offsets is too small"); + } + } + } else { + // GNU Debug Fission doesn't have + // DW_AT_str_offsets_base; the base is always 0. + str_offsets_base = 0; + } + + str_offsets = + (char *)debug_str_offsets->d_buf + + str_offsets_base; + } + + struct drgn_dwarf_index_cu *cu = + drgn_dwarf_index_cu_vector_append_entry(cus); + if (!cu) + return &drgn_enomem; + *cu = (struct drgn_dwarf_index_cu){ + .file = cu_file, + .buf = cu_buf, + .len = cu_end_off - cu_start_off, + .version = version, + .unit_type = unit_type, + .address_size = address_size, + .is_64_bit = offset_size == 8, + .scn = scn, + .pending_abbrev = pending_abbrev, + .str_offsets = str_offsets, + .libdw_cu = cudie.cu, + }; + + off = next_off; + } + if (ret < 0) + return drgn_error_libdw(); return NULL; } struct drgn_error * drgn_dwarf_index_read_module(struct drgn_dwarf_index_state *state, - struct drgn_debug_info_module *module) + struct drgn_module *module) { struct drgn_error *err; - err = drgn_dwarf_index_read_cus(state, module, DRGN_SCN_DEBUG_INFO); - if (!err && module->scn_data[DRGN_SCN_DEBUG_TYPES]) { - err = drgn_dwarf_index_read_cus(state, module, + struct drgn_elf_file *file = module->debug_file; + err = drgn_dwarf_index_read_cus(state, file, DRGN_SCN_DEBUG_INFO); + if (!err && file->scn_data[DRGN_SCN_DEBUG_TYPES]) { + err = drgn_dwarf_index_read_cus(state, file, DRGN_SCN_DEBUG_TYPES); } return err; } +static struct drgn_error *read_strx(struct drgn_dwarf_index_cu_buffer *buffer, + uint64_t strx, const char **ret) +{ + if (!buffer->cu->str_offsets) { + return binary_buffer_error(&buffer->bb, + "string index without .debug_str_offsets section"); + } + Elf_Data *debug_str_offsets = + buffer->cu->file->scn_data[DRGN_SCN_DEBUG_STR_OFFSETS]; + size_t offset_size = buffer->cu->is_64_bit ? 8 : 4; + if (((char *)debug_str_offsets->d_buf + debug_str_offsets->d_size + - buffer->cu->str_offsets) + / offset_size <= strx) { + return binary_buffer_error(&buffer->bb, + "string index out of bounds"); + } + uint64_t strp; + if (buffer->cu->is_64_bit) { + memcpy(&strp, (uint64_t *)buffer->cu->str_offsets + strx, + sizeof(strp)); + if (buffer->bb.bswap) + strp = bswap_64(strp); + } else { + uint32_t strp32; + memcpy(&strp32, (uint32_t *)buffer->cu->str_offsets + strx, + sizeof(strp32)); + if (buffer->bb.bswap) + strp32 = bswap_32(strp32); + strp = strp32; + } + if (strp >= buffer->cu->file->scn_data[DRGN_SCN_DEBUG_STR]->d_size) { + return binary_buffer_error(&buffer->bb, + "indirect string is out of bounds"); + } + *ret = ((char *)buffer->cu->file->scn_data[DRGN_SCN_DEBUG_STR]->d_buf + + strp); + return NULL; +} + static struct drgn_error *dw_form_to_insn(struct drgn_dwarf_index_cu *cu, struct binary_buffer *bb, uint64_t form, uint8_t *insn_ret) @@ -668,6 +698,8 @@ static struct drgn_error *dw_form_to_insn(struct drgn_dwarf_index_cu *cu, case DW_FORM_addrx: case DW_FORM_loclistx: case DW_FORM_rnglistx: + case DW_FORM_GNU_addr_index: + case DW_FORM_GNU_str_index: *insn_ret = INSN_SKIP_LEB128; return NULL; case DW_FORM_ref_addr: @@ -675,7 +707,7 @@ static struct drgn_error *dw_form_to_insn(struct drgn_dwarf_index_cu *cu, *insn_ret = cu->address_size; return NULL; } - /* fallthrough */ + fallthrough; case DW_FORM_sec_offset: case DW_FORM_strp: case DW_FORM_strp_sup: @@ -690,7 +722,7 @@ static struct drgn_error *dw_form_to_insn(struct drgn_dwarf_index_cu *cu, case DW_FORM_implicit_const: if ((err = binary_buffer_skip_leb128(bb))) return err; - /* fallthrough */ + fallthrough; case DW_FORM_flag_present: *insn_ret = 0; return NULL; @@ -740,7 +772,7 @@ static struct drgn_error *dw_at_name_to_insn(struct drgn_dwarf_index_cu *cu, { switch (form) { case DW_FORM_strp: - if (!cu->module->scn_data[DRGN_SCN_DEBUG_STR]) { + if (!cu->file->scn_data[DRGN_SCN_DEBUG_STR]) { return binary_buffer_error(bb, "DW_FORM_strp without .debug_str section"); } @@ -753,6 +785,7 @@ static struct drgn_error *dw_at_name_to_insn(struct drgn_dwarf_index_cu *cu, *insn_ret = INSN_NAME_STRING; return NULL; case DW_FORM_strx: + case DW_FORM_GNU_str_index: *insn_ret = INSN_NAME_STRX; return NULL; case DW_FORM_strx1: @@ -768,7 +801,7 @@ static struct drgn_error *dw_at_name_to_insn(struct drgn_dwarf_index_cu *cu, *insn_ret = INSN_NAME_STRX4; return NULL; case DW_FORM_GNU_strp_alt: - if (!cu->module->alt_debug_str_data) { + if (!cu->file->alt_debug_str_data) { return binary_buffer_error(bb, "DW_FORM_GNU_strp_alt without alternate .debug_str section"); } @@ -787,260 +820,94 @@ static struct drgn_error *dw_at_name_to_insn(struct drgn_dwarf_index_cu *cu, } } -static struct drgn_error *dw_at_comp_dir_to_insn(struct drgn_dwarf_index_cu *cu, - struct binary_buffer *bb, - uint64_t form, - uint8_t *insn_ret) +static struct drgn_error * +dw_at_declaration_to_insn(struct binary_buffer *bb, uint64_t form, + uint8_t *insn_ret, uint8_t *die_flags) { switch (form) { - case DW_FORM_strp: - if (!cu->module->scn_data[DRGN_SCN_DEBUG_STR]) { - return binary_buffer_error(bb, - "DW_FORM_strp without .debug_str section"); - } - if (cu->is_64_bit) - *insn_ret = INSN_COMP_DIR_STRP8; - else - *insn_ret = INSN_COMP_DIR_STRP4; + case DW_FORM_flag: + *insn_ret = INSN_DECLARATION_FLAG; return NULL; - case DW_FORM_line_strp: - if (!cu->module->scn_data[DRGN_SCN_DEBUG_LINE_STR]) { - return binary_buffer_error(bb, - "DW_FORM_line_strp without .debug_line_str section"); - } - if (cu->is_64_bit) - *insn_ret = INSN_COMP_DIR_LINE_STRP8; - else - *insn_ret = INSN_COMP_DIR_LINE_STRP4; + case DW_FORM_flag_present: + /* + * This could be an instruction, but as long as we have a free + * DIE flag bit, we might as well use it. + */ + *insn_ret = 0; + *die_flags |= INSN_DIE_FLAG_DECLARATION; return NULL; - case DW_FORM_string: - *insn_ret = INSN_COMP_DIR_STRING; + case DW_FORM_indirect: + *insn_ret = INSN_DECLARATION_INDIRECT; return NULL; - case DW_FORM_strx: - *insn_ret = INSN_COMP_DIR_STRX; + default: + return binary_buffer_error(bb, + "unknown attribute form %#" PRIx64 " for DW_AT_declaration", + form); + } +} + +static struct drgn_error * +dw_at_specification_to_insn(struct drgn_dwarf_index_cu *cu, + struct binary_buffer *bb, uint64_t form, + uint8_t *insn_ret) +{ + switch (form) { + case DW_FORM_ref1: + *insn_ret = INSN_SPECIFICATION_REF1; return NULL; - case DW_FORM_strx1: - *insn_ret = INSN_COMP_DIR_STRX1; + case DW_FORM_ref2: + *insn_ret = INSN_SPECIFICATION_REF2; return NULL; - case DW_FORM_strx2: - *insn_ret = INSN_COMP_DIR_STRX2; + case DW_FORM_ref4: + *insn_ret = INSN_SPECIFICATION_REF4; return NULL; - case DW_FORM_strx3: - *insn_ret = INSN_COMP_DIR_STRX3; + case DW_FORM_ref8: + *insn_ret = INSN_SPECIFICATION_REF8; return NULL; - case DW_FORM_strx4: - *insn_ret = INSN_COMP_DIR_STRX4; + case DW_FORM_ref_udata: + *insn_ret = INSN_SPECIFICATION_REF_UDATA; return NULL; - case DW_FORM_GNU_strp_alt: - if (!cu->module->alt_debug_str_data) { + case DW_FORM_ref_addr: + if (cu->version >= 3) { + if (cu->is_64_bit) + *insn_ret = INSN_SPECIFICATION_REF_ADDR8; + else + *insn_ret = INSN_SPECIFICATION_REF_ADDR4; + } else { + if (cu->address_size == 8) + *insn_ret = INSN_SPECIFICATION_REF_ADDR8; + else if (cu->address_size == 4) + *insn_ret = INSN_SPECIFICATION_REF_ADDR4; + else + return binary_buffer_error(bb, + "unsupported address size %" PRIu8 " for DW_FORM_ref_addr", + cu->address_size); + } + return NULL; + case DW_FORM_GNU_ref_alt: + if (!cu->file->alt_debug_info_data) { return binary_buffer_error(bb, - "DW_FORM_GNU_strp_alt without alternate .debug_str section"); + "DW_FORM_GNU_ref_alt without alternate .debug_info section"); } if (cu->is_64_bit) - *insn_ret = INSN_COMP_DIR_STRP_ALT8; + *insn_ret = INSN_SPECIFICATION_REF_ALT8; else - *insn_ret = INSN_COMP_DIR_STRP_ALT4; + *insn_ret = INSN_SPECIFICATION_REF_ALT4; return NULL; case DW_FORM_indirect: - *insn_ret = INSN_COMP_DIR_INDIRECT; + *insn_ret = INSN_SPECIFICATION_INDIRECT; return NULL; default: return binary_buffer_error(bb, - "unknown attribute form %#" PRIx64 " for DW_AT_comp_dir", + "unknown attribute form %#" PRIx64 " for DW_AT_specification", form); } } static struct drgn_error * -dw_at_str_offsets_base_to_insn(struct drgn_dwarf_index_cu *cu, - struct binary_buffer *bb, uint64_t form, - uint8_t *insn_ret) -{ - switch (form) { - case DW_FORM_sec_offset: - if (cu->is_64_bit) - *insn_ret = INSN_STR_OFFSETS_BASE8; - else - *insn_ret = INSN_STR_OFFSETS_BASE4; - return NULL; - case DW_FORM_indirect: - *insn_ret = INSN_STR_OFFSETS_BASE_INDIRECT; - return NULL; - default: - return binary_buffer_error(bb, - "unknown attribute form %#" PRIx64 " for DW_AT_str_offsets_base", - form); - } -} - -static struct drgn_error * -dw_at_stmt_list_to_insn(struct drgn_dwarf_index_cu *cu, - struct binary_buffer *bb, uint64_t form, - uint8_t *insn_ret) -{ - switch (form) { - case DW_FORM_data4: - *insn_ret = INSN_STMT_LIST_LINEPTR4; - return NULL; - case DW_FORM_data8: - *insn_ret = INSN_STMT_LIST_LINEPTR8; - return NULL; - case DW_FORM_sec_offset: - if (cu->is_64_bit) - *insn_ret = INSN_STMT_LIST_LINEPTR8; - else - *insn_ret = INSN_STMT_LIST_LINEPTR4; - return NULL; - case DW_FORM_indirect: - *insn_ret = INSN_STMT_LIST_INDIRECT; - return NULL; - default: - return binary_buffer_error(bb, - "unknown attribute form %#" PRIx64 " for DW_AT_stmt_list", - form); - } -} - -static struct drgn_error *dw_at_decl_file_to_insn(struct binary_buffer *bb, - uint64_t form, - uint8_t *insn_ret, - uint64_t *implicit_const_ret) -{ - switch (form) { - case DW_FORM_data1: - *insn_ret = INSN_DECL_FILE_DATA1; - return NULL; - case DW_FORM_data2: - *insn_ret = INSN_DECL_FILE_DATA2; - return NULL; - case DW_FORM_data4: - *insn_ret = INSN_DECL_FILE_DATA4; - return NULL; - case DW_FORM_data8: - *insn_ret = INSN_DECL_FILE_DATA8; - return NULL; - /* - * decl_file must be positive, so if the compiler uses - * DW_FORM_sdata for some reason, just treat it as udata. - */ - case DW_FORM_sdata: - case DW_FORM_udata: - *insn_ret = INSN_DECL_FILE_UDATA; - return NULL; - case DW_FORM_implicit_const: - *insn_ret = INSN_DECL_FILE_IMPLICIT; - return binary_buffer_next_uleb128(bb, implicit_const_ret); - case DW_FORM_indirect: - *insn_ret = INSN_DECL_FILE_INDIRECT; - return NULL; - default: - return binary_buffer_error(bb, - "unknown attribute form %#" PRIx64 " for DW_AT_decl_file", - form); - } -} - -static struct drgn_error * -dw_at_declaration_to_insn(struct binary_buffer *bb, uint64_t form, - uint8_t *insn_ret, uint8_t *die_flags) -{ - switch (form) { - case DW_FORM_flag: - *insn_ret = INSN_DECLARATION_FLAG; - return NULL; - case DW_FORM_flag_present: - /* - * This could be an instruction, but as long as we have a free - * DIE flag bit, we might as well use it. - */ - *insn_ret = 0; - *die_flags |= INSN_DIE_FLAG_DECLARATION; - return NULL; - case DW_FORM_indirect: - *insn_ret = INSN_DECLARATION_INDIRECT; - return NULL; - default: - return binary_buffer_error(bb, - "unknown attribute form %#" PRIx64 " for DW_AT_declaration", - form); - } -} - -static struct drgn_error * -dw_at_specification_to_insn(struct drgn_dwarf_index_cu *cu, - struct binary_buffer *bb, uint64_t form, - uint8_t *insn_ret) -{ - switch (form) { - case DW_FORM_ref1: - *insn_ret = INSN_SPECIFICATION_REF1; - return NULL; - case DW_FORM_ref2: - *insn_ret = INSN_SPECIFICATION_REF2; - return NULL; - case DW_FORM_ref4: - *insn_ret = INSN_SPECIFICATION_REF4; - return NULL; - case DW_FORM_ref8: - *insn_ret = INSN_SPECIFICATION_REF8; - return NULL; - case DW_FORM_ref_udata: - *insn_ret = INSN_SPECIFICATION_REF_UDATA; - return NULL; - case DW_FORM_ref_addr: - if (cu->version >= 3) { - if (cu->is_64_bit) - *insn_ret = INSN_SPECIFICATION_REF_ADDR8; - else - *insn_ret = INSN_SPECIFICATION_REF_ADDR4; - } else { - if (cu->address_size == 8) - *insn_ret = INSN_SPECIFICATION_REF_ADDR8; - else if (cu->address_size == 4) - *insn_ret = INSN_SPECIFICATION_REF_ADDR4; - else - return binary_buffer_error(bb, - "unsupported address size %" PRIu8 " for DW_FORM_ref_addr", - cu->address_size); - } - return NULL; - case DW_FORM_GNU_ref_alt: - if (!cu->module->alt_debug_info_data) { - return binary_buffer_error(bb, - "DW_FORM_GNU_ref_alt without alternate .debug_info section"); - } - if (cu->is_64_bit) - *insn_ret = INSN_SPECIFICATION_REF_ALT8; - else - *insn_ret = INSN_SPECIFICATION_REF_ALT4; - return NULL; - case DW_FORM_indirect: - *insn_ret = INSN_SPECIFICATION_INDIRECT; - return NULL; - default: - return binary_buffer_error(bb, - "unknown attribute form %#" PRIx64 " for DW_AT_specification", - form); - } -} - -static bool append_uleb128(struct uint8_vector *insns, uint64_t value) -{ - do { - uint8_t byte = value & 0x7f; - value >>= 7; - if (value != 0) - byte |= 0x80; - if (!uint8_vector_append(insns, &byte)) - return false; - } while (value != 0); - return true; -} - -static struct drgn_error * -read_abbrev_decl(struct drgn_debug_info_buffer *buffer, - struct drgn_dwarf_index_cu *cu, struct uint32_vector *decls, - struct uint8_vector *insns) +read_abbrev_decl(struct drgn_elf_file_section_buffer *buffer, + struct drgn_dwarf_index_cu *cu, struct uint32_vector *decls, + struct uint8_vector *insns) { struct drgn_error *err; @@ -1049,12 +916,12 @@ read_abbrev_decl(struct drgn_debug_info_buffer *buffer, return err; if (code == 0) return &drgn_stop; - if (code != decls->size + 1) { + if (code != uint32_vector_size(decls) + 1) { return binary_buffer_error(&buffer->bb, "DWARF abbreviation table is not sequential"); } - uint32_t insn_index = insns->size; + uint32_t insn_index = uint8_vector_size(insns); if (!uint32_vector_append(decls, &insn_index)) return &drgn_enomem; @@ -1062,31 +929,17 @@ read_abbrev_decl(struct drgn_debug_info_buffer *buffer, if ((err = binary_buffer_next_uleb128(&buffer->bb, &tag))) return err; - bool should_index; + uint8_t die_flags; + bool should_index = true; switch (tag) { - /* Types. */ - case DW_TAG_base_type: - case DW_TAG_class_type: - case DW_TAG_enumeration_type: - case DW_TAG_structure_type: - case DW_TAG_typedef: - case DW_TAG_union_type: - /* Variables. */ - case DW_TAG_variable: - /* Constants. */ - case DW_TAG_enumerator: - /* Functions. */ - case DW_TAG_subprogram: - /* Namespaces */ - case DW_TAG_namespace: - /* If adding anything here, make sure it fits in INSN_DIE_FLAG_TAG_MASK. */ - should_index = true; - break; +#define X(name) case DW_TAG_##name: die_flags = DRGN_DWARF_INDEX_##name; break; + DRGN_DWARF_INDEX_TAGS +#undef X default: + die_flags = INSN_DIE_FLAG_TAG_MASK; should_index = false; break; } - uint8_t die_flags = should_index ? tag : 0; uint8_t children; if ((err = binary_buffer_next_u8(&buffer->bb, &children))) @@ -1097,7 +950,6 @@ read_abbrev_decl(struct drgn_debug_info_buffer *buffer, uint8_t insn, last_insn = UINT8_MAX; for (;;) { uint64_t name, form; - uint64_t implicit_const; if ((err = binary_buffer_next_uleb128(&buffer->bb, &name))) return err; if ((err = binary_buffer_next_uleb128(&buffer->bb, &form))) @@ -1109,28 +961,6 @@ read_abbrev_decl(struct drgn_debug_info_buffer *buffer, err = dw_at_sibling_to_insn(&buffer->bb, form, &insn); } else if (name == DW_AT_name && should_index) { err = dw_at_name_to_insn(cu, &buffer->bb, form, &insn); - } else if (name == DW_AT_comp_dir) { - err = dw_at_comp_dir_to_insn(cu, &buffer->bb, form, - &insn); - } else if (name == DW_AT_str_offsets_base) { - if (!cu->module->scn_data[DRGN_SCN_DEBUG_STR_OFFSETS]) { - return binary_buffer_error(&buffer->bb, - "DW_AT_str_offsets_base without .debug_str_offsets section"); - } - err = dw_at_str_offsets_base_to_insn(cu, &buffer->bb, - form, &insn); - } else if (name == DW_AT_stmt_list) { - if (!cu->module->scn_data[DRGN_SCN_DEBUG_LINE]) { - return binary_buffer_error(&buffer->bb, - "DW_AT_stmt_list without .debug_line section"); - } - err = dw_at_stmt_list_to_insn(cu, &buffer->bb, form, - &insn); - } else if (name == DW_AT_decl_file && should_index && - /* Namespaces are merged, so we ignore their file. */ - tag != DW_TAG_namespace) { - err = dw_at_decl_file_to_insn(&buffer->bb, form, &insn, - &implicit_const); } else if (name == DW_AT_declaration && should_index) { err = dw_at_declaration_to_insn(&buffer->bb, form, &insn, &die_flags); @@ -1146,21 +976,17 @@ read_abbrev_decl(struct drgn_debug_info_buffer *buffer, if (insn != 0) { if (insn <= INSN_MAX_SKIP) { if (last_insn + insn <= INSN_MAX_SKIP) { - insns->data[insns->size - 1] += insn; + *uint8_vector_last(insns) += insn; continue; } else if (last_insn < INSN_MAX_SKIP) { insn = last_insn + insn - INSN_MAX_SKIP; - insns->data[insns->size - 1] = INSN_MAX_SKIP; + *uint8_vector_last(insns) = INSN_MAX_SKIP; } } last_insn = insn; if (!uint8_vector_append(insns, &insn)) return &drgn_enomem; - - if (insn == INSN_DECL_FILE_IMPLICIT && - !append_uleb128(insns, implicit_const)) - return &drgn_enomem; } } insn = INSN_END; @@ -1170,13 +996,12 @@ read_abbrev_decl(struct drgn_debug_info_buffer *buffer, return NULL; } -static struct drgn_error *read_abbrev_table(struct drgn_dwarf_index_cu *cu, - size_t debug_abbrev_offset) +static struct drgn_error *read_cu(struct drgn_dwarf_index_cu *cu) { - struct drgn_debug_info_buffer buffer; - drgn_debug_info_buffer_init(&buffer, cu->module, DRGN_SCN_DEBUG_ABBREV); - /* Checked in read_cu(). */ - buffer.bb.pos += debug_abbrev_offset; + struct drgn_elf_file_section_buffer buffer; + drgn_elf_file_section_buffer_init_index(&buffer, cu->file, + DRGN_SCN_DEBUG_ABBREV); + buffer.bb.pos = cu->pending_abbrev; struct uint32_vector decls = VECTOR_INIT; struct uint8_vector insns = VECTOR_INIT; for (;;) { @@ -1192,9 +1017,8 @@ static struct drgn_error *read_abbrev_table(struct drgn_dwarf_index_cu *cu, } uint8_vector_shrink_to_fit(&insns); uint32_vector_shrink_to_fit(&decls); - cu->abbrev_decls = decls.data; - cu->num_abbrev_decls = decls.size; - cu->abbrev_insns = insns.data; + uint32_vector_steal(&decls, &cu->abbrev_decls, &cu->num_abbrev_decls); + uint8_vector_steal(&insns, &cu->abbrev_insns, NULL); return NULL; } @@ -1208,7 +1032,7 @@ static size_t cu_header_extra_size(struct drgn_dwarf_index_cu *cu) case DW_UT_skeleton: case DW_UT_split_compile: /* dwo_id */ - return 8; + return cu->version >= 5 ? 8 : 0; case DW_UT_type: case DW_UT_split_type: /* type_signature and type_offset */ @@ -1227,688 +1051,19 @@ static size_t cu_header_size(struct drgn_dwarf_index_cu *cu) return size; } -static struct drgn_error *read_cu(struct drgn_dwarf_index_cu_buffer *buffer) -{ - struct drgn_error *err; - buffer->bb.pos += buffer->cu->is_64_bit ? 12 : 4; - uint16_t version; - if ((err = binary_buffer_next_u16(&buffer->bb, &version))) - return err; - if (version < 2 || version > 5) { - return binary_buffer_error(&buffer->bb, - "unknown DWARF CU version %" PRIu16, - version); - } - buffer->cu->version = version; - - if (version >= 5) { - if ((err = binary_buffer_next_u8(&buffer->bb, - &buffer->cu->unit_type))) - return err; - if (buffer->cu->unit_type < DW_UT_compile || - buffer->cu->unit_type > DW_UT_split_type) { - return binary_buffer_error(&buffer->bb, - "unknown DWARF unit type"); - } - } else if (buffer->cu->scn == DRGN_SCN_DEBUG_TYPES) { - buffer->cu->unit_type = DW_UT_type; - } else { - buffer->cu->unit_type = DW_UT_compile; - } - - if (version >= 5 && - (err = binary_buffer_next_u8(&buffer->bb, - &buffer->cu->address_size))) - return err; - - uint64_t debug_abbrev_offset; - if (buffer->cu->is_64_bit) { - if ((err = binary_buffer_next_u64(&buffer->bb, - &debug_abbrev_offset))) - return err; - } else { - if ((err = binary_buffer_next_u32_into_u64(&buffer->bb, - &debug_abbrev_offset))) - return err; - } - if (debug_abbrev_offset > - buffer->cu->module->scn_data[DRGN_SCN_DEBUG_ABBREV]->d_size) { - return binary_buffer_error(&buffer->bb, - "debug_abbrev_offset is out of bounds"); - } - - if (version < 5 && - (err = binary_buffer_next_u8(&buffer->bb, - &buffer->cu->address_size))) - return err; - if (buffer->cu->address_size > 8) { - return binary_buffer_error(&buffer->bb, - "unsupported address size %" PRIu8, - buffer->cu->address_size); - } - - if ((err = binary_buffer_skip(&buffer->bb, - cu_header_extra_size(buffer->cu)))) - return err; - - return read_abbrev_table(buffer->cu, debug_abbrev_offset); -} - -static struct drgn_error *read_strx(struct drgn_dwarf_index_cu_buffer *buffer, - uint64_t strx, const char **ret) -{ - if (!buffer->cu->str_offsets) { - return binary_buffer_error(&buffer->bb, - "string index without DW_AT_str_offsets_base"); - } - Elf_Data *debug_str_offsets = - buffer->cu->module->scn_data[DRGN_SCN_DEBUG_STR_OFFSETS]; - size_t offset_size = buffer->cu->is_64_bit ? 8 : 4; - if (((char *)debug_str_offsets->d_buf + debug_str_offsets->d_size - - buffer->cu->str_offsets) - / offset_size <= strx) { - return binary_buffer_error(&buffer->bb, - "string index out of bounds"); - } - uint64_t strp; - if (buffer->cu->is_64_bit) { - memcpy(&strp, (uint64_t *)buffer->cu->str_offsets + strx, - sizeof(strp)); - if (buffer->bb.bswap) - strp = bswap_64(strp); - } else { - uint32_t strp32; - memcpy(&strp32, (uint32_t *)buffer->cu->str_offsets + strx, - sizeof(strp32)); - if (buffer->bb.bswap) - strp32 = bswap_32(strp32); - strp = strp32; - } - if (strp >= buffer->cu->module->scn_data[DRGN_SCN_DEBUG_STR]->d_size) { - return binary_buffer_error(&buffer->bb, - "indirect string is out of bounds"); - } - *ret = ((char *)buffer->cu->module->scn_data[DRGN_SCN_DEBUG_STR]->d_buf - + strp); - return NULL; -} - -static struct drgn_error *read_lnp_header(struct drgn_debug_info_buffer *buffer, - bool *is_64_bit_ret, int *version_ret) -{ - struct drgn_error *err; - uint32_t tmp; - if ((err = binary_buffer_next_u32(&buffer->bb, &tmp))) - return err; - bool is_64_bit = tmp == UINT32_C(0xffffffff); - if (is_64_bit && - (err = binary_buffer_skip(&buffer->bb, sizeof(uint64_t)))) - return err; - *is_64_bit_ret = is_64_bit; - - uint16_t version; - if ((err = binary_buffer_next_u16(&buffer->bb, &version))) - return err; - if (version < 2 || version > 5) { - return binary_buffer_error(&buffer->bb, - "unknown DWARF LNP version %" PRIu16, - version); - } - *version_ret = version; - - uint8_t opcode_base; - if ((err = binary_buffer_skip(&buffer->bb, - /* address_size + segment_selector_size */ - + (version >= 5 ? 2 : 0) - + (is_64_bit ? 8 : 4) /* header_length */ - + 1 /* minimum_instruction_length */ - + (version >= 4) /* maximum_operations_per_instruction */ - + 1 /* default_is_stmt */ - + 1 /* line_base */ - + 1 /* line_range */)) || - (err = binary_buffer_next_u8(&buffer->bb, &opcode_base)) || - (err = binary_buffer_skip(&buffer->bb, opcode_base - 1))) - return err; - - return NULL; -} - -/** - * Cached hash of file path. - * - * File names in the DWARF line number program header consist of three parts: - * the compilation directory path, the directory path, and the file name. - * Multiple file names may be relative to the same directory, and relative - * directory paths are all relative to the compilation directory. - * - * We'd like to hash DWARF file names to a unique hash so that we can - * deduplicate definitions without comparing full paths. - * - * The naive way to hash a DWARF file name entry would be to join and normalize - * the compilation directory path, directory path, and file name, and hash that. - * But this would involve a lot of redundant computations since most paths will - * have common prefixes. Instead, we cache the hashes of each directory path and - * update the hash for relative paths. - * - * It is not sufficient to cache the final hash for each directory because ".." - * components may require us to use the hash of a parent directory. So, we also - * cache the hash of every parent directory in a linked list. - * - * We use the FNV-1a hash function. Although FNV-1a is - * [known](https://github.com/rurban/smhasher/blob/master/doc/FNV1a.txt) to have - * some hash quality problems, it is sufficient for producing unique 64-bit - * hashes of file names. It has a couple of advantages over "better" hash - * functions: - * - * 1. Its only internal state is the 64-bit hash value, which keeps this - * structure small. - * 2. It operates byte-by-byte, which works well for incrementally hashing lots - * of short path components. - */ -struct path_hash { - /** Hash of this path. */ - uint64_t hash; - /** - * Tagged pointer comprising `struct path_hash *` of parent directory - * and flag in lowest-order bit specifying whether this path ends in a - * ".." component. - */ - uintptr_t parent_and_is_dot_dot; -}; - -#define FNV_OFFSET_BASIS_64 UINT64_C(0xcbf29ce484222325) -#define FNV_PRIME_64 UINT64_C(0x00000100000001b3) - -static inline void path_hash_update(struct path_hash *path_hash, - const void *src, size_t len) -{ - const uint8_t *s = src, *end = s + len; - uint64_t hash = path_hash->hash; - while (s < end) { - hash ^= *(s++); - hash *= FNV_PRIME_64; - } - path_hash->hash = hash; -} - -/** Path hash of "" (empty string). */ -static const struct path_hash empty_path_hash = { FNV_OFFSET_BASIS_64 }; -/** Path hash of "/". */ -static const struct path_hash absolute_path_hash = { - (FNV_OFFSET_BASIS_64 ^ '/') * FNV_PRIME_64, -}; - -static inline const struct path_hash * -path_hash_parent(const struct path_hash *path_hash) -{ - return (struct path_hash *)(path_hash->parent_and_is_dot_dot - & ~(uintptr_t)1); -} - -static inline bool path_hash_is_dot_dot(const struct path_hash *path_hash) -{ - return path_hash->parent_and_is_dot_dot & 1; -} - -/** Chunk of allocated @ref path_hash objects. See @ref path_hash_cache. */ -struct path_hash_chunk { - struct path_hash objects[(4096 - sizeof(struct path_hash_chunk *)) - / sizeof(struct path_hash)]; - struct path_hash_chunk *next; -}; - -DEFINE_VECTOR(path_hash_vector, const struct path_hash *) - -struct lnp_entry_format { - uint64_t content_type; - uint64_t form; -}; - -static const struct lnp_entry_format dwarf4_directory_entry_formats[] = { - { DW_LNCT_path, DW_FORM_string }, -}; -static const struct lnp_entry_format dwarf4_file_name_entry_formats[] = { - { DW_LNCT_path, DW_FORM_string }, - { DW_LNCT_directory_index, DW_FORM_udata }, - { DW_LNCT_timestamp, DW_FORM_udata }, - { DW_LNCT_size, DW_FORM_udata }, -}; - -/** - * Cache of hashed file paths. - * - * This uses a bump allocator for @ref path_hash objects. @ref path_hash objects - * are allocated sequentially out of a @ref path_hash_chunk; when a chunk is - * exhausted, a new @ref path_hash_chunk is allocated from the heap. The - * allocated chunks are kept and reused for each DWARF line number program; they - * are freed at the end of the first indexing pass. - * - * This also caches the allocations for directory hashes and line number program - * header entry formats. - */ -struct path_hash_cache { - /** Next @ref path_hash object to be allocated. */ - struct path_hash *next_object; - /** @ref path_hash_chunk currently being allocated from. */ - struct path_hash_chunk *current_chunk; - /** First allocated @ref path_hash_chunk. */ - struct path_hash_chunk *first_chunk; - /** Hashed directory paths. */ - struct path_hash_vector directories; - /** Line number program header entry formats. */ - struct lnp_entry_format *entry_formats; - /** Allocated size of @ref path_hash_cache::entry_formats. */ - size_t entry_formats_capacity; -}; - -static struct path_hash *path_hash_alloc(struct path_hash_cache *cache) -{ - struct path_hash_chunk *current_chunk = cache->current_chunk; - if (cache->next_object < - ¤t_chunk->objects[array_size(current_chunk->objects)]) - return cache->next_object++; - struct path_hash_chunk *next_chunk = current_chunk->next; - if (!next_chunk) { - next_chunk = malloc(sizeof(*next_chunk)); - if (!next_chunk) - return NULL; - next_chunk->next = NULL; - current_chunk->next = next_chunk; - } - cache->current_chunk = next_chunk; - cache->next_object = &next_chunk->objects[1]; - return next_chunk->objects; -} - -static inline bool is_dot_dot(const char *component, size_t component_len) -{ - return component_len == 2 && component[0] == '.' && component[1] == '.'; -} - -static const struct path_hash *hash_path(struct path_hash_cache *cache, - const char *path, - const struct path_hash *path_hash) -{ - const char *p = path; - if (*p == '/') { - path_hash = &absolute_path_hash; - p++; - } - while (*p != '\0') { - const char *component = p; - p = strchrnul(p, '/'); - size_t component_len = p - component; - if (*p == '/') - p++; - if (component_len == 0 || - (component_len == 1 && component[0] == '.')) { - } else if (!is_dot_dot(component, component_len) || - path_hash == &empty_path_hash || - path_hash_is_dot_dot(path_hash)) { - struct path_hash *new_path_hash = path_hash_alloc(cache); - if (!new_path_hash) - return NULL; - new_path_hash->hash = path_hash->hash; - if (path_hash->parent_and_is_dot_dot != 0) - path_hash_update(new_path_hash, "/", 1); - path_hash_update(new_path_hash, component, - component_len); - new_path_hash->parent_and_is_dot_dot = - ((uintptr_t)path_hash | - is_dot_dot(component, component_len)); - path_hash = new_path_hash; - } else if (path_hash != &absolute_path_hash) { - path_hash = path_hash_parent(path_hash); - } - } - return path_hash; -} - -static struct drgn_error * -read_lnp_entry_formats(struct drgn_debug_info_buffer *buffer, - struct path_hash_cache *cache, int *count_ret) -{ - struct drgn_error *err; - uint8_t count; - if ((err = binary_buffer_next_u8(&buffer->bb, &count))) - return err; - if (count > cache->entry_formats_capacity) { - free(cache->entry_formats); - cache->entry_formats = malloc_array(count, - sizeof(cache->entry_formats[0])); - if (!cache->entry_formats) { - cache->entry_formats_capacity = 0; - return &drgn_enomem; - } - cache->entry_formats_capacity = count; - } - bool have_path = false; - for (int i = 0; i < count; i++) { - if ((err = binary_buffer_next_uleb128(&buffer->bb, - &cache->entry_formats[i].content_type))) - return err; - if (cache->entry_formats[i].content_type == DW_LNCT_path) - have_path = true; - if ((err = binary_buffer_next_uleb128(&buffer->bb, - &cache->entry_formats[i].form))) - return err; - } - if (!have_path) { - return binary_buffer_error(&buffer->bb, - "DWARF line number program header entry does not include DW_LNCT_path"); - } - *count_ret = count; - return NULL; -} - -static struct drgn_error *skip_lnp_form(struct binary_buffer *bb, - bool is_64_bit, uint64_t form) -{ - struct drgn_error *err; - uint64_t skip; - switch (form) { - case DW_FORM_block: - if ((err = binary_buffer_next_uleb128(bb, &skip))) - return err; -block: - return binary_buffer_skip(bb, skip); - case DW_FORM_block1: - if ((err = binary_buffer_next_u8_into_u64(bb, &skip))) - return err; - goto block; - case DW_FORM_block2: - if ((err = binary_buffer_next_u16_into_u64(bb, &skip))) - return err; - goto block; - case DW_FORM_block4: - if ((err = binary_buffer_next_u32_into_u64(bb, &skip))) - return err; - goto block; - case DW_FORM_data1: - case DW_FORM_flag: - case DW_FORM_strx1: - return binary_buffer_skip(bb, 1); - case DW_FORM_data2: - case DW_FORM_strx2: - return binary_buffer_skip(bb, 2); - case DW_FORM_strx3: - return binary_buffer_skip(bb, 3); - case DW_FORM_data4: - case DW_FORM_strx4: - return binary_buffer_skip(bb, 4); - case DW_FORM_data8: - return binary_buffer_skip(bb, 8); - case DW_FORM_data16: - return binary_buffer_skip(bb, 16); - case DW_FORM_line_strp: - case DW_FORM_sec_offset: - case DW_FORM_strp: - return binary_buffer_skip(bb, is_64_bit ? 8 : 4); - case DW_FORM_sdata: - case DW_FORM_strx: - case DW_FORM_udata: - return binary_buffer_skip_leb128(bb); - case DW_FORM_string: - return binary_buffer_skip_string(bb); - default: - return binary_buffer_error(bb, - "unknown attribute form %#" PRIx64 " for line number program", - form); - } -} - -static struct drgn_error *read_lnp_string(struct drgn_debug_info_buffer *buffer, - bool is_64_bit, uint64_t form, - const char **ret) -{ - struct drgn_error *err; - uint64_t strp; - Elf_Data *data; - switch (form) { - case DW_FORM_string: - *ret = buffer->bb.pos; - return binary_buffer_skip_string(&buffer->bb); - case DW_FORM_line_strp: - case DW_FORM_strp: - if (is_64_bit) - err = binary_buffer_next_u64(&buffer->bb, &strp); - else - err = binary_buffer_next_u32_into_u64(&buffer->bb, &strp); - if (err) - return err; - data = buffer->module->scn_data[ - form == DW_FORM_line_strp ? - DRGN_SCN_DEBUG_LINE_STR : DRGN_SCN_DEBUG_STR]; - if (!data || strp >= data->d_size) { - return binary_buffer_error(&buffer->bb, - "DW_LNCT_path is out of bounds"); - } - *ret = (const char *)data->d_buf + strp; - return NULL; - default: - return binary_buffer_error(&buffer->bb, - "unknown attribute form %#" PRIx64 " for DW_LNCT_path", - form); - } -} - -static struct drgn_error * -read_lnp_directory_index(struct drgn_debug_info_buffer *buffer, uint64_t form, - uint64_t *ret) -{ - switch (form) { - case DW_FORM_data1: - return binary_buffer_next_u8_into_u64(&buffer->bb, ret); - case DW_FORM_data2: - return binary_buffer_next_u16_into_u64(&buffer->bb, ret); - case DW_FORM_udata: - return binary_buffer_next_uleb128(&buffer->bb, ret); - default: - return binary_buffer_error(&buffer->bb, - "unknown attribute form %#" PRIx64 " for DW_LNCT_directory_index", - form); - } -} - -static struct drgn_error *read_file_name_table(struct path_hash_cache *cache, - struct drgn_dwarf_index_cu *cu, - const char *comp_dir, - size_t stmt_list) -{ - struct drgn_error *err; - - struct drgn_debug_info_buffer buffer; - drgn_debug_info_buffer_init(&buffer, cu->module, DRGN_SCN_DEBUG_LINE); - /* Checked in index_cu_first_pass(). */ - buffer.bb.pos += stmt_list; - - bool is_64_bit; - int version; - if ((err = read_lnp_header(&buffer, &is_64_bit, &version))) - return err; - - cache->current_chunk = cache->first_chunk; - cache->next_object = cache->first_chunk->objects; - cache->directories.size = 0; - - const struct lnp_entry_format *entry_formats; - int entry_format_count; - uint64_t entry_count = 0; /* For -Wmaybe-uninitialized. */ - const struct path_hash *path_hash, *parent; - if (version >= 5) { - if ((err = read_lnp_entry_formats(&buffer, cache, - &entry_format_count))) - return err; - entry_formats = cache->entry_formats; - if ((err = binary_buffer_next_uleb128(&buffer.bb, - &entry_count))) - return err; - if (entry_count > SIZE_MAX || - !path_hash_vector_reserve(&cache->directories, entry_count)) - return err; - parent = &empty_path_hash; - } else { - entry_formats = dwarf4_directory_entry_formats; - entry_format_count = array_size(dwarf4_directory_entry_formats); - path_hash = hash_path(cache, comp_dir, &empty_path_hash); - if (!path_hash || - !path_hash_vector_append(&cache->directories, &path_hash)) - return &drgn_enomem; - parent = path_hash; - } - - while (version < 5 || entry_count-- > 0) { - const char *path; - for (int j = 0; j < entry_format_count; j++) { - if (entry_formats[j].content_type == DW_LNCT_path) { - err = read_lnp_string(&buffer, is_64_bit, - entry_formats[j].form, - &path); - if (version < 5 && path[0] == '\0') - goto file_name_entries; - } else { - err = skip_lnp_form(&buffer.bb, is_64_bit, - entry_formats[j].form); - } - if (err) - return err; - } - path_hash = hash_path(cache, path, parent); - if (!path_hash || - !path_hash_vector_append(&cache->directories, &path_hash)) - return &drgn_enomem; - parent = cache->directories.data[0]; - } - -file_name_entries:; - /* - * File name 0 needs special treatment. In DWARF 2-4, file name entries - * are numbered starting at 1, and a DW_AT_decl_file of 0 indicates that - * no file was specified. In DWARF 5, file name entries are numbered - * starting at 0, and entry 0 is the current compilation file name. The - * DWARF 5 specification still states that a DW_AT_decl_file of 0 - * indicates that no file was specified, but some producers (including - * Clang) and consumers (including elfutils and GDB) treat a - * DW_AT_decl_file of 0 as specifying the current compilation file name, - * so we do the same. - * - * So, for DWARF 5, we hash entry 0 as usual, and for DWARF 4, we insert - * a placeholder for entry 0. If there are no file names at all, we keep - * the no_file_name_hashes placeholder. - */ - struct uint64_vector file_name_hashes; - if (version >= 5) { - if ((err = read_lnp_entry_formats(&buffer, cache, - &entry_format_count))) - return err; - entry_formats = cache->entry_formats; - if ((err = binary_buffer_next_uleb128(&buffer.bb, - &entry_count))) - return err; - if (entry_count == 0) - return NULL; - if (entry_count > SIZE_MAX) - return &drgn_enomem; - uint64_vector_init(&file_name_hashes); - if (!uint64_vector_reserve(&file_name_hashes, entry_count)) { - err = &drgn_enomem; - goto err; - } - } else { - entry_formats = dwarf4_file_name_entry_formats; - entry_format_count = array_size(dwarf4_file_name_entry_formats); - uint64_vector_init(&file_name_hashes); - } - - while (version < 5 || entry_count-- > 0) { - const char *path; - uint64_t directory_index = 0; - for (int j = 0; j < entry_format_count; j++) { - if (entry_formats[j].content_type == DW_LNCT_path) { - err = read_lnp_string(&buffer, is_64_bit, - entry_formats[j].form, - &path); - if (!err && version < 5) { - if (path[0] == '\0') { - if (file_name_hashes.size == 0) { - uint64_vector_deinit(&file_name_hashes); - return NULL; - } - goto done; - } else if (file_name_hashes.size == 0) { - uint64_t zero = 0; - if (!uint64_vector_append(&file_name_hashes, - &zero)) { - err = &drgn_enomem; - goto err; - } - } - } - } else if (entry_formats[j].content_type == - DW_LNCT_directory_index) { - err = read_lnp_directory_index(&buffer, - entry_formats[j].form, - &directory_index); - } else { - err = skip_lnp_form(&buffer.bb, is_64_bit, - entry_formats[j].form); - } - if (err) - goto err; - } - - if (directory_index >= cache->directories.size) { - err = binary_buffer_error(&buffer.bb, - "directory index %" PRIu64 " is invalid", - directory_index); - goto err; - } - struct path_hash *prev_object = cache->next_object; - struct path_hash_chunk *prev_chunk = cache->current_chunk; - path_hash = hash_path(cache, path, - cache->directories.data[directory_index]); - if (!path_hash || - !uint64_vector_append(&file_name_hashes, &path_hash->hash)) { - err = &drgn_enomem; - goto err; - } - - /* "Free" the objects allocated for this file name. */ - cache->next_object = prev_object; - cache->current_chunk = prev_chunk; - } - -done: - uint64_vector_shrink_to_fit(&file_name_hashes); - cu->file_name_hashes = file_name_hashes.data; - cu->num_file_names = file_name_hashes.size; - return NULL; - -err: - uint64_vector_deinit(&file_name_hashes); - return err; -} - -static struct drgn_error * -index_specification(struct drgn_debug_info *dbinfo, uintptr_t declaration, - struct drgn_debug_info_module *module, uintptr_t addr) +static bool +index_specification(struct drgn_dwarf_specification_map *specifications, + uintptr_t declaration, uintptr_t addr) { - struct drgn_dwarf_specification entry = { - .declaration = declaration, - .module = module, - .addr = addr, + struct drgn_dwarf_specification_map_entry entry = { + .key = declaration, + .value = addr, }; struct hash_pair hp = drgn_dwarf_specification_map_hash(&declaration); - int ret; - #pragma omp critical(drgn_index_specification) - ret = drgn_dwarf_specification_map_insert_hashed(&dbinfo->dwarf.specifications, - &entry, hp, - NULL); - /* - * There may be duplicates if multiple DIEs reference one declaration, - * but we ignore them. - */ - return ret < 0 ? &drgn_enomem : NULL; + // There may be duplicates if multiple DIEs reference one declaration, + // but we ignore them. + return drgn_dwarf_specification_map_insert_hashed(specifications, &entry, + hp, NULL) >= 0; } static struct drgn_error *read_indirect_insn(struct drgn_dwarf_index_cu *cu, @@ -1931,14 +1086,6 @@ static struct drgn_error *read_indirect_insn(struct drgn_dwarf_index_cu *cu, return dw_at_sibling_to_insn(bb, form, insn_ret); case INSN_NAME_INDIRECT: return dw_at_name_to_insn(cu, bb, form, insn_ret); - case INSN_COMP_DIR_INDIRECT: - return dw_at_comp_dir_to_insn(cu, bb, form, insn_ret); - case INSN_STR_OFFSETS_BASE_INDIRECT: - return dw_at_str_offsets_base_to_insn(cu, bb, form, insn_ret); - case INSN_STMT_LIST_INDIRECT: - return dw_at_stmt_list_to_insn(cu, bb, form, insn_ret); - case INSN_DECL_FILE_INDIRECT: - return dw_at_decl_file_to_insn(bb, form, insn_ret, NULL); case INSN_DECLARATION_INDIRECT: return dw_at_declaration_to_insn(bb, form, insn_ret, die_flags); case INSN_SPECIFICATION_INDIRECT: @@ -1949,25 +1096,16 @@ static struct drgn_error *read_indirect_insn(struct drgn_dwarf_index_cu *cu, } /* - * First pass: read the file name tables and index DIEs with - * DW_AT_specification. This recurses into namespaces. + * First pass: index DIEs with DW_AT_specification. This recurses into + * namespaces. */ static struct drgn_error * -index_cu_first_pass(struct drgn_debug_info *dbinfo, - struct drgn_dwarf_index_cu_buffer *buffer, - struct path_hash_cache *path_hash_cache) +index_cu_first_pass(struct drgn_dwarf_specification_map *specifications, + struct drgn_dwarf_index_cu_buffer *buffer) { - /* - * If DW_AT_comp_dir uses a strx* form, we can't read it right away - * because we might not have seen DW_AT_str_offsets_base yet. Rather - * than adding an extra flag to indicate that we need to read it later, - * we set comp_dir to this sentinel value. - */ - static const char comp_dir_is_strx; - struct drgn_error *err; struct drgn_dwarf_index_cu *cu = buffer->cu; - const char *debug_info_buffer = cu->module->scn_data[cu->scn]->d_buf; + const char *debug_info_buffer = cu->file->scn_data[cu->scn]->d_buf; unsigned int depth = 0; for (;;) { size_t die_addr = (uintptr_t)buffer->bb.pos; @@ -1989,17 +1127,12 @@ index_cu_first_pass(struct drgn_debug_info *dbinfo, uint8_t *insnp = &cu->abbrev_insns[cu->abbrev_decls[code - 1]]; bool declaration = false; uintptr_t specification = 0; - const char *comp_dir = ""; - uint64_t comp_dir_strx; - const char *stmt_list_ptr = NULL; - uint64_t stmt_list; const char *sibling = NULL; uint8_t insn; uint8_t extra_die_flags = 0; while ((insn = *insnp++) != INSN_END) { indirect_insn:; uint64_t skip, tmp; - Elf_Data *strp_scn; switch (insn) { case INSN_SKIP_BLOCK: if ((err = binary_buffer_next_uleb128(&buffer->bb, @@ -2023,13 +1156,9 @@ indirect_insn:; goto skip; case INSN_SKIP_LEB128: case INSN_NAME_STRX: - case INSN_DECL_FILE_UDATA: if ((err = binary_buffer_skip_leb128(&buffer->bb))) return err; break; - case INSN_COMP_DIR_STRING: - comp_dir = buffer->bb.pos; - /* fallthrough */ case INSN_SKIP_STRING: case INSN_NAME_STRING: if ((err = binary_buffer_skip_string(&buffer->bb))) @@ -2071,112 +1200,10 @@ indirect_insn:; "DW_AT_sibling points backwards"); } break; - case INSN_COMP_DIR_STRP4: - if ((err = binary_buffer_next_u32_into_u64(&buffer->bb, - &tmp))) - return err; - strp_scn = cu->module->scn_data[DRGN_SCN_DEBUG_STR]; - goto comp_dir_strp; - case INSN_COMP_DIR_STRP8: - if ((err = binary_buffer_next_u64(&buffer->bb, &tmp))) - return err; - strp_scn = cu->module->scn_data[DRGN_SCN_DEBUG_STR]; - goto comp_dir_strp; - case INSN_COMP_DIR_LINE_STRP4: - if ((err = binary_buffer_next_u32_into_u64(&buffer->bb, - &tmp))) - return err; - strp_scn = cu->module->scn_data[DRGN_SCN_DEBUG_LINE_STR]; - goto comp_dir_strp; - case INSN_COMP_DIR_LINE_STRP8: - if ((err = binary_buffer_next_u64(&buffer->bb, &tmp))) - return err; - strp_scn = cu->module->scn_data[DRGN_SCN_DEBUG_LINE_STR]; -comp_dir_strp: - if (tmp >= strp_scn->d_size) { - return binary_buffer_error(&buffer->bb, - "DW_AT_comp_dir is out of bounds"); - } - comp_dir = (const char *)strp_scn->d_buf + tmp; - break; - case INSN_COMP_DIR_STRX: - if ((err = binary_buffer_next_uleb128(&buffer->bb, - &comp_dir_strx))) - return err; - comp_dir = &comp_dir_is_strx; - break; - case INSN_COMP_DIR_STRX1: - if ((err = binary_buffer_next_u8_into_u64(&buffer->bb, - &comp_dir_strx))) - return err; - comp_dir = &comp_dir_is_strx; - break; - case INSN_COMP_DIR_STRX2: - if ((err = binary_buffer_next_u16_into_u64(&buffer->bb, - &comp_dir_strx))) - return err; - comp_dir = &comp_dir_is_strx; - break; - case INSN_COMP_DIR_STRX3: - if ((err = binary_buffer_next_uint(&buffer->bb, - 3, - &comp_dir_strx))) - return err; - comp_dir = &comp_dir_is_strx; - break; - case INSN_COMP_DIR_STRX4: - if ((err = binary_buffer_next_u32_into_u64(&buffer->bb, - &comp_dir_strx))) - return err; - comp_dir = &comp_dir_is_strx; - break; - case INSN_COMP_DIR_STRP_ALT4: - if ((err = binary_buffer_next_u32_into_u64(&buffer->bb, - &tmp))) - return err; - strp_scn = cu->module->alt_debug_str_data; - goto comp_dir_strp; - case INSN_COMP_DIR_STRP_ALT8: - if ((err = binary_buffer_next_u64(&buffer->bb, &tmp))) - return err; - strp_scn = cu->module->alt_debug_str_data; - goto comp_dir_strp; - case INSN_STR_OFFSETS_BASE4: - if ((err = binary_buffer_next_u32_into_u64(&buffer->bb, - &tmp))) - return err; - goto str_offsets_base; - case INSN_STR_OFFSETS_BASE8: - if ((err = binary_buffer_next_u64(&buffer->bb, - &tmp))) - return err; -str_offsets_base: - if (tmp > cu->module->scn_data[DRGN_SCN_DEBUG_STR_OFFSETS]->d_size) { - return binary_buffer_error(&buffer->bb, - "DW_AT_str_offsets_base is out of bounds"); - } - cu->str_offsets = - (char *)cu->module->scn_data[DRGN_SCN_DEBUG_STR_OFFSETS]->d_buf - + tmp; - break; - case INSN_STMT_LIST_LINEPTR4: - stmt_list_ptr = buffer->bb.pos; - if ((err = binary_buffer_next_u32_into_u64(&buffer->bb, - &stmt_list))) - return err; - break; - case INSN_STMT_LIST_LINEPTR8: - stmt_list_ptr = buffer->bb.pos; - if ((err = binary_buffer_next_u64(&buffer->bb, - &stmt_list))) - return err; - break; case INSN_NAME_STRX1: - case INSN_DECL_FILE_DATA1: skip = 1; goto skip; case INSN_NAME_STRX2: - case INSN_DECL_FILE_DATA2: skip = 2; goto skip; case INSN_NAME_STRX3: @@ -2185,18 +1212,12 @@ indirect_insn:; case INSN_NAME_STRP4: case INSN_NAME_STRX4: case INSN_NAME_STRP_ALT4: - case INSN_DECL_FILE_DATA4: skip = 4; goto skip; case INSN_NAME_STRP8: case INSN_NAME_STRP_ALT8: - case INSN_DECL_FILE_DATA8: skip = 8; goto skip; - case INSN_DECL_FILE_IMPLICIT: - while (*insnp++ & 0x80) - ; - break; case INSN_DECLARATION_FLAG: { uint8_t flag; if ((err = binary_buffer_next_u8(&buffer->bb, @@ -2255,16 +1276,12 @@ indirect_insn:; &tmp))) return err; specification_ref_alt: - specification = ((uintptr_t)cu->module->alt_debug_info_data->d_buf + specification = ((uintptr_t)cu->file->alt_debug_info_data->d_buf + tmp); break; case INSN_INDIRECT: case INSN_SIBLING_INDIRECT: case INSN_NAME_INDIRECT: - case INSN_COMP_DIR_INDIRECT: - case INSN_STR_OFFSETS_BASE_INDIRECT: - case INSN_STMT_LIST_INDIRECT: - case INSN_DECL_FILE_INDIRECT: case INSN_DECLARATION_INDIRECT: case INSN_SPECIFICATION_INDIRECT: if ((err = read_indirect_insn(cu, &buffer->bb, @@ -2283,27 +1300,10 @@ indirect_insn:; return err; break; } - } - insn = *insnp | extra_die_flags; - - if (depth == 0) { - if (stmt_list_ptr) { - if (stmt_list > - cu->module->scn_data[DRGN_SCN_DEBUG_LINE]->d_size) { - return binary_buffer_error_at(&buffer->bb, - stmt_list_ptr, - "DW_AT_stmt_list is out of bounds"); - } - if (comp_dir == &comp_dir_is_strx && - (err = read_strx(buffer, comp_dir_strx, - &comp_dir))) - return err; - if ((err = read_file_name_table(path_hash_cache, - cu, comp_dir, - stmt_list))) - return err; - } - } else if (specification) { + } + insn = *insnp | extra_die_flags; + + if (depth > 0 && specification) { if (insn & INSN_DIE_FLAG_DECLARATION) declaration = true; /* @@ -2312,15 +1312,16 @@ indirect_insn:; * declarations. We may need to handle * DW_AT_specification "chains" in the future. */ - if (!declaration && - (err = index_specification(dbinfo, specification, - cu->module, die_addr))) - return err; + if (!declaration + && !index_specification(specifications, + specification, die_addr)) + return &drgn_enomem; } if (insn & INSN_DIE_FLAG_CHILDREN) { - if (sibling && - (insn & INSN_DIE_FLAG_TAG_MASK) != DW_TAG_namespace) + if (sibling + && ((insn & INSN_DIE_FLAG_TAG_MASK) + != DRGN_DWARF_INDEX_namespace)) buffer->bb.pos = sibling; else depth++; @@ -2338,127 +1339,67 @@ indirect_insn:; * refers to the given address. * * @param[in] die_addr The address of the declaration DIE. - * @param[out] module_ret Returned module containing the definition DIE. - * @param[out] addr_ret Returned address of the definition DIE. + * @param[out] ret Returned address of the definition DIE. * @return @c true if a definition DIE was found, @c false if not (in which case - * *@p module_ret and *@p addr_ret are not modified). + * `*ret` is not modified). */ -static bool -drgn_dwarf_find_definition(struct drgn_debug_info *dbinfo, uintptr_t die_addr, - struct drgn_debug_info_module **module_ret, - uintptr_t *addr_ret) +static bool drgn_dwarf_find_definition(struct drgn_debug_info *dbinfo, + uintptr_t die_addr, uintptr_t *ret) { struct drgn_dwarf_specification_map_iterator it = drgn_dwarf_specification_map_search(&dbinfo->dwarf.specifications, &die_addr); if (!it.entry) return false; - *module_ret = it.entry->module; - *addr_ret = it.entry->addr; + *ret = it.entry->value; return true; } -static bool append_die_entry(struct drgn_debug_info *dbinfo, - struct drgn_dwarf_index_shard *shard, uint8_t tag, - uint64_t file_name_hash, - struct drgn_debug_info_module *module, - uintptr_t addr) -{ - if (shard->dies.size == UINT32_MAX) - return false; - struct drgn_dwarf_index_die *die = - drgn_dwarf_index_die_vector_append_entry(&shard->dies); - if (!die) - return false; - die->next = UINT32_MAX; - die->tag = tag; - if (die->tag == DW_TAG_namespace) { - die->namespace = malloc(sizeof(*die->namespace)); - if (!die->namespace) { - shard->dies.size--; +static bool +index_die(struct drgn_dwarf_index_die_map map[static DRGN_DWARF_INDEX_MAP_SIZE], + struct drgn_dwarf_base_type_map *base_types, const char *name, + int tag, uintptr_t addr) +{ + size_t name_len = strlen(name); + if (tag != DRGN_DWARF_INDEX_base_type) { + struct drgn_dwarf_index_die_map_entry entry = { + .key = { name, name_len }, + .value = VECTOR_INIT, + }; + struct hash_pair hp = drgn_dwarf_index_die_map_hash(&entry.key); + auto it = drgn_dwarf_index_die_map_search_hashed(&map[tag], + &entry.key, + hp); + if (!it.entry + && drgn_dwarf_index_die_map_insert_searched(&map[tag], + &entry, hp, + &it) < 0) return false; - } - drgn_namespace_dwarf_index_init(die->namespace, dbinfo); - } else { - die->file_name_hash = file_name_hash; + return drgn_dwarf_index_die_vector_append(&it.entry->value, + &addr); + } else if (base_types) { + struct drgn_dwarf_base_type_map_entry entry = { + .key = { name, name_len }, + .value = addr, + }; + struct hash_pair hp = drgn_dwarf_base_type_map_hash(&entry.key); + return drgn_dwarf_base_type_map_insert_hashed(base_types, + &entry, hp, + NULL) >= 0; } - die->module = module; - die->addr = addr; - return true; } -static bool index_die(struct drgn_namespace_dwarf_index *ns, - struct drgn_dwarf_index_cu *cu, const char *name, - uint8_t tag, uint64_t file_name_hash, - struct drgn_debug_info_module *module, uintptr_t addr) -{ - bool success = false; - struct drgn_dwarf_index_die_map_entry entry = { - .key = { name, strlen(name) }, - }; - struct hash_pair hp = drgn_dwarf_index_die_map_hash(&entry.key); - struct drgn_dwarf_index_shard *shard = - &ns->shards[hash_pair_to_shard(hp)]; - omp_set_lock(&shard->lock); - struct drgn_dwarf_index_die_map_iterator it = - drgn_dwarf_index_die_map_search_hashed(&shard->map, &entry.key, - hp); - struct drgn_dwarf_index_die *die; - if (!it.entry) { - if (!append_die_entry(ns->dbinfo, shard, tag, file_name_hash, - module, addr)) - goto err; - entry.value = shard->dies.size - 1; - if (drgn_dwarf_index_die_map_insert_searched(&shard->map, - &entry, hp, - NULL) < 0) - goto err; - die = &shard->dies.data[shard->dies.size - 1]; - goto out; - } - - die = &shard->dies.data[it.entry->value]; - for (;;) { - const uint64_t die_file_name_hash = - die->tag == DW_TAG_namespace ? 0 : die->file_name_hash; - if (die->tag == tag && die_file_name_hash == file_name_hash) - goto out; - - if (die->next == UINT32_MAX) - break; - die = &shard->dies.data[die->next]; - } - - size_t index = die - shard->dies.data; - if (!append_die_entry(ns->dbinfo, shard, tag, file_name_hash, module, - addr)) - goto err; - die = &shard->dies.data[shard->dies.size - 1]; - shard->dies.data[index].next = shard->dies.size - 1; -out: - if (tag == DW_TAG_namespace) { - struct drgn_dwarf_index_pending_die *pending = - drgn_dwarf_index_pending_die_vector_append_entry(&die->namespace->pending_dies); - if (!pending) - goto err; - pending->cu = cu - ns->dbinfo->dwarf.index_cus.data; - pending->addr = addr; - } - success = true; -err: - omp_unset_lock(&shard->lock); - return success; -} - /* Second pass: index the actual DIEs. */ static struct drgn_error * -index_cu_second_pass(struct drgn_namespace_dwarf_index *ns, +index_cu_second_pass(struct drgn_debug_info *dbinfo, + struct drgn_dwarf_index_die_map map[static DRGN_DWARF_INDEX_MAP_SIZE], + struct drgn_dwarf_base_type_map *base_types, struct drgn_dwarf_index_cu_buffer *buffer) { struct drgn_error *err; struct drgn_dwarf_index_cu *cu = buffer->cu; - Elf_Data *debug_str = cu->module->scn_data[DRGN_SCN_DEBUG_STR]; + Elf_Data *debug_str = cu->file->scn_data[DRGN_SCN_DEBUG_STR]; unsigned int depth = 0; uint8_t depth1_tag = 0; size_t depth1_addr = 0; @@ -2481,8 +1422,6 @@ index_cu_second_pass(struct drgn_namespace_dwarf_index *ns, uint8_t *insnp = &cu->abbrev_insns[cu->abbrev_decls[code - 1]]; const char *name = NULL; - const char *decl_file_ptr = NULL; - uint64_t decl_file = 0; /* For -Wmaybe-uninitialized. */ bool declaration = false; bool specification = false; const char *sibling = NULL; @@ -2514,17 +1453,15 @@ indirect_insn:; goto skip; case INSN_SPECIFICATION_REF_UDATA: specification = true; - /* fallthrough */ + fallthrough; case INSN_SKIP_LEB128: - case INSN_COMP_DIR_STRX: if ((err = binary_buffer_skip_leb128(&buffer->bb))) return err; break; case INSN_NAME_STRING: name = buffer->bb.pos; - /* fallthrough */ + fallthrough; case INSN_SKIP_STRING: - case INSN_COMP_DIR_STRING: if ((err = binary_buffer_skip_string(&buffer->bb))) return err; break; @@ -2618,67 +1555,13 @@ indirect_insn:; if ((err = binary_buffer_next_u64(&buffer->bb, &tmp))) return err; name_alt_strp: - if (tmp >= cu->module->alt_debug_str_data->d_size) { + if (tmp >= cu->file->alt_debug_str_data->d_size) { return binary_buffer_error(&buffer->bb, "DW_AT_name is out of bounds"); } - name = (const char *)cu->module->alt_debug_str_data->d_buf + tmp; + name = (const char *)cu->file->alt_debug_str_data->d_buf + tmp; __builtin_prefetch(name); break; - case INSN_COMP_DIR_STRP4: - case INSN_COMP_DIR_LINE_STRP4: - case INSN_COMP_DIR_STRP_ALT4: - case INSN_STR_OFFSETS_BASE4: - case INSN_STMT_LIST_LINEPTR4: - skip = 4; - goto skip; - case INSN_COMP_DIR_STRP8: - case INSN_COMP_DIR_LINE_STRP8: - case INSN_COMP_DIR_STRP_ALT8: - case INSN_STR_OFFSETS_BASE8: - case INSN_STMT_LIST_LINEPTR8: - skip = 8; - goto skip; - case INSN_DECL_FILE_DATA1: - decl_file_ptr = buffer->bb.pos; - if ((err = binary_buffer_next_u8_into_u64(&buffer->bb, - &decl_file))) - return err; - break; - case INSN_DECL_FILE_DATA2: - decl_file_ptr = buffer->bb.pos; - if ((err = binary_buffer_next_u16_into_u64(&buffer->bb, - &decl_file))) - return err; - break; - case INSN_DECL_FILE_DATA4: - decl_file_ptr = buffer->bb.pos; - if ((err = binary_buffer_next_u32_into_u64(&buffer->bb, - &decl_file))) - return err; - break; - case INSN_DECL_FILE_DATA8: - decl_file_ptr = buffer->bb.pos; - if ((err = binary_buffer_next_u64(&buffer->bb, - &decl_file))) - return err; - break; - case INSN_DECL_FILE_UDATA: - decl_file_ptr = buffer->bb.pos; - if ((err = binary_buffer_next_uleb128(&buffer->bb, - &decl_file))) - return err; - break; - case INSN_DECL_FILE_IMPLICIT: - decl_file_ptr = buffer->bb.pos; - decl_file = 0; - for (int shift = 0; ; shift += 7) { - uint8_t byte = *insnp++; - decl_file |= (uint64_t)(byte & 0x7f) << shift; - if (!(byte & 0x80)) - break; - } - break; case INSN_DECLARATION_FLAG: { uint8_t flag; if ((err = binary_buffer_next_u8(&buffer->bb, @@ -2690,25 +1573,16 @@ indirect_insn:; } case INSN_SPECIFICATION_REF1: specification = true; - /* fallthrough */ - case INSN_COMP_DIR_STRX1: skip = 1; goto skip; case INSN_SPECIFICATION_REF2: specification = true; - /* fallthrough */ - case INSN_COMP_DIR_STRX2: skip = 2; goto skip; - case INSN_COMP_DIR_STRX3: - skip = 3; - goto skip; case INSN_SPECIFICATION_REF4: case INSN_SPECIFICATION_REF_ADDR4: case INSN_SPECIFICATION_REF_ALT4: specification = true; - /* fallthrough */ - case INSN_COMP_DIR_STRX4: skip = 4; goto skip; case INSN_SPECIFICATION_REF8: @@ -2720,10 +1594,6 @@ indirect_insn:; case INSN_INDIRECT: case INSN_SIBLING_INDIRECT: case INSN_NAME_INDIRECT: - case INSN_COMP_DIR_INDIRECT: - case INSN_STR_OFFSETS_BASE_INDIRECT: - case INSN_STMT_LIST_INDIRECT: - case INSN_DECL_FILE_INDIRECT: case INSN_DECLARATION_INDIRECT: case INSN_SPECIFICATION_INDIRECT: if ((err = read_indirect_insn(cu, &buffer->bb, @@ -2750,13 +1620,12 @@ indirect_insn:; depth1_tag = tag; depth1_addr = die_addr; } - if (depth == (tag == DW_TAG_enumerator ? 2 : 1) && name && - !specification) { + if (depth == (tag == DRGN_DWARF_INDEX_enumerator ? 2 : 1) + && name && !specification) { if (insn & INSN_DIE_FLAG_DECLARATION) declaration = true; - struct drgn_debug_info_module *module = cu->module; - if (tag == DW_TAG_enumerator) { - if (depth1_tag != DW_TAG_enumeration_type) + if (tag == DRGN_DWARF_INDEX_enumerator) { + if (depth1_tag != DRGN_DWARF_INDEX_enumeration_type) goto next; /* * NB: the enumerator name points to the @@ -2765,28 +1634,24 @@ indirect_insn:; * that. */ die_addr = depth1_addr; - } else if (declaration && - !drgn_dwarf_find_definition(ns->dbinfo, - die_addr, - &module, - &die_addr)) { - goto next; + } else if (declaration) { + // Declaration class, struct, and union DIEs + // with children are treated like namespaces. + if ((insn & INSN_DIE_FLAG_CHILDREN) + && (tag == DRGN_DWARF_INDEX_class_type + || tag == DRGN_DWARF_INDEX_structure_type + || tag == DRGN_DWARF_INDEX_union_type) + && !index_die(map, base_types, name, + DRGN_DWARF_INDEX_namespace, + die_addr)) + return &drgn_enomem; + if (!drgn_dwarf_find_definition(dbinfo, + die_addr, + &die_addr)) + goto next; } - uint64_t file_name_hash; - if (decl_file_ptr) { - if (decl_file >= cu->num_file_names) { - return binary_buffer_error_at(&buffer->bb, - decl_file_ptr, - "invalid DW_AT_decl_file %" PRIu64, - decl_file); - } - file_name_hash = cu->file_name_hashes[decl_file]; - } else { - file_name_hash = 0; - } - if (!index_die(ns, cu, name, tag, file_name_hash, - module, die_addr)) + if (!index_die(map, base_types, name, tag, die_addr)) return &drgn_enomem; } @@ -2798,8 +1663,8 @@ indirect_insn:; * over the children of the top-level DIE even if it has * a sibling pointer. */ - if (sibling && tag != DW_TAG_enumeration_type && - depth > 0) + if (sibling && tag != DRGN_DWARF_INDEX_enumeration_type + && depth > 0) buffer->bb.pos = sibling; else depth++; @@ -2810,66 +1675,112 @@ indirect_insn:; return NULL; } -static void drgn_dwarf_index_rollback(struct drgn_debug_info *dbinfo) +static inline int drgn_dwarf_index_cu_cmp(const void *_a, const void *_b) { - for (size_t i = 0; i < DRGN_DWARF_INDEX_NUM_SHARDS; i++) { - struct drgn_dwarf_index_shard *shard = - &dbinfo->dwarf.global.shards[i]; - /* - * Because we're deleting everything that was added since the - * last update, we can just shrink the dies array to the first - * entry that was added for this update. - */ - while (shard->dies.size) { - struct drgn_dwarf_index_die *die = - &shard->dies.data[shard->dies.size - 1]; - if (die->module->state == - DRGN_DEBUG_INFO_MODULE_INDEXED) - break; - if (die->tag == DW_TAG_namespace) { - drgn_namespace_dwarf_index_deinit(die->namespace); - free(die->namespace); - } - shard->dies.size--; - } + uintptr_t a = (uintptr_t)((struct drgn_dwarf_index_cu *)_a)->buf; + uintptr_t b = (uintptr_t)((struct drgn_dwarf_index_cu *)_b)->buf; + return (a > b) - (a < b); +} - /* - * The new entries may be chained off of existing entries; - * unchain them. Note that any entries chained off of the new - * entries must also be new, so there's no need to preserve - * them. - */ - for (size_t index = 0; index < shard->dies.size; index++) { - struct drgn_dwarf_index_die *die = - &shard->dies.data[index]; - if (die->next != UINT32_MAX && - die->next >= shard->dies.size) - die->next = UINT32_MAX; - } +// die_addr must be from an indexed CU. +static struct drgn_dwarf_index_cu * +drgn_dwarf_index_find_cu(struct drgn_debug_info *dbinfo, uintptr_t die_addr) +{ + struct drgn_dwarf_index_cu *cus = + drgn_dwarf_index_cu_vector_begin(&dbinfo->dwarf.index_cus); + size_t lo = 0; + size_t hi = drgn_dwarf_index_cu_vector_size(&dbinfo->dwarf.index_cus); + while (lo < hi) { + size_t mid = lo + (hi - lo) / 2; + if (die_addr < (uintptr_t)cus[mid].buf) + hi = mid; + else + lo = mid + 1; + } + // We don't check that the address is within bounds because this can + // only be called with a valid address. + return &cus[lo - 1]; +} - /* Finally, delete the new entries in the map. */ - for (struct drgn_dwarf_index_die_map_iterator it = - drgn_dwarf_index_die_map_first(&shard->map); - it.entry; ) { - if (it.entry->value >= shard->dies.size) { - it = drgn_dwarf_index_die_map_delete_iterator(&shard->map, - it); - } else { - it = drgn_dwarf_index_die_map_next(it); +// If there wasn't already an error, merge src into dst, and return an error if +// that fails. If there was already an error, return the original error. Free +// src whether or not there was an error. +static struct drgn_error * +drgn_dwarf_specification_map_merge(struct drgn_dwarf_specification_map *dst, + struct drgn_dwarf_specification_map *src, + struct drgn_error *err) +{ + if (!err) { + for (auto it = drgn_dwarf_specification_map_first(src); + it.entry; it = drgn_dwarf_specification_map_next(it)) { + if (drgn_dwarf_specification_map_insert(dst, it.entry, + NULL) < 0) { + err = &drgn_enomem; + break; } } } + drgn_dwarf_specification_map_deinit(src); + return err; +} - for (struct drgn_dwarf_specification_map_iterator it = - drgn_dwarf_specification_map_first(&dbinfo->dwarf.specifications); - it.entry; ) { - if (it.entry->module->state == DRGN_DEBUG_INFO_MODULE_INDEXED) { - it = drgn_dwarf_specification_map_next(it); +static struct drgn_error * +drgn_dwarf_index_die_map_merge(struct drgn_dwarf_index_die_map *dst, + struct drgn_dwarf_index_die_map *src, + struct drgn_error *err) +{ + auto src_it = drgn_dwarf_index_die_map_first(src); + for (; !err && src_it.entry; + src_it = drgn_dwarf_index_die_map_next(src_it)) { + struct drgn_dwarf_index_die_vector *src_vector = + &src_it.entry->value; + + struct hash_pair hp = + drgn_dwarf_index_die_map_hash(&src_it.entry->key); + auto dst_it = drgn_dwarf_index_die_map_search_hashed(dst, + &src_it.entry->key, + hp); + // If dst already has the key, extend its vector with + // src_vector, and free src_vector. Otherwise, move src_vector + // into dst. + if (dst_it.entry) { + if (!drgn_dwarf_index_die_vector_extend(&dst_it.entry->value, + src_vector)) + err = &drgn_enomem; + } else if (drgn_dwarf_index_die_map_insert_searched(dst, + src_it.entry, + hp, + NULL) < 0) { + err = &drgn_enomem; } else { - it = drgn_dwarf_specification_map_delete_iterator(&dbinfo->dwarf.specifications, - it); + // We stole src_vector; don't free it. + continue; + } + drgn_dwarf_index_die_vector_deinit(src_vector); + } + for (; src_it.entry; src_it = drgn_dwarf_index_die_map_next(src_it)) + drgn_dwarf_index_die_vector_deinit(&src_it.entry->value); + drgn_dwarf_index_die_map_deinit(src); + return err; +} + +static struct drgn_error * +drgn_dwarf_base_type_map_merge(struct drgn_dwarf_base_type_map *dst, + struct drgn_dwarf_base_type_map *src, + struct drgn_error *err) +{ + if (!err) { + for (auto it = drgn_dwarf_base_type_map_first(src); it.entry; + it = drgn_dwarf_base_type_map_next(it)) { + if (drgn_dwarf_base_type_map_insert(dst, it.entry, NULL) + < 0) { + err = &drgn_enomem; + break; + } } } + drgn_dwarf_base_type_map_deinit(src); + return err; } struct drgn_error * @@ -2878,60 +1789,64 @@ drgn_dwarf_info_update_index(struct drgn_dwarf_index_state *state) struct drgn_debug_info *dbinfo = state->dbinfo; struct drgn_dwarf_index_cu_vector *cus = &dbinfo->dwarf.index_cus; - if (!drgn_namespace_dwarf_index_alloc_shards(&dbinfo->dwarf.global)) - return &drgn_enomem; + if (dbinfo->dwarf.global.saved_err) + return drgn_error_copy(dbinfo->dwarf.global.saved_err); + + size_t new_cus_size = drgn_dwarf_index_cu_vector_size(cus); + for (int i = 0; i < drgn_num_threads; i++) + new_cus_size += drgn_dwarf_index_cu_vector_size(&state->cus[i]); + if (new_cus_size == drgn_dwarf_index_cu_vector_size(cus)) + return NULL; + + // Per-thread array of maps to populate. Thread 0 uses the maps in the + // dbinfo directly. These are merged into the dbinfo and freed. + _cleanup_free_ union { + // For first pass. + struct drgn_dwarf_specification_map specifications; + // For second pass. + struct { + struct drgn_dwarf_index_die_map map[DRGN_DWARF_INDEX_MAP_SIZE]; + struct drgn_dwarf_base_type_map base_types; + }; + } *maps = NULL; + if (drgn_num_threads > 1) { + maps = malloc_array(drgn_num_threads - 1, sizeof(maps[0])); + if (!maps) + return &drgn_enomem; + } - size_t old_cus_size = cus->size; - size_t new_cus_size = old_cus_size; - for (size_t i = 0; i < state->max_threads; i++) - new_cus_size += state->cus[i].size; if (!drgn_dwarf_index_cu_vector_reserve(cus, new_cus_size)) return &drgn_enomem; - for (size_t i = 0; i < state->max_threads; i++) { - for (size_t j = 0; j < state->cus[i].size; j++) { - struct drgn_dwarf_index_pending_cu *pending_cu = - &state->cus[i].data[j]; - cus->data[cus->size++] = (struct drgn_dwarf_index_cu){ - .module = pending_cu->module, - .buf = pending_cu->buf, - .len = pending_cu->len, - .is_64_bit = pending_cu->is_64_bit, - .scn = pending_cu->scn, - .file_name_hashes = - (uint64_t *)no_file_name_hashes, - .num_file_names = - array_size(no_file_name_hashes), - }; - } - } + for (int i = 0; i < drgn_num_threads; i++) + drgn_dwarf_index_cu_vector_extend(cus, &state->cus[i]); struct drgn_error *err = NULL; - #pragma omp parallel + #pragma omp parallel num_threads(drgn_num_threads) { - struct path_hash_cache path_hash_cache; - path_hash_vector_init(&path_hash_cache.directories); - path_hash_cache.entry_formats = NULL; - path_hash_cache.entry_formats_capacity = 0; - path_hash_cache.first_chunk = - malloc(sizeof(struct path_hash_chunk)); - if (path_hash_cache.first_chunk) { - path_hash_cache.first_chunk->next = NULL; + struct drgn_dwarf_specification_map *specifications; + int thread_num = omp_get_thread_num(); + if (thread_num == 0) { + specifications = &dbinfo->dwarf.specifications; } else { - #pragma omp critical(drgn_dwarf_info_update_index_error) - if (!err) - err = &drgn_enomem; + specifications = &maps[thread_num - 1].specifications; + drgn_dwarf_specification_map_init(specifications); } + #pragma omp for schedule(dynamic) - for (size_t i = old_cus_size; i < cus->size; i++) { + for (size_t i = dbinfo->dwarf.global.cus_indexed; + i < drgn_dwarf_index_cu_vector_size(cus); i++) { + struct drgn_dwarf_index_cu *cu = + drgn_dwarf_index_cu_vector_at(cus, i); if (err) continue; - struct drgn_dwarf_index_cu *cu = &cus->data[i]; - struct drgn_dwarf_index_cu_buffer cu_buffer; - drgn_dwarf_index_cu_buffer_init(&cu_buffer, cu); - struct drgn_error *cu_err = read_cu(&cu_buffer); - if (!cu_err) - cu_err = index_cu_first_pass(dbinfo, &cu_buffer, - &path_hash_cache); + struct drgn_error *cu_err = read_cu(cu); + if (!cu_err) { + struct drgn_dwarf_index_cu_buffer buffer; + drgn_dwarf_index_cu_buffer_init(&buffer, cu); + buffer.bb.pos += cu_header_size(cu); + cu_err = index_cu_first_pass(specifications, + &buffer); + } if (cu_err) { #pragma omp critical(drgn_dwarf_info_update_index_error) if (err) @@ -2940,86 +1855,220 @@ drgn_dwarf_info_update_index(struct drgn_dwarf_index_state *state) err = cu_err; } } - free(path_hash_cache.entry_formats); - path_hash_vector_deinit(&path_hash_cache.directories); - struct path_hash_chunk *chunk = path_hash_cache.first_chunk; - while (chunk) { - struct path_hash_chunk *next_chunk = chunk->next; - free(chunk); - chunk = next_chunk; - } + } + for (int i = 0; i < drgn_num_threads - 1; i++) { + err = drgn_dwarf_specification_map_merge(&dbinfo->dwarf.specifications, + &maps[i].specifications, + err); } if (err) goto err; - #pragma omp parallel for schedule(dynamic) - for (size_t i = old_cus_size; i < cus->size; i++) { - if (err) - continue; - struct drgn_dwarf_index_cu *cu = &cus->data[i]; - struct drgn_dwarf_index_cu_buffer buffer; - drgn_dwarf_index_cu_buffer_init(&buffer, cu); - buffer.bb.pos += cu_header_size(cu); - struct drgn_error *cu_err = - index_cu_second_pass(&dbinfo->dwarf.global, &buffer); - if (cu_err) { + #pragma omp parallel num_threads(drgn_num_threads) + { + struct drgn_error *thread_err; + + struct drgn_dwarf_index_die_map *map; + struct drgn_dwarf_base_type_map *base_types; + int thread_num = omp_get_thread_num(); + if (thread_num == 0) { + map = dbinfo->dwarf.global.map; + base_types = &dbinfo->dwarf.base_types; + } else { + array_for_each(tag_map, maps[thread_num - 1].map) + drgn_dwarf_index_die_map_init(tag_map); + map = maps[thread_num - 1].map; + base_types = &maps[thread_num - 1].base_types; + drgn_dwarf_base_type_map_init(base_types); + } + + #pragma omp for schedule(dynamic) + for (size_t i = dbinfo->dwarf.global.cus_indexed; + i < drgn_dwarf_index_cu_vector_size(cus); i++) { + if (err) + continue; + struct drgn_dwarf_index_cu *cu = + drgn_dwarf_index_cu_vector_at(cus, i); + struct drgn_dwarf_index_cu_buffer buffer; + drgn_dwarf_index_cu_buffer_init(&buffer, cu); + buffer.bb.pos += cu_header_size(cu); + thread_err = index_cu_second_pass(dbinfo, map, + base_types, &buffer); + if (thread_err) { + #pragma omp critical(drgn_dwarf_info_update_index_error) + if (err) + drgn_error_destroy(thread_err); + else + err = thread_err; + } + } + + thread_err = err; + + #pragma omp for schedule(dynamic) nowait + for (size_t i = 0; i <= array_size(dbinfo->dwarf.global.map); i++) { + if (i < array_size(dbinfo->dwarf.global.map)) { + for (int j = 0; j < drgn_num_threads - 1; j++) { + thread_err = + drgn_dwarf_index_die_map_merge(&dbinfo->dwarf.global.map[i], + &maps[j].map[i], + thread_err); + } + } else { + for (int j = 0; j < drgn_num_threads - 1; j++) { + thread_err = + drgn_dwarf_base_type_map_merge(&dbinfo->dwarf.base_types, + &maps[j].base_types, + thread_err); + } + } + } + if (thread_err) { #pragma omp critical(drgn_dwarf_info_update_index_error) if (err) - drgn_error_destroy(cu_err); + drgn_error_destroy(thread_err); else - err = cu_err; + err = thread_err; } } + if (err) { - drgn_dwarf_index_rollback(dbinfo); err: - for (size_t i = old_cus_size; i < cus->size; i++) - drgn_dwarf_index_cu_deinit(&cus->data[i]); - cus->size = old_cus_size; - } - return err; + dbinfo->dwarf.global.saved_err = err; + return drgn_error_copy(err); + } + qsort(drgn_dwarf_index_cu_vector_begin(cus), + drgn_dwarf_index_cu_vector_size(cus), + sizeof(struct drgn_dwarf_index_cu), drgn_dwarf_index_cu_cmp); + dbinfo->dwarf.global.cus_indexed = + drgn_dwarf_index_cu_vector_size(cus); + return NULL; } static struct drgn_error *index_namespace(struct drgn_namespace_dwarf_index *ns) { - if (ns->pending_dies.size == 0) + size_t num_index_cus = + drgn_dwarf_index_cu_vector_size(&ns->dbinfo->dwarf.index_cus); + if (ns->cus_indexed >= num_index_cus) return NULL; if (ns->saved_err) return drgn_error_copy(ns->saved_err); - if (!drgn_namespace_dwarf_index_alloc_shards(ns)) - return &drgn_enomem; + // The parent namespace must be indexed first so that the DIEs for this + // namespace are populated. + struct drgn_error *err = index_namespace(ns->parent); + if (err) + return err; - struct drgn_error *err = NULL; - #pragma omp parallel for schedule(dynamic) - for (size_t i = 0; i < ns->pending_dies.size; i++) { - if (!err) { - struct drgn_dwarf_index_pending_die *pending = - &ns->pending_dies.data[i]; - struct drgn_dwarf_index_cu *cu = - &ns->dbinfo->dwarf.index_cus.data[pending->cu]; - struct drgn_dwarf_index_cu_buffer buffer; - drgn_dwarf_index_cu_buffer_init(&buffer, cu); - buffer.bb.pos = (char *)pending->addr; - struct drgn_error *cu_err = - index_cu_second_pass(ns, &buffer); - if (cu_err) { - #pragma omp critical(drgn_index_namespace_error) + drgn_blocking_guard(ns->dbinfo->prog); + + struct drgn_dwarf_index_die_vector + *die_vectors_to_index[DRGN_DWARF_INDEX_NUM_NAMESPACE_TAGS]; + int tags_to_index[DRGN_DWARF_INDEX_NUM_NAMESPACE_TAGS]; + int num_tags_to_index = 0; + struct nstring key = { ns->name, ns->name_len }; + struct hash_pair hp = drgn_dwarf_index_die_map_hash(&key); + for (int i = 0; i < DRGN_DWARF_INDEX_NUM_NAMESPACE_TAGS; i++) { + auto it = drgn_dwarf_index_die_map_search_hashed(&ns->parent->map[i], + &key, hp); + if (!it.entry) + continue; + struct drgn_dwarf_index_die_vector *dies = &it.entry->value; + if (ns->dies_indexed[i] + >= drgn_dwarf_index_die_vector_size(dies)) + continue; + + die_vectors_to_index[num_tags_to_index] = dies; + tags_to_index[num_tags_to_index] = i; + num_tags_to_index++; + } + if (num_tags_to_index == 0) { + ns->cus_indexed = num_index_cus; + return NULL; + } + + _cleanup_free_ struct drgn_dwarf_index_die_map + (*maps)[DRGN_DWARF_INDEX_MAP_SIZE] = NULL; + if (drgn_num_threads > 1) { + maps = malloc_array(drgn_num_threads - 1, sizeof(maps[0])); + if (!maps) + return &drgn_enomem; + } + + err = NULL; + #pragma omp parallel num_threads(drgn_num_threads) + { + struct drgn_error *thread_err; + + struct drgn_dwarf_index_die_map *map; + int thread_num = omp_get_thread_num(); + if (thread_num == 0) { + map = ns->map; + } else { + array_for_each(tag_map, maps[thread_num - 1]) + drgn_dwarf_index_die_map_init(tag_map); + map = maps[thread_num - 1]; + } + + for (int i = 0; i < num_tags_to_index; i++) { + struct drgn_dwarf_index_die_vector *dies = + die_vectors_to_index[i]; + #pragma omp for schedule(dynamic) nowait + for (uint32_t j = ns->dies_indexed[tags_to_index[i]]; + j < drgn_dwarf_index_die_vector_size(dies); j++) { if (err) - drgn_error_destroy(cu_err); - else - err = cu_err; + continue; + uintptr_t die_addr = + *drgn_dwarf_index_die_vector_at(dies, j); + struct drgn_dwarf_index_cu *cu = + drgn_dwarf_index_find_cu(ns->dbinfo, die_addr); + struct drgn_dwarf_index_cu_buffer buffer; + drgn_dwarf_index_cu_buffer_init(&buffer, cu); + buffer.bb.pos = (void *)die_addr; + thread_err = index_cu_second_pass(ns->dbinfo, + map, NULL, + &buffer); + if (thread_err) { + #pragma omp critical(drgn_index_namespace_error) + if (err) + drgn_error_destroy(thread_err); + else + err = thread_err; + } + } + } + #pragma omp barrier + + thread_err = err; + + #pragma omp for schedule(dynamic) nowait + for (size_t i = 0; i < array_size(ns->map); i++) { + for (int j = 0; j < drgn_num_threads - 1; j++) { + thread_err = + drgn_dwarf_index_die_map_merge(&ns->map[i], + &maps[j][i], + thread_err); } } + if (thread_err) { + #pragma omp critical(drgn_index_namespace_error) + if (err) + drgn_error_destroy(thread_err); + else + err = thread_err; + } } if (err) { ns->saved_err = err; return drgn_error_copy(ns->saved_err); } - ns->pending_dies.size = 0; - drgn_dwarf_index_pending_die_vector_shrink_to_fit(&ns->pending_dies); - return err; + ns->cus_indexed = num_index_cus; + for (int i = 0; i < num_tags_to_index; i++) { + ns->dies_indexed[tags_to_index[i]] = + drgn_dwarf_index_die_vector_size(die_vectors_to_index[i]); + } + return NULL; } /** @@ -3029,9 +2078,12 @@ static struct drgn_error *index_namespace(struct drgn_namespace_dwarf_index *ns) * advanced with @ref drgn_dwarf_index_iterator_next(). */ struct drgn_dwarf_index_iterator { - const uint64_t *tags; + struct drgn_namespace_dwarf_index *ns; + const char *name; + size_t name_len; + const enum drgn_dwarf_index_tag *tags; size_t num_tags; - struct drgn_dwarf_index_shard *shard; + struct drgn_dwarf_index_die_vector *dies; uint32_t index; }; @@ -3050,92 +2102,133 @@ static struct drgn_error * drgn_dwarf_index_iterator_init(struct drgn_dwarf_index_iterator *it, struct drgn_namespace_dwarf_index *ns, const char *name, size_t name_len, - const uint64_t *tags, size_t num_tags) + const enum drgn_dwarf_index_tag *tags, + size_t num_tags) { struct drgn_error *err = index_namespace(ns); if (err) return err; - if (ns->shards) { - struct nstring key = { name, name_len }; - struct hash_pair hp = drgn_dwarf_index_die_map_hash(&key); - it->shard = &ns->shards[hash_pair_to_shard(hp)]; - struct drgn_dwarf_index_die_map_iterator map_it = - drgn_dwarf_index_die_map_search_hashed(&it->shard->map, - &key, hp); - it->index = map_it.entry ? map_it.entry->value : UINT32_MAX; - } else { - it->shard = NULL; - it->index = UINT32_MAX; - } + it->ns = ns; + it->name = name; + it->name_len = name_len; it->tags = tags; it->num_tags = num_tags; + // Sentinel to simplify first iteration. + static const struct drgn_dwarf_index_die_vector empty_dies = VECTOR_INIT; + it->dies = (struct drgn_dwarf_index_die_vector *)&empty_dies; + it->index = 0; return NULL; } -static inline bool -drgn_dwarf_index_iterator_matches_tag(struct drgn_dwarf_index_iterator *it, - struct drgn_dwarf_index_die *die) -{ - if (it->num_tags == 0) - return true; - for (size_t i = 0; i < it->num_tags; i++) { - if (die->tag == it->tags[i]) - return true; - } - return false; -} - /** * Get the next matching DIE from a DWARF index iterator. * - * If matching any name, this is O(n), where n is the number of indexed DIEs. If - * matching by name, this is O(1) on average and O(n) worst case. - * - * Note that this returns the parent `DW_TAG_enumeration_type` for indexed - * `DW_TAG_enumerator` DIEs. + * Note the quirks in @ref drgn_namespace_dwarf_index::map about + * `DW_TAG_enumerator` and `DW_TAG_namespace`. * * @param[in] it DWARF index iterator. - * @return Next DIE, or @c NULL if there are no more matching DIEs. + * @param[out] die_ret Returned DIE. + * @param[out] file_ret If not @c NULL, returned file that DIE came from. + * @return @c true on success, @c false if there are no more matching DIEs. */ -static struct drgn_dwarf_index_die * -drgn_dwarf_index_iterator_next(struct drgn_dwarf_index_iterator *it) -{ - while (it->index != UINT32_MAX) { - struct drgn_dwarf_index_die *die = - &it->shard->dies.data[it->index]; - it->index = die->next; - if (drgn_dwarf_index_iterator_matches_tag(it, die)) - return die; +static bool +drgn_dwarf_index_iterator_next(struct drgn_dwarf_index_iterator *it, + Dwarf_Die *die_ret, + struct drgn_elf_file **file_ret) +{ + uintptr_t die_addr; + if (it->index < drgn_dwarf_index_die_vector_size(it->dies)) { + die_addr = *drgn_dwarf_index_die_vector_at(it->dies, + it->index++); + } else { + for (;;) { + if (it->num_tags == 0) + return false; + + int tag = *it->tags++; + it->num_tags--; + if (tag == DRGN_DWARF_INDEX_base_type) { + // Only look up base types in the global + // namespace. + if (it->ns->parent) + continue; + struct nstring key = { it->name, it->name_len }; + auto map_it = drgn_dwarf_base_type_map_search(&it->ns->dbinfo->dwarf.base_types, + &key); + if (map_it.entry) { + die_addr = map_it.entry->value; + break; + } + } else { + struct nstring key = { it->name, it->name_len }; + auto map_it = + drgn_dwarf_index_die_map_search(&it->ns->map[tag], + &key); + if (map_it.entry) { + die_addr = *drgn_dwarf_index_die_vector_first(&map_it.entry->value); + it->dies = &map_it.entry->value; + it->index = 1; + break; + } + } + } } - return NULL; + + struct drgn_dwarf_index_cu *cu = + drgn_dwarf_index_find_cu(it->ns->dbinfo, die_addr); + *die_ret = (Dwarf_Die){ + .addr = (void *)die_addr, + .cu = cu->libdw_cu, + }; + if (file_ret) + *file_ret = cu->file; + return true; } -/** - * Get a @c Dwarf_Die from a @ref drgn_dwarf_index_die. - * - * @param[in] die Indexed DIE. - * @param[out] die_ret Returned DIE. - * @return @c NULL on success, non-@c NULL on error. - */ static struct drgn_error * -drgn_dwarf_index_get_die(struct drgn_dwarf_index_die *die, Dwarf_Die *die_ret) +drgn_namespace_find_child(struct drgn_namespace_dwarf_index *ns, + const char *name, size_t name_len, + struct drgn_namespace_dwarf_index **ret) { - Dwarf_Addr bias; - Dwarf *dwarf = dwfl_module_getdwarf(die->module->dwfl_module, &bias); - if (!dwarf) - return drgn_error_libdwfl(); - uintptr_t start = - (uintptr_t)die->module->scn_data[DRGN_SCN_DEBUG_INFO]->d_buf; - size_t size = die->module->scn_data[DRGN_SCN_DEBUG_INFO]->d_size; - if (die->addr >= start && die->addr < start + size) { - if (!dwarf_offdie(dwarf, die->addr - start, die_ret)) - return drgn_error_libdw(); - } else { - start = (uintptr_t)die->module->scn_data[DRGN_SCN_DEBUG_TYPES]->d_buf; - if (!dwarf_offdie_types(dwarf, die->addr - start, die_ret)) - return drgn_error_libdw(); + struct drgn_error *err = index_namespace(ns); + if (err) + return err; + + struct nstring key = { name, name_len }; + struct hash_pair hp = nstring_hash_pair(&key); + auto it = drgn_namespace_table_search_hashed(&ns->children, &key, hp); + if (it.entry) { + *ret = *it.entry; + return NULL; } - return NULL; + + for (int i = 0; i < DRGN_DWARF_INDEX_NUM_NAMESPACE_TAGS; i++) { + auto die_it = + drgn_dwarf_index_die_map_search_hashed(&ns->map[i], + &key, hp); + if (die_it.entry) { + struct drgn_namespace_dwarf_index *new_ns = + malloc(sizeof(*new_ns)); + if (!new_ns) + return &drgn_enomem; + // Use the name from the DIE map, which has the same + // lifetime as the namespace table. + drgn_namespace_dwarf_index_init(new_ns, + die_it.entry->key.str, + die_it.entry->key.len, + ns); + if (drgn_namespace_table_insert_searched(&ns->children, + &new_ns, hp, + NULL) < 0) { + drgn_namespace_dwarf_index_deinit(new_ns); + free(new_ns); + return &drgn_enomem; + } + *ret = new_ns; + return NULL; + } + } + return &drgn_not_found; } /* @@ -3183,20 +2276,13 @@ drgn_debug_info_main_language(struct drgn_debug_info *dbinfo, { struct drgn_error *err; struct drgn_dwarf_index_iterator it; - const uint64_t tag = DW_TAG_subprogram; + const enum drgn_dwarf_index_tag tag = DRGN_DWARF_INDEX_subprogram; err = drgn_dwarf_index_iterator_init(&it, &dbinfo->dwarf.global, "main", strlen("main"), &tag, 1); if (err) return err; - struct drgn_dwarf_index_die *index_die; - while ((index_die = drgn_dwarf_index_iterator_next(&it))) { - Dwarf_Die die; - err = drgn_dwarf_index_get_die(index_die, &die); - if (err) { - drgn_error_destroy(err); - continue; - } - + Dwarf_Die die; + while (drgn_dwarf_index_iterator_next(&it, &die, NULL)) { err = drgn_language_from_die(&die, false, ret); if (err) { drgn_error_destroy(err); @@ -3213,16 +2299,23 @@ drgn_debug_info_main_language(struct drgn_debug_info *dbinfo, * DIE iteration. */ -DEFINE_VECTOR(dwarf_die_vector, Dwarf_Die) +DEFINE_VECTOR(dwarf_die_vector, Dwarf_Die); -/** Iterator over DWARF DIEs in a @ref drgn_debug_info_module. */ +/** Iterator over DWARF DIEs in a @ref drgn_module. */ struct drgn_dwarf_die_iterator { /** Stack of current DIE and its ancestors. */ struct dwarf_die_vector dies; + /** + * Dwarf handle that we're iterating over. For split DWARF, this is the + * main file. + */ Dwarf *dwarf; - /** End of current CU (for bounds checking). */ + /** + * End of current CU (for bounds checking). For split DWARF, this is in + * the split file. + */ const char *cu_end; - /** Offset of next CU. */ + /** Offset of next CU. For split DWARF, this is in the main file. */ Dwarf_Off next_cu_off; /** Whether current CU is from .debug_types. */ bool debug_types; @@ -3270,11 +2363,11 @@ static struct drgn_error * drgn_dwarf_die_iterator_next(struct drgn_dwarf_die_iterator *it, bool children, size_t subtree) { -#define TOP() (&it->dies.data[it->dies.size - 1]) +#define TOP() (dwarf_die_vector_last(&it->dies)) int r; Dwarf_Die die; - assert(subtree <= it->dies.size); - if (it->dies.size == 0) { + assert(subtree <= dwarf_die_vector_size(&it->dies)); + if (dwarf_die_vector_empty(&it->dies)) { /* This is the first call. Get the first unit DIE. */ if (!dwarf_die_vector_append_entry(&it->dies)) return &drgn_enomem; @@ -3292,7 +2385,7 @@ drgn_dwarf_die_iterator_next(struct drgn_dwarf_die_iterator *it, bool children, /* The previous DIE has no children. */ } - if (it->dies.size == subtree) { + if (dwarf_die_vector_size(&it->dies) == subtree) { /* * The previous DIE is the root of the subtree. We're * done. @@ -3300,7 +2393,7 @@ drgn_dwarf_die_iterator_next(struct drgn_dwarf_die_iterator *it, bool children, return &drgn_stop; } - if (it->dies.size > 1) { + if (dwarf_die_vector_size(&it->dies) > 1) { r = dwarf_siblingof(TOP(), &die); if (r == 0) { /* The previous DIE has a sibling. Return it. */ @@ -3322,24 +2415,25 @@ drgn_dwarf_die_iterator_next(struct drgn_dwarf_die_iterator *it, bool children, * either the parent's sibling or * another null terminator. */ - it->dies.size--; + dwarf_die_vector_pop(&it->dies); addr++; - if (it->dies.size == subtree) { + if (dwarf_die_vector_size(&it->dies) + == subtree) { /* * We're back to the root of the * subtree. We're done. */ return &drgn_stop; } - if (it->dies.size == 1 || - addr >= it->cu_end) + if (dwarf_die_vector_size(&it->dies) == 1 + || addr >= it->cu_end) goto next_unit; } while (*addr == '\0'); /* * addr now points to the next DIE. Return it. */ *TOP() = (Dwarf_Die){ - .cu = it->dies.data[0].cu, + .cu = dwarf_die_vector_first(&it->dies)->cu, .addr = addr, }; return NULL; @@ -3359,20 +2453,42 @@ next_unit:; it->debug_types ? &type_signature : NULL, NULL); if (r == 0) { /* Got the next unit. Return the unit DIE. */ - if (it->debug_types) { - r = !dwarf_offdie_types(it->dwarf, - cu_off + cu_header_size, TOP()); - } else { - r = !dwarf_offdie(it->dwarf, cu_off + cu_header_size, - TOP()); - } + Dwarf_Off offset = cu_off + cu_header_size; + if (it->debug_types) + r = !dwarf_offdie_types(it->dwarf, offset, TOP()); + else + r = !dwarf_offdie(it->dwarf, offset, TOP()); if (r) return drgn_error_libdw(); + Dwarf_Off cu_end_off = it->next_cu_off; +#if _ELFUTILS_PREREQ(0, 171) + // If the unit is a skeleton, replace it with the split unit. + Dwarf_Die subdie; + uint8_t unit_type; + if (dwarf_cu_info(TOP()->cu, NULL, &unit_type, NULL, &subdie, + NULL, NULL, NULL)) + return drgn_error_libdw(); + if (unit_type == DW_UT_skeleton && subdie.cu) { + offset = dwarf_dieoffset(&subdie); + if (dwarf_next_unit(dwarf_cu_getdwarf(subdie.cu), + offset - dwarf_cuoffset(&subdie), + &cu_end_off, NULL, NULL, NULL, NULL, + NULL, NULL, NULL)) + return drgn_error_libdw(); + *TOP() = subdie; + } +#endif it->cu_end = ((const char *)TOP()->addr - - dwarf_dieoffset(TOP()) - + it->next_cu_off); + - offset + + cu_end_off); return NULL; } else if (r > 0) { + // Note that in split DWARF, there are no skeleton units for + // type units, and the main file doesn't have a .debug_types + // section. Instead, the split files contain a .debug_types.dwo + // section per type unit. elfutils as of 0.189 doesn't support + // multiple sections with the same name, so we don't support + // type units with split DWARF. if (!it->debug_types) { it->next_cu_off = 0; it->debug_types = true; @@ -3386,20 +2502,23 @@ next_unit:; #undef TOP } -struct drgn_error * -drgn_debug_info_module_find_dwarf_scopes(struct drgn_debug_info_module *module, - uint64_t pc, uint64_t *bias_ret, - Dwarf_Die **dies_ret, - size_t *length_ret) +struct drgn_error *drgn_module_find_dwarf_scopes(struct drgn_module *module, + uint64_t pc, + uint64_t *bias_ret, + Dwarf_Die **dies_ret, + size_t *length_ret) { struct drgn_error *err; - Dwarf_Addr bias; - Dwarf *dwarf = dwfl_module_getdwarf(module->dwfl_module, &bias); - if (!dwarf) - return drgn_error_libdw(); - *bias_ret = bias; - pc -= bias; + if (!module->debug_file) { + *bias_ret = 0; + *dies_ret = NULL; + *length_ret = 0; + return NULL; + } + Dwarf *dwarf = module->debug_file->dwarf; + *bias_ret = module->debug_file_bias; + pc -= module->debug_file_bias; /* First, try to get the CU containing the PC. */ Dwarf_Aranges *aranges; @@ -3407,107 +2526,108 @@ drgn_debug_info_module_find_dwarf_scopes(struct drgn_debug_info_module *module, if (dwarf_getaranges(dwarf, &aranges, &naranges) < 0) return drgn_error_libdw(); - struct drgn_dwarf_die_iterator it; - bool children; + _cleanup_(drgn_dwarf_die_iterator_deinit) + struct drgn_dwarf_die_iterator it; + drgn_dwarf_die_iterator_init(&it, dwarf); size_t subtree; Dwarf_Off offset; if (dwarf_getarangeinfo(dwarf_getarange_addr(aranges, pc), NULL, NULL, &offset) >= 0) { - drgn_dwarf_die_iterator_init(&it, dwarf); Dwarf_Die *cu_die = dwarf_die_vector_append_entry(&it.dies); - if (!cu_die) { - err = &drgn_enomem; - goto err; - } - if (!dwarf_offdie(dwarf, offset, cu_die)) { - err = drgn_error_libdw(); - goto err; + if (!cu_die) + return &drgn_enomem; + if (!dwarf_offdie(dwarf, offset, cu_die)) + return drgn_error_libdw(); +#if _ELFUTILS_PREREQ(0, 171) + // If the unit is a skeleton, replace it with the split unit. + Dwarf_Die subdie; + uint8_t unit_type; + if (dwarf_cu_info(cu_die->cu, NULL, &unit_type, NULL, &subdie, + NULL, NULL, NULL)) + return drgn_error_libdw(); + if (unit_type == DW_UT_skeleton && subdie.cu) { + dwarf = dwarf_cu_getdwarf(subdie.cu); + offset = dwarf_dieoffset(&subdie); + *cu_die = subdie; } +#endif if (dwarf_next_unit(dwarf, offset - dwarf_cuoffset(cu_die), &it.next_cu_off, NULL, NULL, NULL, NULL, - NULL, NULL, NULL)) { - err = drgn_error_libdw(); - goto err; - } + NULL, NULL, NULL)) + return drgn_error_libdw(); it.cu_end = ((const char *)cu_die->addr - - dwarf_dieoffset(cu_die) + - offset + it.next_cu_off); - children = true; subtree = 1; } else { /* * Range was not found. .debug_aranges could be missing or * incomplete, so fall back to checking each CU. */ - drgn_dwarf_die_iterator_init(&it, dwarf); - children = false; subtree = 0; } - /* Now find DIEs containing the PC. */ - while (!(err = drgn_dwarf_die_iterator_next(&it, children, subtree))) { - int r = dwarf_haspc(&it.dies.data[it.dies.size - 1], pc); + /* + * Now find the most specific DIE containing the PC. Entry subtree - 1 + * in it.dies is the most specific DIE we have found so far. We iterate + * over all of its children until we find a more specific DIE, then we + * descend into that one. + * + * We only want to descend into children DIEs when the last DIE + * contained the PC, which is when size(it.dies) == subtree. + */ + while (!(err = drgn_dwarf_die_iterator_next(&it, + dwarf_die_vector_size(&it.dies) + == subtree, + subtree))) { + int r = dwarf_haspc(dwarf_die_vector_last(&it.dies), pc); if (r > 0) { - children = true; - subtree = it.dies.size; + subtree = dwarf_die_vector_size(&it.dies); } else if (r < 0) { - err = drgn_error_libdw(); - goto err; + return drgn_error_libdw(); } } if (err != &drgn_stop) - goto err; + return err; - *dies_ret = it.dies.data; - *length_ret = it.dies.size; + dwarf_die_vector_steal(&it.dies, dies_ret, length_ret); return NULL; - -err: - drgn_dwarf_die_iterator_deinit(&it); - return err; } struct drgn_error *drgn_find_die_ancestors(Dwarf_Die *die, Dwarf_Die **dies_ret, size_t *length_ret) { - struct drgn_error *err; - Dwarf *dwarf = dwarf_cu_getdwarf(die->cu); if (!dwarf) return drgn_error_libdw(); - struct dwarf_die_vector dies = VECTOR_INIT; + _cleanup_(dwarf_die_vector_deinit) + struct dwarf_die_vector dies = VECTOR_INIT; Dwarf_Die *cu_die = dwarf_die_vector_append_entry(&dies); - if (!cu_die) { - err = &drgn_enomem; - goto err; - } + if (!cu_die) + return &drgn_enomem; Dwarf_Half cu_version; Dwarf_Off type_offset; if (!dwarf_cu_die(die->cu, cu_die, &cu_version, NULL, NULL, NULL, NULL, - &type_offset)) { - err = drgn_error_libdw(); - goto err; - } + &type_offset)) + return drgn_error_libdw(); Dwarf_Off cu_die_offset = dwarf_dieoffset(cu_die); bool debug_types = cu_version == 4 && type_offset != 0; Dwarf_Off next_cu_offset; uint64_t type_signature; if (dwarf_next_unit(dwarf, cu_die_offset - dwarf_cuoffset(cu_die), &next_cu_offset, NULL, NULL, NULL, NULL, NULL, - debug_types ? &type_signature : NULL, NULL)) { - err = drgn_error_libdw(); - goto err; - } + debug_types ? &type_signature : NULL, NULL)) + return drgn_error_libdw(); const unsigned char *cu_end = (unsigned char *)cu_die->addr - cu_die_offset + next_cu_offset; -#define TOP() (&dies.data[dies.size - 1]) +#define TOP() (dwarf_die_vector_last(&dies)) while ((char *)TOP()->addr <= (char *)die->addr) { if (TOP()->addr == die->addr) { - *dies_ret = dies.data; - *length_ret = dies.size - 1; + dwarf_die_vector_steal(&dies, dies_ret, length_ret); + (*length_ret)--; return NULL; } @@ -3515,16 +2635,12 @@ struct drgn_error *drgn_find_die_ancestors(Dwarf_Die *die, Dwarf_Die **dies_ret, if (dwarf_attr(TOP(), DW_AT_sibling, &attr)) { /* The top DIE has a DW_AT_sibling attribute. */ Dwarf_Die sibling; - if (!dwarf_formref_die(&attr, &sibling)) { - err = drgn_error_libdw(); - goto err; - } + if (!dwarf_formref_die(&attr, &sibling)) + return drgn_error_libdw(); if (sibling.cu != TOP()->cu || - (char *)sibling.addr <= (char *)TOP()->addr) { - err = drgn_error_create(DRGN_ERROR_OTHER, + (char *)sibling.addr <= (char *)TOP()->addr) + return drgn_error_create(DRGN_ERROR_OTHER, "invalid DW_AT_sibling"); - goto err; - } if ((char *)sibling.addr > (char *)die->addr) { /* @@ -3534,14 +2650,11 @@ struct drgn_error *drgn_find_die_ancestors(Dwarf_Die *die, Dwarf_Die **dies_ret, */ Dwarf_Die *child = dwarf_die_vector_append_entry(&dies); - if (!child) { - err = &drgn_enomem; - goto err; - } + if (!child) + return &drgn_enomem; int r = dwarf_child(TOP() - 1, child); if (r < 0) { - err = drgn_error_libdw(); - goto err; + return drgn_error_libdw(); } else if (r > 0) { /* * The top DIE didn't have any children, @@ -3572,7 +2685,7 @@ struct drgn_error *drgn_find_die_ancestors(Dwarf_Die *die, Dwarf_Die **dies_ret, * child. Otherwise, then addr is its sibling. (Unless * it is a null terminator.) */ - size_t new_size = dies.size; + size_t new_size = dwarf_die_vector_size(&dies); if (dwarf_haschildren(TOP()) > 0) new_size++; @@ -3590,16 +2703,14 @@ struct drgn_error *drgn_find_die_ancestors(Dwarf_Die *die, Dwarf_Die **dies_ret, } /* addr now points to the next DIE. Go to it. */ - if (new_size > dies.size) { - if (!dwarf_die_vector_append_entry(&dies)) { - err = &drgn_enomem; - goto err; - } + if (new_size > dwarf_die_vector_size(&dies)) { + if (!dwarf_die_vector_append_entry(&dies)) + return &drgn_enomem; } else { - dies.size = new_size; + dwarf_die_vector_resize(&dies, new_size); } *TOP() = (Dwarf_Die){ - .cu = dies.data[0].cu, + .cu = dwarf_die_vector_first(&dies)->cu, .addr = addr, }; } @@ -3607,28 +2718,34 @@ struct drgn_error *drgn_find_die_ancestors(Dwarf_Die *die, Dwarf_Die **dies_ret, #undef TOP not_found: - err = drgn_error_create(DRGN_ERROR_OTHER, - "could not find DWARF DIE ancestors"); -err: - dwarf_die_vector_deinit(&dies); - return err; + return drgn_error_create(DRGN_ERROR_OTHER, + "could not find DWARF DIE ancestors"); } /* * Location lists. */ -static struct drgn_error * -drgn_dwarf_next_addrx(struct binary_buffer *bb, - struct drgn_debug_info_module *module, Dwarf_Die *cu_die, - uint8_t address_size, const char **addr_base, - uint64_t *ret) +static struct drgn_error *drgn_dwarf_next_addrx(struct binary_buffer *bb, + struct drgn_elf_file *file, + Dwarf_Die *cu_die, + uint8_t address_size, + const char **addr_base, + uint64_t *ret) { struct drgn_error *err; + // For split DWARF, .debug_addr is in the main debug file. + file = file->module->debug_file; + + // addr_base is a cache of the address table base for the compilation + // unit. if (!*addr_base) { Dwarf_Attribute attr_mem, *attr; - if (!(attr = dwarf_attr(cu_die, DW_AT_addr_base, &attr_mem))) { + if (!(attr = dwarf_attr_integrate(cu_die, DW_AT_addr_base, + &attr_mem)) && + !(attr = dwarf_attr_integrate(cu_die, DW_AT_GNU_addr_base, + &attr_mem))) { return drgn_error_create(DRGN_ERROR_OTHER, "indirect address without DW_AT_addr_base"); } @@ -3636,27 +2753,35 @@ drgn_dwarf_next_addrx(struct binary_buffer *bb, if (dwarf_formudata(attr, &base)) return drgn_error_libdw(); - if (!module->scns[DRGN_SCN_DEBUG_ADDR]) { + if (!file->scns[DRGN_SCN_DEBUG_ADDR]) { return drgn_error_create(DRGN_ERROR_OTHER, "indirect address without .debug_addr section"); } - err = drgn_debug_info_module_cache_section(module, - DRGN_SCN_DEBUG_ADDR); + err = drgn_elf_file_cache_section(file, DRGN_SCN_DEBUG_ADDR); if (err) return err; - if (base > module->scn_data[DRGN_SCN_DEBUG_ADDR]->d_size || - base == 0) { + if (base > file->scn_data[DRGN_SCN_DEBUG_ADDR]->d_size) { return drgn_error_create(DRGN_ERROR_OTHER, "DW_AT_addr_base is out of bounds"); } - *addr_base = (char *)module->scn_data[DRGN_SCN_DEBUG_ADDR]->d_buf + base; - uint8_t segment_selector_size = ((uint8_t *)*addr_base)[-1]; - if (segment_selector_size != 0) { - return drgn_error_format(DRGN_ERROR_OTHER, - "unsupported segment selector size %" PRIu8, - segment_selector_size); + *addr_base = (char *)file->scn_data[DRGN_SCN_DEBUG_ADDR]->d_buf + base; + // In DWARF 5, there is a header immediately before addr_base, + // which ends with a segment selector size. We don't support a + // segment selector yet. In GNU Debug Fission, .debug_addr + // doesn't contain any headers or segment selectors. + if (attr->code != DW_AT_GNU_addr_base) { + if (base == 0) { + return drgn_error_create(DRGN_ERROR_OTHER, + "DW_AT_addr_base is out of bounds"); + } + uint8_t segment_selector_size = ((uint8_t *)*addr_base)[-1]; + if (segment_selector_size != 0) { + return drgn_error_format(DRGN_ERROR_OTHER, + "unsupported segment selector size %" PRIu8, + segment_selector_size); + } } } @@ -3664,7 +2789,7 @@ drgn_dwarf_next_addrx(struct binary_buffer *bb, if ((err = binary_buffer_next_uleb128(bb, &index))) return err; - Elf_Data *data = module->scn_data[DRGN_SCN_DEBUG_ADDR]; + Elf_Data *data = file->scn_data[DRGN_SCN_DEBUG_ADDR]; if (index >= ((char *)data->d_buf + data->d_size - *addr_base) / address_size) { return binary_buffer_error(bb, @@ -3672,41 +2797,47 @@ drgn_dwarf_next_addrx(struct binary_buffer *bb, } copy_lsbytes(ret, sizeof(*ret), HOST_LITTLE_ENDIAN, *addr_base + index * address_size, address_size, - drgn_platform_is_little_endian(&module->platform)); + drgn_elf_file_is_little_endian(file)); return NULL; } -static struct drgn_error * -drgn_dwarf_read_loclistx(struct drgn_debug_info_module *module, - Dwarf_Die *cu_die, uint8_t offset_size, - Dwarf_Word index, Dwarf_Word *ret) +static struct drgn_error *drgn_dwarf_read_loclistx(struct drgn_elf_file *file, + Dwarf_Die *cu_die, + uint8_t offset_size, + Dwarf_Word index, + Dwarf_Word *ret) { struct drgn_error *err; + assert(offset_size == 4 || offset_size == 8); + Dwarf_Attribute attr_mem, *attr; - if (!(attr = dwarf_attr(cu_die, DW_AT_loclists_base, &attr_mem))) { - return drgn_error_create(DRGN_ERROR_OTHER, - "DW_FORM_loclistx without DW_AT_loclists_base"); - } Dwarf_Word base; - if (dwarf_formudata(attr, &base)) - return drgn_error_libdw(); + if ((attr = dwarf_attr(cu_die, DW_AT_loclists_base, &attr_mem))) { + if (dwarf_formudata(attr, &base)) + return drgn_error_libdw(); + } else { + // The DWARF 5 specification doesn't say what it means if there + // is no DW_AT_loclists_base. In practice, it seems like split + // units don't have DW_AT_loclist_base, and the base is intended + // to be the first entry in .debug_loclists immediately after + // the first header. + base = offset_size == 8 ? 20 : 12; + } - if (!module->scns[DRGN_SCN_DEBUG_LOCLISTS]) { + if (!file->scns[DRGN_SCN_DEBUG_LOCLISTS]) { return drgn_error_create(DRGN_ERROR_OTHER, "DW_FORM_loclistx without .debug_loclists section"); } - err = drgn_debug_info_module_cache_section(module, - DRGN_SCN_DEBUG_LOCLISTS); + err = drgn_elf_file_cache_section(file, DRGN_SCN_DEBUG_LOCLISTS); if (err) return err; - Elf_Data *data = module->scn_data[DRGN_SCN_DEBUG_LOCLISTS]; + Elf_Data *data = file->scn_data[DRGN_SCN_DEBUG_LOCLISTS]; if (base > data->d_size) { return drgn_error_create(DRGN_ERROR_OTHER, "DW_AT_loclists_base is out of bounds"); } - assert(offset_size == 4 || offset_size == 8); if (index >= (data->d_size - base) / offset_size) { return drgn_error_create(DRGN_ERROR_OTHER, "DW_FORM_loclistx is out of bounds"); @@ -3715,37 +2846,39 @@ drgn_dwarf_read_loclistx(struct drgn_debug_info_module *module, if (offset_size == 8) { uint64_t offset; memcpy(&offset, (uint64_t *)basep + index, sizeof(offset)); - if (drgn_platform_bswap(&module->platform)) + if (drgn_elf_file_bswap(file)) offset = bswap_64(offset); *ret = base + offset; } else { uint32_t offset; memcpy(&offset, (uint32_t *)basep + index, sizeof(offset)); - if (drgn_platform_bswap(&module->platform)) + if (drgn_elf_file_bswap(file)) offset = bswap_32(offset); *ret = base + offset; } return NULL; } -static struct drgn_error * -drgn_dwarf5_location_list(struct drgn_debug_info_module *module, - Dwarf_Word offset, Dwarf_Die *cu_die, - uint8_t address_size, uint64_t pc, - const char **expr_ret, size_t *expr_size_ret) +static struct drgn_error *drgn_dwarf5_location_list(struct drgn_elf_file *file, + Dwarf_Word offset, + Dwarf_Die *cu_die, + uint8_t address_size, + uint64_t pc, + const char **expr_ret, + size_t *expr_size_ret) { struct drgn_error *err; - if (!module->scns[DRGN_SCN_DEBUG_LOCLISTS]) { + if (!file->scns[DRGN_SCN_DEBUG_LOCLISTS]) { return drgn_error_create(DRGN_ERROR_OTHER, "loclist without .debug_loclists section"); } - err = drgn_debug_info_module_cache_section(module, - DRGN_SCN_DEBUG_LOCLISTS); + err = drgn_elf_file_cache_section(file, DRGN_SCN_DEBUG_LOCLISTS); if (err) return err; - struct drgn_debug_info_buffer buffer; - drgn_debug_info_buffer_init(&buffer, module, DRGN_SCN_DEBUG_LOCLISTS); + struct drgn_elf_file_section_buffer buffer; + drgn_elf_file_section_buffer_init_index(&buffer, file, + DRGN_SCN_DEBUG_LOCLISTS); if (offset > buffer.bb.end - buffer.bb.pos) { return drgn_error_create(DRGN_ERROR_OTHER, "loclist is out of bounds"); @@ -3767,17 +2900,17 @@ drgn_dwarf5_location_list(struct drgn_debug_info_module *module, case DW_LLE_end_of_list: return NULL; case DW_LLE_base_addressx: - if ((err = drgn_dwarf_next_addrx(&buffer.bb, module, + if ((err = drgn_dwarf_next_addrx(&buffer.bb, file, cu_die, address_size, &addr_base, &base))) return err; base_valid = true; break; case DW_LLE_startx_endx: - if ((err = drgn_dwarf_next_addrx(&buffer.bb, module, + if ((err = drgn_dwarf_next_addrx(&buffer.bb, file, cu_die, address_size, &addr_base, &start)) || - (err = drgn_dwarf_next_addrx(&buffer.bb, module, + (err = drgn_dwarf_next_addrx(&buffer.bb, file, cu_die, address_size, &addr_base, &length))) return err; @@ -3798,7 +2931,7 @@ drgn_dwarf5_location_list(struct drgn_debug_info_module *module, buffer.bb.pos += expr_size; break; case DW_LLE_startx_length: - if ((err = drgn_dwarf_next_addrx(&buffer.bb, module, + if ((err = drgn_dwarf_next_addrx(&buffer.bb, file, cu_die, address_size, &addr_base, &start)) || (err = binary_buffer_next_uleb128(&buffer.bb, @@ -3867,22 +3000,133 @@ drgn_dwarf5_location_list(struct drgn_debug_info_module *module, } static struct drgn_error * -drgn_dwarf4_location_list(struct drgn_debug_info_module *module, - Dwarf_Word offset, Dwarf_Die *cu_die, - uint8_t address_size, uint64_t pc, - const char **expr_ret, size_t *expr_size_ret) +drgn_dwarf4_split_location_list(struct drgn_elf_file *file, Dwarf_Word offset, + Dwarf_Die *cu_die, uint8_t address_size, + uint64_t pc, const char **expr_ret, + size_t *expr_size_ret) +{ + struct drgn_error *err; + + if (!file->scns[DRGN_SCN_DEBUG_LOC]) { + return drgn_error_create(DRGN_ERROR_OTHER, + "loclistptr without .debug_loc section"); + } + err = drgn_elf_file_cache_section(file, DRGN_SCN_DEBUG_LOC); + if (err) + return err; + struct drgn_elf_file_section_buffer buffer; + drgn_elf_file_section_buffer_init_index(&buffer, file, + DRGN_SCN_DEBUG_LOC); + if (offset > buffer.bb.end - buffer.bb.pos) { + return drgn_error_create(DRGN_ERROR_OTHER, + "loclistptr is out of bounds"); + } + buffer.bb.pos += offset; + + const char *addr_base = NULL; + uint64_t base; + bool base_valid = false; + *expr_ret = NULL; + *expr_size_ret = 0; + for (;;) { + uint8_t kind; + if ((err = binary_buffer_next_u8(&buffer.bb, &kind))) + return err; + uint64_t start, length; + switch (kind) { + // The GNU Debug Fission design document + // (https://gcc.gnu.org/wiki/DebugFission) uses slightly + // different names for entry kinds than DWARF 5, but they're the + // same as the DWARF 5 versions except as noted below. + case DW_LLE_end_of_list: // DW_LLE_end_of_list_entry + return NULL; + case DW_LLE_base_addressx: // DW_LLE_base_address_selection_entry + if ((err = drgn_dwarf_next_addrx(&buffer.bb, file, + cu_die, address_size, + &addr_base, &base))) + return err; + base_valid = true; + break; + case DW_LLE_startx_endx: // DW_LLE_start_end_entry + if ((err = drgn_dwarf_next_addrx(&buffer.bb, file, + cu_die, address_size, + &addr_base, &start)) || + (err = drgn_dwarf_next_addrx(&buffer.bb, file, + cu_die, address_size, + &addr_base, &length))) + return err; + length -= start; +counted_location_description:; + // Note: this is ULEB128 in DWARF 5. + uint16_t expr_size; + if ((err = binary_buffer_next_u16(&buffer.bb, + &expr_size))) + return err; + if (expr_size > buffer.bb.end - buffer.bb.pos) { + return binary_buffer_error(&buffer.bb, + "location description size is out of bounds"); + } + if (pc >= start && pc - start < length) { + *expr_ret = buffer.bb.pos; + *expr_size_ret = expr_size; + return NULL; + } + buffer.bb.pos += expr_size; + break; + case DW_LLE_startx_length: // DW_LLE_start_length_entry + if ((err = drgn_dwarf_next_addrx(&buffer.bb, file, + cu_die, address_size, + &addr_base, &start)) || + // Note: this is ULEB128 in DWARF 5. + (err = binary_buffer_next_u32_into_u64(&buffer.bb, + &length))) + return err; + goto counted_location_description; + case DW_LLE_offset_pair: // DW_LLE_offset_pair_entry + // Note: these are ULEB128 in DWARF 5. + if ((err = binary_buffer_next_u32_into_u64(&buffer.bb, + &start)) || + (err = binary_buffer_next_u32_into_u64(&buffer.bb, + &length))) + return err; + length -= start; + if (!base_valid) { + Dwarf_Addr low_pc; + if (dwarf_lowpc(cu_die, &low_pc)) + return drgn_error_libdw(); + base = low_pc; + base_valid = true; + } + start += base; + goto counted_location_description; + default: + return binary_buffer_error(&buffer.bb, + "unknown location list entry kind %#" PRIx8, + kind); + } + } +} + +static struct drgn_error *drgn_dwarf4_location_list(struct drgn_elf_file *file, + Dwarf_Word offset, + Dwarf_Die *cu_die, + uint8_t address_size, + uint64_t pc, + const char **expr_ret, + size_t *expr_size_ret) { struct drgn_error *err; - if (!module->scns[DRGN_SCN_DEBUG_LOC]) { + if (!file->scns[DRGN_SCN_DEBUG_LOC]) { return drgn_error_create(DRGN_ERROR_OTHER, "loclistptr without .debug_loc section"); } - err = drgn_debug_info_module_cache_section(module, DRGN_SCN_DEBUG_LOC); + err = drgn_elf_file_cache_section(file, DRGN_SCN_DEBUG_LOC); if (err) return err; - struct drgn_debug_info_buffer buffer; - drgn_debug_info_buffer_init(&buffer, module, DRGN_SCN_DEBUG_LOC); + struct drgn_elf_file_section_buffer buffer; + drgn_elf_file_section_buffer_init_index(&buffer, file, + DRGN_SCN_DEBUG_LOC); if (offset > buffer.bb.end - buffer.bb.pos) { return drgn_error_create(DRGN_ERROR_OTHER, "loclistptr is out of bounds"); @@ -3933,8 +3177,7 @@ drgn_dwarf4_location_list(struct drgn_debug_info_module *module, } static struct drgn_error * -drgn_dwarf_location(struct drgn_debug_info_module *module, - Dwarf_Attribute *attr, +drgn_dwarf_location(struct drgn_elf_file *file, Dwarf_Attribute *attr, const struct drgn_register_state *regs, const char **expr_ret, size_t *expr_size_ret) { @@ -3947,13 +3190,21 @@ drgn_dwarf_location(struct drgn_debug_info_module *module, case DW_FORM_sec_offset: /* DWARF 5 */ case DW_FORM_loclistx: { - Dwarf_Die cu_die; Dwarf_Half cu_version; + uint8_t unit_type; + Dwarf_Die cu_die; uint8_t address_size; uint8_t offset_size; +#if _ELFUTILS_PREREQ(0, 171) + if (dwarf_cu_info(attr->cu, &cu_version, &unit_type, &cu_die, + NULL, NULL, &address_size, &offset_size)) + return drgn_error_libdw(); +#else + unit_type = DW_UT_compile; if (!dwarf_cu_die(attr->cu, &cu_die, &cu_version, NULL, &address_size, &offset_size, NULL, NULL)) return drgn_error_libdw(); +#endif if ((err = drgn_check_address_size(address_size))) return err; @@ -3961,9 +3212,8 @@ drgn_dwarf_location(struct drgn_debug_info_module *module, if (dwarf_formudata(attr, &offset)) return drgn_error_libdw(); if (attr->form == DW_FORM_loclistx && - ((err = drgn_dwarf_read_loclistx(module, &cu_die, - offset_size, offset, - &offset)))) + ((err = drgn_dwarf_read_loclistx(file, &cu_die, offset_size, + offset, &offset)))) return err; struct optional_uint64 pc; @@ -3973,20 +3223,25 @@ drgn_dwarf_location(struct drgn_debug_info_module *module, *expr_size_ret = 0; return NULL; } - Dwarf_Addr bias; - dwfl_module_info(module->dwfl_module, NULL, NULL, NULL, &bias, - NULL, NULL, NULL); - pc.value = pc.value - !regs->interrupted - bias; + pc.value -= !regs->interrupted + file->module->debug_file_bias; if (cu_version >= 5) { - return drgn_dwarf5_location_list(module, offset, - &cu_die, address_size, - pc.value, expr_ret, + return drgn_dwarf5_location_list(file, offset, &cu_die, + address_size, pc.value, + expr_ret, expr_size_ret); + } else if (unit_type == DW_UT_split_compile + || unit_type == DW_UT_split_type) { + return drgn_dwarf4_split_location_list(file, offset, + &cu_die, + address_size, + pc.value, + expr_ret, + expr_size_ret); } else { - return drgn_dwarf4_location_list(module, offset, - &cu_die, address_size, - pc.value, expr_ret, + return drgn_dwarf4_location_list(file, offset, &cu_die, + address_size, pc.value, + expr_ret, expr_size_ret); } } @@ -4016,7 +3271,7 @@ struct drgn_dwarf_expression_context { struct binary_buffer bb; const char *start; struct drgn_program *prog; - struct drgn_debug_info_module *module; + struct drgn_elf_file *file; uint8_t address_size; Dwarf_Die cu_die; const char *cu_addr_base; @@ -4030,24 +3285,24 @@ drgn_dwarf_expression_buffer_error(struct binary_buffer *bb, const char *pos, { struct drgn_dwarf_expression_context *ctx = container_of(bb, struct drgn_dwarf_expression_context, bb); - return drgn_error_debug_info(ctx->module, pos, message); + return drgn_elf_file_section_error(ctx->file, NULL, NULL, pos, message); } static inline struct drgn_error * drgn_dwarf_expression_context_init(struct drgn_dwarf_expression_context *ctx, struct drgn_program *prog, - struct drgn_debug_info_module *module, - Dwarf_CU *cu, Dwarf_Die *function, + struct drgn_elf_file *file, Dwarf_CU *cu, + Dwarf_Die *function, const struct drgn_register_state *regs, const char *expr, size_t expr_size) { struct drgn_error *err; binary_buffer_init(&ctx->bb, expr, expr_size, - drgn_platform_is_little_endian(&module->platform), + drgn_elf_file_is_little_endian(file), drgn_dwarf_expression_buffer_error); ctx->start = expr; ctx->prog = prog; - ctx->module = module; + ctx->file = file; if (cu) { if (!dwarf_cu_die(cu, &ctx->cu_die, NULL, NULL, &ctx->address_size, NULL, NULL, NULL)) @@ -4056,8 +3311,7 @@ drgn_dwarf_expression_context_init(struct drgn_dwarf_expression_context *ctx, return err; } else { ctx->cu_die.addr = NULL; - ctx->address_size = - drgn_platform_address_size(&module->platform); + ctx->address_size = drgn_elf_file_address_size(file); } ctx->cu_addr_base = NULL; ctx->function = function; @@ -4066,9 +3320,8 @@ drgn_dwarf_expression_context_init(struct drgn_dwarf_expression_context *ctx, } static struct drgn_error * -drgn_dwarf_frame_base(struct drgn_program *prog, - struct drgn_debug_info_module *module, Dwarf_Die *die, - const struct drgn_register_state *regs, +drgn_dwarf_frame_base(struct drgn_program *prog, struct drgn_elf_file *file, + Dwarf_Die *die, const struct drgn_register_state *regs, int *remaining_ops, uint64_t *ret); /* @@ -4083,23 +3336,25 @@ drgn_eval_dwarf_expression(struct drgn_dwarf_expression_context *ctx, int *remaining_ops) { struct drgn_error *err; - const struct drgn_platform *platform = &ctx->module->platform; - bool little_endian = drgn_platform_is_little_endian(platform); + bool little_endian = + drgn_elf_file_is_little_endian(ctx->file); uint8_t address_size = ctx->address_size; uint8_t address_bits = address_size * CHAR_BIT; uint64_t address_mask = uint_max(address_size); + const struct drgn_register_layout *register_layout = + ctx->file->platform.arch->register_layout; drgn_register_number (*dwarf_regno_to_internal)(uint64_t) = - platform->arch->dwarf_regno_to_internal; + ctx->file->platform.arch->dwarf_regno_to_internal; #define CHECK(n) do { \ size_t _n = (n); \ - if (stack->size < _n) { \ + if (uint64_vector_size(stack) < _n) { \ return binary_buffer_error(&ctx->bb, \ "DWARF expression stack underflow"); \ } \ } while (0) -#define ELEM(i) stack->data[stack->size - 1 - (i)] +#define ELEM(i) *uint64_vector_at(stack, uint64_vector_size(stack) - 1 - (i)) #define PUSH(x) do { \ uint64_t push = (x); \ @@ -4194,11 +3449,13 @@ drgn_eval_dwarf_expression(struct drgn_dwarf_expression_context *ctx, break; case DW_OP_addrx: case DW_OP_constx: + case DW_OP_GNU_addr_index: + case DW_OP_GNU_const_index: if (!ctx->cu_die.addr) { ctx->bb.pos = ctx->bb.prev; return NULL; } - if ((err = drgn_dwarf_next_addrx(&ctx->bb, ctx->module, + if ((err = drgn_dwarf_next_addrx(&ctx->bb, ctx->file, &ctx->cu_die, address_size, &ctx->cu_addr_base, @@ -4208,7 +3465,7 @@ drgn_eval_dwarf_expression(struct drgn_dwarf_expression_context *ctx, break; /* Register values. */ case DW_OP_fbreg: { - err = drgn_dwarf_frame_base(ctx->prog, ctx->module, + err = drgn_dwarf_frame_base(ctx->prog, ctx->file, ctx->function, ctx->regs, remaining_ops, &uvalue); if (err) @@ -4236,7 +3493,7 @@ drgn_eval_dwarf_expression(struct drgn_dwarf_expression_context *ctx, if (!drgn_register_state_has_register(ctx->regs, regno)) return &drgn_not_found; const struct drgn_register_layout *layout = - &platform->arch->register_layout[regno]; + ®ister_layout[regno]; copy_lsbytes(&uvalue, sizeof(uvalue), HOST_LITTLE_ENDIAN, &ctx->regs->buf[layout->offset], @@ -4255,7 +3512,7 @@ drgn_eval_dwarf_expression(struct drgn_dwarf_expression_context *ctx, break; case DW_OP_drop: CHECK(1); - stack->size--; + uint64_vector_pop(stack); break; case DW_OP_pick: { uint8_t index; @@ -4335,12 +3592,12 @@ drgn_eval_dwarf_expression(struct drgn_dwarf_expression_context *ctx, #define BINOP(op) do { \ CHECK(2); \ ELEM(1) = ELEM(1) op ELEM(0); \ - stack->size--; \ + uint64_vector_pop(stack); \ } while (0) #define BINOP_MASK(op) do { \ CHECK(2); \ ELEM(1) = (ELEM(1) op ELEM(0)) & address_mask; \ - stack->size--; \ + uint64_vector_pop(stack); \ } while (0) case DW_OP_abs: CHECK(1); @@ -4359,7 +3616,7 @@ drgn_eval_dwarf_expression(struct drgn_dwarf_expression_context *ctx, ELEM(1) = ((truncate_signed(ELEM(1), address_bits) / truncate_signed(ELEM(0), address_bits)) & address_mask); - stack->size--; + uint64_vector_pop(stack); break; case DW_OP_minus: BINOP_MASK(-); @@ -4371,7 +3628,7 @@ drgn_eval_dwarf_expression(struct drgn_dwarf_expression_context *ctx, "modulo by zero in DWARF expression"); } ELEM(1) = ELEM(1) % ELEM(0); - stack->size--; + uint64_vector_pop(stack); break; case DW_OP_mul: BINOP_MASK(*); @@ -4401,7 +3658,7 @@ drgn_eval_dwarf_expression(struct drgn_dwarf_expression_context *ctx, ELEM(1) = (ELEM(1) << ELEM(0)) & address_mask; else ELEM(1) = 0; - stack->size--; + uint64_vector_pop(stack); break; case DW_OP_shr: CHECK(2); @@ -4409,7 +3666,7 @@ drgn_eval_dwarf_expression(struct drgn_dwarf_expression_context *ctx, ELEM(1) >>= ELEM(0); else ELEM(1) = 0; - stack->size--; + uint64_vector_pop(stack); break; case DW_OP_shra: CHECK(2); @@ -4422,7 +3679,7 @@ drgn_eval_dwarf_expression(struct drgn_dwarf_expression_context *ctx, } else { ELEM(1) = 0; } - stack->size--; + uint64_vector_pop(stack); break; case DW_OP_xor: BINOP(^); @@ -4435,7 +3692,7 @@ drgn_eval_dwarf_expression(struct drgn_dwarf_expression_context *ctx, CHECK(2); \ ELEM(1) = (truncate_signed(ELEM(1), address_bits) op \ truncate_signed(ELEM(0), address_bits)); \ - stack->size--; \ + uint64_vector_pop(stack); \ } while (0) case DW_OP_le: RELOP(<=); @@ -4473,10 +3730,10 @@ drgn_eval_dwarf_expression(struct drgn_dwarf_expression_context *ctx, case DW_OP_bra: CHECK(1); if (ELEM(0)) { - stack->size--; + uint64_vector_pop(stack); goto branch; } else { - stack->size--; + uint64_vector_pop(stack); if ((err = binary_buffer_skip(&ctx->bb, 2))) return err; } @@ -4484,6 +3741,16 @@ drgn_eval_dwarf_expression(struct drgn_dwarf_expression_context *ctx, /* Special operations. */ case DW_OP_nop: break; + case DW_OP_entry_value: + case DW_OP_GNU_entry_value: + // TODO: DW_OP_(GNU_)entry_value followed by + // DW_OP_reg means the value of the register when the + // current subprogram was entered. We could recover this + // by finding the DW_TAG_(GNU_)call_site for the return + // address and using the DW_AT_(GNU_)call_value of a + // DW_TAG_(GNU_)call_parameter with a DW_AT_location + // matching that register. + return &drgn_not_found; /* Location description operations. */ case DW_OP_reg0 ... DW_OP_reg31: case DW_OP_regx: @@ -4499,7 +3766,6 @@ drgn_eval_dwarf_expression(struct drgn_dwarf_expression_context *ctx, * * - DW_OP_push_object_address * - DW_OP_form_tls_address - * - DW_OP_entry_value * DW_OP_implicit_pointer * - Procedure calls: DW_OP_call2, DW_OP_call4, DW_OP_call_ref. * - Typed operations: DW_OP_const_type, DW_OP_regval_type, @@ -4523,15 +3789,16 @@ drgn_eval_dwarf_expression(struct drgn_dwarf_expression_context *ctx, } static struct drgn_error * -drgn_dwarf_frame_base(struct drgn_program *prog, - struct drgn_debug_info_module *module, Dwarf_Die *die, - const struct drgn_register_state *regs, +drgn_dwarf_frame_base(struct drgn_program *prog, struct drgn_elf_file *file, + Dwarf_Die *die, const struct drgn_register_state *regs, int *remaining_ops, uint64_t *ret) { struct drgn_error *err; - bool little_endian = drgn_platform_is_little_endian(&module->platform); + bool little_endian = drgn_elf_file_is_little_endian(file); + const struct drgn_register_layout *register_layout = + file->platform.arch->register_layout; drgn_register_number (*dwarf_regno_to_internal)(uint64_t) = - module->platform.arch->dwarf_regno_to_internal; + file->platform.arch->dwarf_regno_to_internal; if (!die) return &drgn_not_found; @@ -4540,24 +3807,25 @@ drgn_dwarf_frame_base(struct drgn_program *prog, return &drgn_not_found; const char *expr; size_t expr_size; - err = drgn_dwarf_location(module, attr, regs, &expr, &expr_size); + err = drgn_dwarf_location(file, attr, regs, &expr, &expr_size); if (err) return err; struct drgn_dwarf_expression_context ctx; - if ((err = drgn_dwarf_expression_context_init(&ctx, prog, module, - die->cu, NULL, regs, expr, + if ((err = drgn_dwarf_expression_context_init(&ctx, prog, file, die->cu, + NULL, regs, expr, expr_size))) return err; - struct uint64_vector stack = VECTOR_INIT; + _cleanup_(uint64_vector_deinit) + struct uint64_vector stack = VECTOR_INIT; for (;;) { err = drgn_eval_dwarf_expression(&ctx, &stack, remaining_ops); if (err) - goto out; + return err; if (binary_buffer_has_next(&ctx.bb)) { uint8_t opcode; if ((err = binary_buffer_next_u8(&ctx.bb, &opcode))) - goto out; + return err; uint64_t dwarf_regno; switch (opcode) { @@ -4567,22 +3835,18 @@ drgn_dwarf_frame_base(struct drgn_program *prog, case DW_OP_regx: if ((err = binary_buffer_next_uleb128(&ctx.bb, &dwarf_regno))) - goto out; + return err; reg: { - if (!regs) { - err = &drgn_not_found; - goto out; - } + if (!regs) + return &drgn_not_found; drgn_register_number regno = dwarf_regno_to_internal(dwarf_regno); if (!drgn_register_state_has_register(regs, - regno)) { - err = &drgn_not_found; - goto out; - } + regno)) + return &drgn_not_found; const struct drgn_register_layout *layout = - &prog->platform.arch->register_layout[regno]; + ®ister_layout[regno]; /* * Note that this doesn't mask the address since * the caller does that. @@ -4592,31 +3856,24 @@ drgn_dwarf_frame_base(struct drgn_program *prog, ®s->buf[layout->offset], layout->size, little_endian); if (binary_buffer_has_next(&ctx.bb)) { - err = binary_buffer_error(&ctx.bb, - "stray operations in DW_AT_frame_base expression"); + return binary_buffer_error(&ctx.bb, + "stray operations in DW_AT_frame_base expression"); } else { - err = NULL; + return NULL; } - goto out; } default: - err = binary_buffer_error(&ctx.bb, - "invalid opcode %#" PRIx8 " for DW_AT_frame_base expression", - opcode); - goto out; + return binary_buffer_error(&ctx.bb, + "invalid opcode %#" PRIx8 " for DW_AT_frame_base expression", + opcode); } - } else if (stack.size) { - *ret = stack.data[stack.size - 1]; - err = NULL; - break; + } else if (!uint64_vector_empty(&stack)) { + *ret = *uint64_vector_last(&stack); + return NULL; } else { - err = &drgn_not_found; - break; + return &drgn_not_found; } } -out: - uint64_vector_deinit(&stack); - return err; } /* @@ -4733,7 +3990,7 @@ static int dwarf_flag_integrate(Dwarf_Die *die, unsigned int name, bool *ret) * type. * * @param[in] dbinfo Debugging information. - * @param[in] module Module containing @p die. + * @param[in] file File containing @p die. * @param[in] die DIE to parse. * @param[in] can_be_incomplete_array Whether the type can be an incomplete * array type. If this is @c false and the type appears to be an incomplete @@ -4746,8 +4003,8 @@ static int dwarf_flag_integrate(Dwarf_Die *die, unsigned int name, bool *ret) */ static struct drgn_error * drgn_type_from_dwarf_internal(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, - Dwarf_Die *die, bool can_be_incomplete_array, + struct drgn_elf_file *file, Dwarf_Die *die, + bool can_be_incomplete_array, bool *is_incomplete_array_ret, struct drgn_qualified_type *ret); @@ -4755,17 +4012,16 @@ drgn_type_from_dwarf_internal(struct drgn_debug_info *dbinfo, * Parse a type from a DWARF debugging information entry. * * @param[in] dbinfo Debugging information. - * @param[in] module Module containing @p die. + * @param[in] file File containing @p die. * @param[in] die DIE to parse. * @param[out] ret Returned type. * @return @c NULL on success, non-@c NULL on error. */ static inline struct drgn_error * -drgn_type_from_dwarf(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, Dwarf_Die *die, - struct drgn_qualified_type *ret) +drgn_type_from_dwarf(struct drgn_debug_info *dbinfo, struct drgn_elf_file *file, + Dwarf_Die *die, struct drgn_qualified_type *ret) { - return drgn_type_from_dwarf_internal(dbinfo, module, die, true, NULL, + return drgn_type_from_dwarf_internal(dbinfo, file, die, true, NULL, ret); } @@ -4774,7 +4030,7 @@ drgn_type_from_dwarf(struct drgn_debug_info *dbinfo, * information entry. * * @param[in] dbinfo Debugging information. - * @param[in] module Module containing @p die. + * @param[in] file File containing @p die. * @param[in] die DIE with @c DW_AT_type attribute. * @param[in] lang Language of @p die if it is already known, @c NULL if it * should be determined from @p die. @@ -4788,14 +4044,14 @@ drgn_type_from_dwarf(struct drgn_debug_info *dbinfo, */ static struct drgn_error * drgn_type_from_dwarf_attr(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, Dwarf_Die *die, + struct drgn_elf_file *file, Dwarf_Die *die, const struct drgn_language *lang, bool can_be_void, bool can_be_incomplete_array, bool *is_incomplete_array_ret, struct drgn_qualified_type *ret) { struct drgn_error *err; - char tag_buf[DW_TAG_BUF_LEN]; + char tag_buf[DW_TAG_STR_BUF_LEN]; Dwarf_Attribute attr_mem; Dwarf_Attribute *attr; @@ -4823,20 +4079,19 @@ drgn_type_from_dwarf_attr(struct drgn_debug_info *dbinfo, dwarf_tag_str(die, tag_buf)); } - return drgn_type_from_dwarf_internal(dbinfo, module, &type_die, + return drgn_type_from_dwarf_internal(dbinfo, file, &type_die, can_be_incomplete_array, is_incomplete_array_ret, ret); } static struct drgn_error * drgn_object_from_dwarf_enumerator(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, - Dwarf_Die *die, const char *name, - struct drgn_object *ret) + struct drgn_elf_file *file, Dwarf_Die *die, + const char *name, struct drgn_object *ret) { struct drgn_error *err; struct drgn_qualified_type qualified_type; - err = drgn_type_from_dwarf(dbinfo, module, die, &qualified_type); + err = drgn_type_from_dwarf(dbinfo, file, die, &qualified_type); if (err) return err; const struct drgn_type_enumerator *enumerators = @@ -4860,22 +4115,20 @@ drgn_object_from_dwarf_enumerator(struct drgn_debug_info *dbinfo, static struct drgn_error * drgn_object_from_dwarf_subprogram(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, - Dwarf_Die *die, struct drgn_object *ret) + struct drgn_elf_file *file, Dwarf_Die *die, + struct drgn_object *ret) { struct drgn_qualified_type qualified_type; - struct drgn_error *err = drgn_type_from_dwarf(dbinfo, module, die, + struct drgn_error *err = drgn_type_from_dwarf(dbinfo, file, die, &qualified_type); if (err) return err; Dwarf_Addr low_pc; if (dwarf_lowpc(die, &low_pc) == -1) return drgn_object_set_absent(ret, qualified_type, 0); - Dwarf_Addr bias; - dwfl_module_info(module->dwfl_module, NULL, NULL, NULL, &bias, NULL, - NULL, NULL); - return drgn_object_set_reference(ret, qualified_type, low_pc + bias, 0, - 0); + return drgn_object_set_reference(ret, qualified_type, + low_pc + file->module->debug_file_bias, + 0, 0); } static struct drgn_error *read_bits(struct drgn_program *prog, void *dst, @@ -4947,8 +4200,7 @@ static struct drgn_error *read_bits(struct drgn_program *prog, void *dst, static struct drgn_error * drgn_object_from_dwarf_location(struct drgn_program *prog, - struct drgn_debug_info_module *module, - Dwarf_Die *die, + struct drgn_elf_file *file, Dwarf_Die *die, struct drgn_qualified_type qualified_type, const char *expr, size_t expr_size, Dwarf_Die *function_die, @@ -4956,10 +4208,12 @@ drgn_object_from_dwarf_location(struct drgn_program *prog, struct drgn_object *ret) { struct drgn_error *err; - bool little_endian = drgn_platform_is_little_endian(&module->platform); - uint64_t address_mask = drgn_platform_address_mask(&module->platform); + bool little_endian = drgn_elf_file_is_little_endian(file); + uint64_t address_mask = drgn_elf_file_address_mask(file); + const struct drgn_register_layout *register_layout = + file->platform.arch->register_layout; drgn_register_number (*dwarf_regno_to_internal)(uint64_t) = - module->platform.arch->dwarf_regno_to_internal; + file->platform.arch->dwarf_regno_to_internal; struct drgn_object_type type; err = drgn_object_type(qualified_type, 0, &type); @@ -4976,13 +4230,13 @@ drgn_object_from_dwarf_location(struct drgn_program *prog, int remaining_ops = MAX_DWARF_EXPR_OPS; struct drgn_dwarf_expression_context ctx; - if ((err = drgn_dwarf_expression_context_init(&ctx, prog, module, - die->cu, function_die, - regs, expr, expr_size))) + if ((err = drgn_dwarf_expression_context_init(&ctx, prog, file, die->cu, + function_die, regs, expr, + expr_size))) return err; struct uint64_vector stack = VECTOR_INIT; do { - stack.size = 0; + uint64_vector_clear(&stack); err = drgn_eval_dwarf_expression(&ctx, &stack, &remaining_ops); if (err == &drgn_not_found) goto absent; @@ -5016,7 +4270,7 @@ drgn_object_from_dwarf_location(struct drgn_program *prog, regno)) goto absent; const struct drgn_register_layout *layout = - &prog->platform.arch->register_layout[regno]; + ®ister_layout[regno]; src = ®s->buf[layout->offset]; src_size = layout->size; break; @@ -5034,14 +4288,14 @@ drgn_object_from_dwarf_location(struct drgn_program *prog, ctx.bb.pos += uvalue; break; case DW_OP_stack_value: - if (!stack.size) + if (uint64_vector_empty(&stack)) goto absent; if (little_endian != HOST_LITTLE_ENDIAN) { - stack.data[stack.size - 1] = - bswap_64(stack.data[stack.size - 1]); + *uint64_vector_last(&stack) = + bswap_64(*uint64_vector_last(&stack)); } - src = &stack.data[stack.size - 1]; - src_size = sizeof(stack.data[0]); + src = uint64_vector_last(&stack); + src_size = sizeof(uint64_t); break; default: ctx.bb.pos = ctx.bb.prev; @@ -5153,9 +4407,10 @@ drgn_object_from_dwarf_location(struct drgn_program *prog, (const char *)src + (piece_bit_offset / 8), piece_bit_offset % 8, copy_bit_size, little_endian); - } else if (stack.size) { + } else if (!uint64_vector_empty(&stack)) { uint64_t piece_address = - ((stack.data[stack.size - 1] + piece_bit_offset / 8) + ((*uint64_vector_last(&stack) + + piece_bit_offset / 8) & address_mask); piece_bit_offset %= 8; if (bit_pos > 0 && bit_offset >= 0) { @@ -5224,8 +4479,8 @@ drgn_object_from_dwarf_location(struct drgn_program *prog, err = NULL; } else if (bit_offset >= 0) { Dwarf_Addr start, end, bias; - dwfl_module_info(module->dwfl_module, NULL, &start, &end, &bias, - NULL, NULL, NULL); + dwfl_module_info(file->module->dwfl_module, NULL, &start, &end, + &bias, NULL, NULL, NULL); /* * If the address is not in the module's address range, then * it's probably something special like a Linux per-CPU variable @@ -5292,15 +4547,14 @@ drgn_object_from_dwarf_constant(struct drgn_debug_info *dbinfo, Dwarf_Die *die, struct drgn_error * drgn_object_from_dwarf(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, - Dwarf_Die *die, Dwarf_Die *type_die, - Dwarf_Die *function_die, + struct drgn_elf_file *file, Dwarf_Die *die, + Dwarf_Die *type_die, Dwarf_Die *function_die, const struct drgn_register_state *regs, struct drgn_object *ret) { struct drgn_error *err; if (dwarf_tag(die) == DW_TAG_subprogram) { - return drgn_object_from_dwarf_subprogram(dbinfo, module, die, + return drgn_object_from_dwarf_subprogram(dbinfo, file, die, ret); } /* @@ -5310,10 +4564,10 @@ drgn_object_from_dwarf(struct drgn_debug_info *dbinfo, */ struct drgn_qualified_type qualified_type; if (type_die) { - err = drgn_type_from_dwarf(dbinfo, module, type_die, + err = drgn_type_from_dwarf(dbinfo, file, type_die, &qualified_type); } else { - err = drgn_type_from_dwarf_attr(dbinfo, module, die, NULL, true, + err = drgn_type_from_dwarf_attr(dbinfo, file, die, NULL, true, true, NULL, &qualified_type); } if (err) @@ -5322,8 +4576,7 @@ drgn_object_from_dwarf(struct drgn_debug_info *dbinfo, const char *expr; size_t expr_size; if ((attr = dwarf_attr_integrate(die, DW_AT_location, &attr_mem))) { - err = drgn_dwarf_location(module, attr, regs, &expr, - &expr_size); + err = drgn_dwarf_location(file, attr, regs, &expr, &expr_size); if (err) return err; } else if ((attr = dwarf_attr_integrate(die, DW_AT_const_value, @@ -5335,20 +4588,91 @@ drgn_object_from_dwarf(struct drgn_debug_info *dbinfo, expr = NULL; expr_size = 0; } - return drgn_object_from_dwarf_location(dbinfo->prog, module, die, + return drgn_object_from_dwarf_location(dbinfo->prog, file, die, qualified_type, expr, expr_size, function_die, regs, ret); } +DEFINE_VECTOR(const_char_p_vector, const char *); + +static struct drgn_error *add_dwarf_enumerators(Dwarf_Die *enumeration_type, + struct const_char_p_vector *vec) +{ + Dwarf_Die child; + int r = dwarf_child(enumeration_type, &child); + while (r == 0) { + if (dwarf_tag(&child) == DW_TAG_enumerator) { + const char *die_name = dwarf_diename(&child); + if (!die_name) + continue; + if (!const_char_p_vector_append(vec, &die_name)) + return &drgn_enomem; + } + r = dwarf_siblingof(&child, &child); + } + if (r < 0) + return drgn_error_libdw(); + return NULL; +} + +struct drgn_error *drgn_dwarf_scopes_names(Dwarf_Die *scopes, + size_t num_scopes, + const char ***names_ret, + size_t *count_ret) +{ + struct drgn_error *err; + Dwarf_Die die; + _cleanup_(const_char_p_vector_deinit) + struct const_char_p_vector vec = VECTOR_INIT; + for (size_t scope = 0; scope < num_scopes; scope++) { + if (dwarf_child(&scopes[scope], &die) != 0) + continue; + do { + switch (dwarf_tag(&die)) { + case DW_TAG_variable: + case DW_TAG_formal_parameter: + case DW_TAG_subprogram: { + const char *die_name = dwarf_diename(&die); + if (!die_name) + continue; + if (!const_char_p_vector_append(&vec, + &die_name)) + return &drgn_enomem; + break; + } + case DW_TAG_enumeration_type: { + bool enum_class; + if (dwarf_flag_integrate(&die, DW_AT_enum_class, + &enum_class)) + return drgn_error_libdw(); + if (!enum_class) { + err = add_dwarf_enumerators(&die, &vec); + if (err) + return err; + } + break; + } + default: + continue; + } + } while (dwarf_siblingof(&die, &die) == 0); + } + const_char_p_vector_shrink_to_fit(&vec); + const_char_p_vector_steal(&vec, names_ret, count_ret); + return NULL; +} + static struct drgn_error *find_dwarf_enumerator(Dwarf_Die *enumeration_type, const char *name, Dwarf_Die *ret) { int r = dwarf_child(enumeration_type, ret); while (r == 0) { - if (dwarf_tag(ret) == DW_TAG_enumerator && - strcmp(dwarf_diename(ret), name) == 0) - return NULL; + if (dwarf_tag(ret) == DW_TAG_enumerator) { + const char *die_name = dwarf_diename(ret); + if (die_name && strcmp(die_name, name) == 0) + return NULL; + } r = dwarf_siblingof(ret, ret); } if (r < 0) @@ -5373,8 +4697,9 @@ struct drgn_error *drgn_find_in_dwarf_scopes(Dwarf_Die *scopes, switch (dwarf_tag(&die)) { case DW_TAG_variable: case DW_TAG_formal_parameter: - case DW_TAG_subprogram: - if (strcmp(dwarf_diename(&die), name) == 0) { + case DW_TAG_subprogram: { + const char *die_name = dwarf_diename(&die); + if (die_name && strcmp(die_name, name) == 0) { *die_ret = die; bool declaration; if (dwarf_flag(&die, DW_AT_declaration, @@ -5386,6 +4711,7 @@ struct drgn_error *drgn_find_in_dwarf_scopes(Dwarf_Die *scopes, return NULL; } break; + } case DW_TAG_enumeration_type: { bool enum_class; if (dwarf_flag_integrate(&die, DW_AT_enum_class, @@ -5418,7 +4744,7 @@ struct drgn_error *drgn_find_in_dwarf_scopes(Dwarf_Die *scopes, static struct drgn_error * drgn_base_type_from_dwarf(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, Dwarf_Die *die, + struct drgn_elf_file *file, Dwarf_Die *die, const struct drgn_language *lang, struct drgn_type **ret) { @@ -5493,33 +4819,27 @@ find_namespace_containing_die(struct drgn_debug_info *dbinfo, return err; for (size_t i = 0; i < num_ancestors; i++) { - if (dwarf_tag(&ancestors[i]) != DW_TAG_namespace) + switch (dwarf_tag(&ancestors[i])) { +#define X(name) case DW_TAG_##name: break; + DRGN_DWARF_INDEX_NAMESPACE_TAGS +#undef X + default: continue; + } + Dwarf_Attribute attr_mem, *attr; if (!(attr = dwarf_attr_integrate(&ancestors[i], DW_AT_name, &attr_mem))) continue; const char *name = dwarf_formstring(attr); if (!name) { - err = drgn_error_create(DRGN_ERROR_OTHER, - "DW_TAG_namespace has invalid DW_AT_name"); + err = drgn_error_libdw(); goto out; } - struct drgn_dwarf_index_iterator it; - const uint64_t ns_tag = DW_TAG_namespace; - err = drgn_dwarf_index_iterator_init(&it, ns, name, - strlen(name), &ns_tag, 1); + err = drgn_namespace_find_child(ns, name, strlen(name), &ns); if (err) goto out; - - struct drgn_dwarf_index_die *index_die = - drgn_dwarf_index_iterator_next(&it); - if (!index_die) { - err = &drgn_not_found; - goto out; - } - ns = index_die->namespace; } *ret = ns; out: @@ -5535,7 +4855,7 @@ find_namespace_containing_die(struct drgn_debug_info *dbinfo, * returns an error. */ static struct drgn_error * -drgn_debug_info_find_complete(struct drgn_debug_info *dbinfo, uint64_t tag, +drgn_debug_info_find_complete(struct drgn_debug_info *dbinfo, int tag, const char *name, Dwarf_Die *incomplete_die, const struct drgn_language *lang, struct drgn_type **ret) @@ -5547,9 +4867,17 @@ drgn_debug_info_find_complete(struct drgn_debug_info *dbinfo, uint64_t tag, if (err) return err; + enum drgn_dwarf_index_tag dwarf_index_tag; + switch (tag) { +#define X(name) case DW_TAG_##name: dwarf_index_tag = DRGN_DWARF_INDEX_##name; break; + DRGN_DWARF_INDEX_TAGS +#undef X + default: + return NULL; + } struct drgn_dwarf_index_iterator it; - err = drgn_dwarf_index_iterator_init(&it, ns, name, strlen(name), &tag, - 1); + err = drgn_dwarf_index_iterator_init(&it, ns, name, strlen(name), + &dwarf_index_tag, 1); if (err) return err; @@ -5558,24 +4886,13 @@ drgn_debug_info_find_complete(struct drgn_debug_info *dbinfo, uint64_t tag, * contain DIEs with DW_AT_declaration, so this will always be a * complete type. */ - struct drgn_dwarf_index_die *index_die = - drgn_dwarf_index_iterator_next(&it); - if (!index_die) - return &drgn_not_found; - /* - * Look for another matching DIE. If there is one, then we can't be sure - * which type this is, so leave it incomplete rather than guessing. - */ - if (drgn_dwarf_index_iterator_next(&it)) + Dwarf_Die die; + struct drgn_elf_file *file; + if (!drgn_dwarf_index_iterator_next(&it, &die, &file)) return &drgn_not_found; - Dwarf_Die die; - err = drgn_dwarf_index_get_die(index_die, &die); - if (err) - return err; struct drgn_qualified_type qualified_type; - err = drgn_type_from_dwarf(dbinfo, index_die->module, &die, - &qualified_type); + err = drgn_type_from_dwarf(dbinfo, file, &die, &qualified_type); if (err) return err; *ret = qualified_type.type; @@ -5583,7 +4900,7 @@ drgn_debug_info_find_complete(struct drgn_debug_info *dbinfo, uint64_t tag, } struct drgn_dwarf_member_thunk_arg { - struct drgn_debug_info_module *module; + struct drgn_elf_file *file; Dwarf_Die die; bool can_be_incomplete_array; }; @@ -5596,7 +4913,7 @@ drgn_dwarf_member_thunk_fn(struct drgn_object *res, void *arg_) if (res) { struct drgn_qualified_type qualified_type; err = drgn_type_from_dwarf_attr(drgn_object_program(res)->dbinfo, - arg->module, &arg->die, NULL, + arg->file, &arg->die, NULL, false, arg->can_be_incomplete_array, NULL, &qualified_type); @@ -5819,9 +5136,8 @@ parse_member_offset(Dwarf_Die *die, union drgn_lazy_object *member_object, } static struct drgn_error * -parse_member(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, Dwarf_Die *die, - bool little_endian, bool can_be_incomplete_array, +parse_member(struct drgn_debug_info *dbinfo, struct drgn_elf_file *file, + Dwarf_Die *die, bool little_endian, bool can_be_incomplete_array, struct drgn_compound_type_builder *builder) { struct drgn_error *err; @@ -5842,7 +5158,7 @@ parse_member(struct drgn_debug_info *dbinfo, malloc(sizeof(*thunk_arg)); if (!thunk_arg) return &drgn_enomem; - thunk_arg->module = module; + thunk_arg->file = file; thunk_arg->die = *die; thunk_arg->can_be_incomplete_array = can_be_incomplete_array; @@ -5868,7 +5184,7 @@ parse_member(struct drgn_debug_info *dbinfo, } struct drgn_dwarf_die_thunk_arg { - struct drgn_debug_info_module *module; + struct drgn_elf_file *file; Dwarf_Die die; }; @@ -5880,7 +5196,7 @@ drgn_dwarf_template_type_parameter_thunk_fn(struct drgn_object *res, void *arg_) if (res) { struct drgn_qualified_type qualified_type; err = drgn_type_from_dwarf_attr(drgn_object_program(res)->dbinfo, - arg->module, &arg->die, NULL, + arg->file, &arg->die, NULL, true, true, NULL, &qualified_type); if (err) @@ -5902,7 +5218,7 @@ drgn_dwarf_template_value_parameter_thunk_fn(struct drgn_object *res, struct drgn_dwarf_die_thunk_arg *arg = arg_; if (res) { err = drgn_object_from_dwarf(drgn_object_program(res)->dbinfo, - arg->module, &arg->die, NULL, NULL, + arg->file, &arg->die, NULL, NULL, NULL, res); if (err) return err; @@ -5912,12 +5228,23 @@ drgn_dwarf_template_value_parameter_thunk_fn(struct drgn_object *res, } static struct drgn_error * -parse_template_parameter(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, Dwarf_Die *die, - drgn_object_thunk_fn *thunk_fn, - struct drgn_template_parameters_builder *builder) +maybe_parse_template_parameter(struct drgn_debug_info *dbinfo, + struct drgn_elf_file *file, Dwarf_Die *die, + struct drgn_template_parameters_builder *builder) { - char tag_buf[DW_TAG_BUF_LEN]; + drgn_object_thunk_fn *thunk_fn; + switch (dwarf_tag(die)) { + case DW_TAG_template_type_parameter: + thunk_fn = drgn_dwarf_template_type_parameter_thunk_fn; + break; + case DW_TAG_template_value_parameter: + thunk_fn = drgn_dwarf_template_value_parameter_thunk_fn; + break; + default: + return NULL; + } + + char tag_buf[DW_TAG_STR_BUF_LEN]; Dwarf_Attribute attr_mem, *attr; const char *name; @@ -5943,7 +5270,7 @@ parse_template_parameter(struct drgn_debug_info *dbinfo, malloc(sizeof(*thunk_arg)); if (!thunk_arg) return &drgn_enomem; - thunk_arg->module = module; + thunk_arg->file = file; thunk_arg->die = *die; union drgn_lazy_object argument; @@ -5958,14 +5285,35 @@ parse_template_parameter(struct drgn_debug_info *dbinfo, return err; } +static struct drgn_error * +drgn_parse_template_parameter_pack(struct drgn_debug_info *dbinfo, + struct drgn_elf_file *file, Dwarf_Die *die, + struct drgn_template_parameters_builder *builder) +{ + struct drgn_error *err; + Dwarf_Die child; + int r = dwarf_child(die, &child); + while (r == 0) { + err = maybe_parse_template_parameter(dbinfo, file, &child, builder); + if (err) + return err; + r = dwarf_siblingof(&child, &child); + } + if (r == -1) { + return drgn_error_create(DRGN_ERROR_OTHER, + "libdw could not parse DIE children"); + } + return NULL; +} + static struct drgn_error * drgn_compound_type_from_dwarf(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, - Dwarf_Die *die, const struct drgn_language *lang, + struct drgn_elf_file *file, Dwarf_Die *die, + const struct drgn_language *lang, enum drgn_type_kind kind, struct drgn_type **ret) { struct drgn_error *err; - char tag_buf[DW_TAG_BUF_LEN]; + char tag_buf[DW_TAG_STR_BUF_LEN]; Dwarf_Attribute attr_mem; Dwarf_Attribute *attr = dwarf_attr_integrate(die, DW_AT_name, @@ -6013,33 +5361,34 @@ drgn_compound_type_from_dwarf(struct drgn_debug_info *dbinfo, } Dwarf_Die member = {}, child; + bool first_member = true; int r = dwarf_child(die, &child); while (r == 0) { switch (dwarf_tag(&child)) { case DW_TAG_member: if (!declaration) { if (member.addr) { - err = parse_member(dbinfo, module, + err = parse_member(dbinfo, file, &member, little_endian, false, &builder); if (err) goto err; + first_member = false; } member = child; } break; case DW_TAG_template_type_parameter: - err = parse_template_parameter(dbinfo, module, &child, - drgn_dwarf_template_type_parameter_thunk_fn, - &builder.template_builder); + case DW_TAG_template_value_parameter: + err = maybe_parse_template_parameter(dbinfo, file, &child, + &builder.template_builder); if (err) goto err; break; - case DW_TAG_template_value_parameter: - err = parse_template_parameter(dbinfo, module, &child, - drgn_dwarf_template_value_parameter_thunk_fn, - &builder.template_builder); + case DW_TAG_GNU_template_parameter_pack: + err = drgn_parse_template_parameter_pack(dbinfo, file, &child, + &builder.template_builder); if (err) goto err; break; @@ -6058,9 +5407,8 @@ drgn_compound_type_from_dwarf(struct drgn_debug_info *dbinfo, * structure with at least one other member. */ if (member.addr) { - err = parse_member(dbinfo, module, &member, little_endian, - kind != DRGN_TYPE_UNION && - builder.members.size > 0, + err = parse_member(dbinfo, file, &member, little_endian, + kind != DRGN_TYPE_UNION && !first_member, &builder); if (err) goto err; @@ -6144,7 +5492,7 @@ enum_compatible_type_fallback(struct drgn_debug_info *dbinfo, static struct drgn_error * drgn_enum_type_from_dwarf(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, Dwarf_Die *die, + struct drgn_elf_file *file, Dwarf_Die *die, const struct drgn_language *lang, struct drgn_type **ret) { @@ -6213,7 +5561,7 @@ drgn_enum_type_from_dwarf(struct drgn_debug_info *dbinfo, goto err; } else { struct drgn_qualified_type qualified_compatible_type; - err = drgn_type_from_dwarf(dbinfo, module, &child, + err = drgn_type_from_dwarf(dbinfo, file, &child, &qualified_compatible_type); if (err) goto err; @@ -6238,8 +5586,8 @@ drgn_enum_type_from_dwarf(struct drgn_debug_info *dbinfo, static struct drgn_error * drgn_typedef_type_from_dwarf(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, - Dwarf_Die *die, const struct drgn_language *lang, + struct drgn_elf_file *file, Dwarf_Die *die, + const struct drgn_language *lang, bool can_be_incomplete_array, bool *is_incomplete_array_ret, struct drgn_type **ret) @@ -6251,7 +5599,7 @@ drgn_typedef_type_from_dwarf(struct drgn_debug_info *dbinfo, } struct drgn_qualified_type aliased_type; - struct drgn_error *err = drgn_type_from_dwarf_attr(dbinfo, module, die, + struct drgn_error *err = drgn_type_from_dwarf_attr(dbinfo, file, die, lang, true, can_be_incomplete_array, is_incomplete_array_ret, @@ -6265,12 +5613,12 @@ drgn_typedef_type_from_dwarf(struct drgn_debug_info *dbinfo, static struct drgn_error * drgn_pointer_type_from_dwarf(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, - Dwarf_Die *die, const struct drgn_language *lang, + struct drgn_elf_file *file, Dwarf_Die *die, + const struct drgn_language *lang, struct drgn_type **ret) { struct drgn_qualified_type referenced_type; - struct drgn_error *err = drgn_type_from_dwarf_attr(dbinfo, module, die, + struct drgn_error *err = drgn_type_from_dwarf_attr(dbinfo, file, die, lang, true, true, NULL, &referenced_type); @@ -6287,10 +5635,11 @@ drgn_pointer_type_from_dwarf(struct drgn_debug_info *dbinfo, } size = word; } else { + // dwarf_diecu() always returns the DIE. We should use + // dwarf_cu_info(), but that requires elfutils >= 0.171. + Dwarf_Die unused; uint8_t address_size; - err = drgn_program_address_size(dbinfo->prog, &address_size); - if (err) - return err; + dwarf_diecu(die, &unused, &address_size, NULL); size = address_size; } @@ -6311,7 +5660,7 @@ struct array_dimension { bool is_complete; }; -DEFINE_VECTOR(array_dimension_vector, struct array_dimension) +DEFINE_VECTOR(array_dimension_vector, struct array_dimension); static struct drgn_error *subrange_length(Dwarf_Die *die, struct array_dimension *dimension) @@ -6336,11 +5685,11 @@ static struct drgn_error *subrange_length(Dwarf_Die *die, dimension->is_complete = true; /* - * GCC emits a DW_FORM_sdata DW_AT_upper_bound of -1 for empty array - * variables without an explicit size (e.g., `int arr[] = {};`). + * GCC emits a DW_FORM_sdata or DW_FORM_data8 DW_AT_upper_bound of -1 + * for empty array variables without an explicit size + * (e.g., `int arr[] = {};`). */ - if (attr->code == DW_AT_upper_bound && attr->form == DW_FORM_sdata && - word == (Dwarf_Word)-1) { + if (attr->code == DW_AT_upper_bound && word == (Dwarf_Word)-1) { dimension->length = 0; } else if (attr->code == DW_AT_upper_bound) { if (word >= UINT64_MAX) { @@ -6360,51 +5709,48 @@ static struct drgn_error *subrange_length(Dwarf_Die *die, static struct drgn_error * drgn_array_type_from_dwarf(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, - Dwarf_Die *die, const struct drgn_language *lang, + struct drgn_elf_file *file, Dwarf_Die *die, + const struct drgn_language *lang, bool can_be_incomplete_array, bool *is_incomplete_array_ret, struct drgn_type **ret) { struct drgn_error *err; - struct array_dimension_vector dimensions = VECTOR_INIT; + _cleanup_(array_dimension_vector_deinit) + struct array_dimension_vector dimensions = VECTOR_INIT; struct array_dimension *dimension; Dwarf_Die child; int r = dwarf_child(die, &child); while (r == 0) { if (dwarf_tag(&child) == DW_TAG_subrange_type) { dimension = array_dimension_vector_append_entry(&dimensions); - if (!dimension) { - err = &drgn_enomem; - goto out; - } + if (!dimension) + return &drgn_enomem; err = subrange_length(&child, dimension); if (err) - goto out; + return err; } r = dwarf_siblingof(&child, &child); } if (r == -1) { - err = drgn_error_create(DRGN_ERROR_OTHER, - "libdw could not parse DIE children"); - goto out; + return drgn_error_create(DRGN_ERROR_OTHER, + "libdw could not parse DIE children"); } - if (!dimensions.size) { + if (array_dimension_vector_empty(&dimensions)) { dimension = array_dimension_vector_append_entry(&dimensions); - if (!dimension) { - err = &drgn_enomem; - goto out; - } + if (!dimension) + return &drgn_enomem; dimension->is_complete = false; } struct drgn_qualified_type element_type; - err = drgn_type_from_dwarf_attr(dbinfo, module, die, lang, false, false, + err = drgn_type_from_dwarf_attr(dbinfo, file, die, lang, false, false, NULL, &element_type); if (err) - goto out; + return err; - *is_incomplete_array_ret = !dimensions.data[0].is_complete; + *is_incomplete_array_ret = + !array_dimension_vector_first(&dimensions)->is_complete; struct drgn_type *type; do { dimension = array_dimension_vector_pop(&dimensions); @@ -6412,7 +5758,8 @@ drgn_array_type_from_dwarf(struct drgn_debug_info *dbinfo, err = drgn_array_type_create(dbinfo->prog, element_type, dimension->length, lang, &type); - } else if (dimensions.size || !can_be_incomplete_array) { + } else if (!array_dimension_vector_empty(&dimensions) + || !can_be_incomplete_array) { err = drgn_array_type_create(dbinfo->prog, element_type, 0, lang, &type); } else { @@ -6421,17 +5768,14 @@ drgn_array_type_from_dwarf(struct drgn_debug_info *dbinfo, lang, &type); } if (err) - goto out; + return err; element_type.type = type; element_type.qualifiers = 0; - } while (dimensions.size); + } while (!array_dimension_vector_empty(&dimensions)); *ret = type; - err = NULL; -out: - array_dimension_vector_deinit(&dimensions); - return err; + return NULL; } static struct drgn_error * @@ -6442,7 +5786,7 @@ drgn_dwarf_formal_parameter_thunk_fn(struct drgn_object *res, void *arg_) if (res) { struct drgn_qualified_type qualified_type; err = drgn_type_from_dwarf_attr(drgn_object_program(res)->dbinfo, - arg->module, &arg->die, NULL, + arg->file, &arg->die, NULL, false, true, NULL, &qualified_type); if (err) @@ -6458,7 +5802,7 @@ drgn_dwarf_formal_parameter_thunk_fn(struct drgn_object *res, void *arg_) static struct drgn_error * parse_formal_parameter(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, Dwarf_Die *die, + struct drgn_elf_file *file, Dwarf_Die *die, struct drgn_function_type_builder *builder) { Dwarf_Attribute attr_mem, *attr; @@ -6477,7 +5821,7 @@ parse_formal_parameter(struct drgn_debug_info *dbinfo, malloc(sizeof(*thunk_arg)); if (!thunk_arg) return &drgn_enomem; - thunk_arg->module = module; + thunk_arg->file = file; thunk_arg->die = *die; union drgn_lazy_object default_argument; @@ -6496,12 +5840,12 @@ parse_formal_parameter(struct drgn_debug_info *dbinfo, static struct drgn_error * drgn_function_type_from_dwarf(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, - Dwarf_Die *die, const struct drgn_language *lang, + struct drgn_elf_file *file, Dwarf_Die *die, + const struct drgn_language *lang, struct drgn_type **ret) { struct drgn_error *err; - char tag_buf[DW_TAG_BUF_LEN]; + char tag_buf[DW_TAG_STR_BUF_LEN]; struct drgn_function_type_builder builder; drgn_function_type_builder_init(&builder, dbinfo->prog); @@ -6518,7 +5862,7 @@ drgn_function_type_from_dwarf(struct drgn_debug_info *dbinfo, tag_buf)); goto err; } - err = parse_formal_parameter(dbinfo, module, &child, + err = parse_formal_parameter(dbinfo, file, &child, &builder); if (err) goto err; @@ -6534,16 +5878,15 @@ drgn_function_type_from_dwarf(struct drgn_debug_info *dbinfo, is_variadic = true; break; case DW_TAG_template_type_parameter: - err = parse_template_parameter(dbinfo, module, &child, - drgn_dwarf_template_type_parameter_thunk_fn, - &builder.template_builder); + case DW_TAG_template_value_parameter: + err = maybe_parse_template_parameter(dbinfo, file, &child, + &builder.template_builder); if (err) goto err; break; - case DW_TAG_template_value_parameter: - err = parse_template_parameter(dbinfo, module, &child, - drgn_dwarf_template_value_parameter_thunk_fn, - &builder.template_builder); + case DW_TAG_GNU_template_parameter_pack: + err = drgn_parse_template_parameter_pack(dbinfo, file, &child, + &builder.template_builder); if (err) goto err; break; @@ -6559,7 +5902,7 @@ drgn_function_type_from_dwarf(struct drgn_debug_info *dbinfo, } struct drgn_qualified_type return_type; - err = drgn_type_from_dwarf_attr(dbinfo, module, die, lang, true, true, + err = drgn_type_from_dwarf_attr(dbinfo, file, die, lang, true, true, NULL, &return_type); if (err) goto err; @@ -6577,8 +5920,8 @@ drgn_function_type_from_dwarf(struct drgn_debug_info *dbinfo, static struct drgn_error * drgn_type_from_dwarf_internal(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, - Dwarf_Die *die, bool can_be_incomplete_array, + struct drgn_elf_file *file, Dwarf_Die *die, + bool can_be_incomplete_array, bool *is_incomplete_array_ret, struct drgn_qualified_type *ret) { @@ -6606,28 +5949,15 @@ drgn_type_from_dwarf_internal(struct drgn_debug_info *dbinfo, if (declaration) { uintptr_t die_addr; if (drgn_dwarf_find_definition(dbinfo, (uintptr_t)die->addr, - &module, &die_addr)) { - Dwarf_Addr bias; - Dwarf *dwarf = dwfl_module_getdwarf(module->dwfl_module, - &bias); - if (!dwarf) - return drgn_error_libdwfl(); - uintptr_t start = - (uintptr_t)module->scn_data[DRGN_SCN_DEBUG_INFO]->d_buf; - size_t size = - module->scn_data[DRGN_SCN_DEBUG_INFO]->d_size; - if (die_addr >= start && die_addr < start + size) { - if (!dwarf_offdie(dwarf, die_addr - start, - &definition_die)) - return drgn_error_libdw(); - } else { - start = (uintptr_t)module->scn_data[DRGN_SCN_DEBUG_TYPES]->d_buf; - /* Assume .debug_types */ - if (!dwarf_offdie_types(dwarf, die_addr - start, - &definition_die)) - return drgn_error_libdw(); - } + &die_addr)) { + struct drgn_dwarf_index_cu *cu = + drgn_dwarf_index_find_cu(dbinfo, die_addr); + definition_die = (Dwarf_Die){ + .addr = (void *)die_addr, + .cu = cu->libdw_cu, + }; die = &definition_die; + file = cu->file; } } @@ -6661,75 +5991,75 @@ drgn_type_from_dwarf_internal(struct drgn_debug_info *dbinfo, entry.value.is_incomplete_array = false; switch (dwarf_tag(die)) { case DW_TAG_const_type: - err = drgn_type_from_dwarf_attr(dbinfo, module, die, lang, true, + err = drgn_type_from_dwarf_attr(dbinfo, file, die, lang, true, can_be_incomplete_array, &entry.value.is_incomplete_array, ret); ret->qualifiers |= DRGN_QUALIFIER_CONST; break; case DW_TAG_restrict_type: - err = drgn_type_from_dwarf_attr(dbinfo, module, die, lang, true, + err = drgn_type_from_dwarf_attr(dbinfo, file, die, lang, true, can_be_incomplete_array, &entry.value.is_incomplete_array, ret); ret->qualifiers |= DRGN_QUALIFIER_RESTRICT; break; case DW_TAG_volatile_type: - err = drgn_type_from_dwarf_attr(dbinfo, module, die, lang, true, + err = drgn_type_from_dwarf_attr(dbinfo, file, die, lang, true, can_be_incomplete_array, &entry.value.is_incomplete_array, ret); ret->qualifiers |= DRGN_QUALIFIER_VOLATILE; break; case DW_TAG_atomic_type: - err = drgn_type_from_dwarf_attr(dbinfo, module, die, lang, true, + err = drgn_type_from_dwarf_attr(dbinfo, file, die, lang, true, can_be_incomplete_array, &entry.value.is_incomplete_array, ret); ret->qualifiers |= DRGN_QUALIFIER_ATOMIC; break; case DW_TAG_base_type: - err = drgn_base_type_from_dwarf(dbinfo, module, die, lang, + err = drgn_base_type_from_dwarf(dbinfo, file, die, lang, &ret->type); break; case DW_TAG_structure_type: - err = drgn_compound_type_from_dwarf(dbinfo, module, die, lang, + err = drgn_compound_type_from_dwarf(dbinfo, file, die, lang, DRGN_TYPE_STRUCT, &ret->type); break; case DW_TAG_union_type: - err = drgn_compound_type_from_dwarf(dbinfo, module, die, lang, + err = drgn_compound_type_from_dwarf(dbinfo, file, die, lang, DRGN_TYPE_UNION, &ret->type); break; case DW_TAG_class_type: - err = drgn_compound_type_from_dwarf(dbinfo, module, die, lang, + err = drgn_compound_type_from_dwarf(dbinfo, file, die, lang, DRGN_TYPE_CLASS, &ret->type); break; case DW_TAG_enumeration_type: - err = drgn_enum_type_from_dwarf(dbinfo, module, die, lang, + err = drgn_enum_type_from_dwarf(dbinfo, file, die, lang, &ret->type); break; case DW_TAG_typedef: - err = drgn_typedef_type_from_dwarf(dbinfo, module, die, lang, + err = drgn_typedef_type_from_dwarf(dbinfo, file, die, lang, can_be_incomplete_array, &entry.value.is_incomplete_array, &ret->type); break; case DW_TAG_pointer_type: - err = drgn_pointer_type_from_dwarf(dbinfo, module, die, lang, + err = drgn_pointer_type_from_dwarf(dbinfo, file, die, lang, &ret->type); break; case DW_TAG_array_type: - err = drgn_array_type_from_dwarf(dbinfo, module, die, lang, + err = drgn_array_type_from_dwarf(dbinfo, file, die, lang, can_be_incomplete_array, &entry.value.is_incomplete_array, &ret->type); break; case DW_TAG_subroutine_type: case DW_TAG_subprogram: - err = drgn_function_type_from_dwarf(dbinfo, module, die, lang, + err = drgn_function_type_from_dwarf(dbinfo, file, die, lang, &ret->type); break; default: @@ -6761,61 +6091,88 @@ drgn_type_from_dwarf_internal(struct drgn_debug_info *dbinfo, return NULL; } -struct drgn_error *drgn_debug_info_find_type(enum drgn_type_kind kind, - const char *name, size_t name_len, +static struct drgn_error * +find_enclosing_namespace(struct drgn_namespace_dwarf_index *global_namespace, + const char **name, size_t *name_len, + struct drgn_namespace_dwarf_index **namespace_ret) +{ + struct drgn_error *err; + + *namespace_ret = global_namespace; + if (*name_len >= 2 && memcmp(*name, "::", 2) == 0) { + /* Explicit global namespace. */ + *name_len -= 2; + *name += 2; + } + + const char *template_parameters_start = memchr(*name, '<', *name_len); + ptrdiff_t searchable_len = + template_parameters_start ? + template_parameters_start - *name: + *name_len; + const char *colons; + while ((colons = memmem(*name, searchable_len, "::", 2))) { + err = drgn_namespace_find_child(*namespace_ret, *name, + colons - *name, namespace_ret); + if (err) + return err; + + size_t chars_consumed = colons + 2 - *name; + searchable_len -= chars_consumed; + *name_len -= chars_consumed; + *name = colons + 2; + } + + return NULL; +} + +struct drgn_error *drgn_debug_info_find_type(uint64_t kinds, const char *name, + size_t name_len, const char *filename, void *arg, struct drgn_qualified_type *ret) { struct drgn_error *err; struct drgn_debug_info *dbinfo = arg; - uint64_t tag; - switch (kind) { - case DRGN_TYPE_INT: - case DRGN_TYPE_BOOL: - case DRGN_TYPE_FLOAT: - tag = DW_TAG_base_type; - break; - case DRGN_TYPE_STRUCT: - tag = DW_TAG_structure_type; - break; - case DRGN_TYPE_UNION: - tag = DW_TAG_union_type; - break; - case DRGN_TYPE_CLASS: - tag = DW_TAG_class_type; - break; - case DRGN_TYPE_ENUM: - tag = DW_TAG_enumeration_type; - break; - case DRGN_TYPE_TYPEDEF: - tag = DW_TAG_typedef; - break; - default: - UNREACHABLE(); - } - + enum drgn_dwarf_index_tag tags[6]; + size_t num_tags = 0; + if (kinds & ((1 << DRGN_TYPE_INT) + | (1 << DRGN_TYPE_BOOL) + | (1 << DRGN_TYPE_FLOAT))) + tags[num_tags++] = DRGN_DWARF_INDEX_base_type; + if (kinds & (1 << DRGN_TYPE_STRUCT)) + tags[num_tags++] = DRGN_DWARF_INDEX_structure_type; + if (kinds & (1 << DRGN_TYPE_UNION)) + tags[num_tags++] = DRGN_DWARF_INDEX_union_type; + if (kinds & (1 << DRGN_TYPE_CLASS)) + tags[num_tags++] = DRGN_DWARF_INDEX_class_type; + if (kinds & (1 << DRGN_TYPE_ENUM)) + tags[num_tags++] = DRGN_DWARF_INDEX_enumeration_type; + if (kinds & (1 << DRGN_TYPE_TYPEDEF)) + tags[num_tags++] = DRGN_DWARF_INDEX_typedef; + + struct drgn_namespace_dwarf_index *namespace; + err = find_enclosing_namespace(&dbinfo->dwarf.global, + &name, &name_len, &namespace); + if (err) + return err; struct drgn_dwarf_index_iterator it; - err = drgn_dwarf_index_iterator_init(&it, &dbinfo->dwarf.global, name, - name_len, &tag, 1); + err = drgn_dwarf_index_iterator_init(&it, namespace, name, name_len, + tags, num_tags); if (err) return err; - struct drgn_dwarf_index_die *index_die; - while ((index_die = drgn_dwarf_index_iterator_next(&it))) { - Dwarf_Die die; - err = drgn_dwarf_index_get_die(index_die, &die); - if (err) - return err; + Dwarf_Die die; + struct drgn_elf_file *file; + while (drgn_dwarf_index_iterator_next(&it, &die, &file)) { if (die_matches_filename(&die, filename)) { - err = drgn_type_from_dwarf(dbinfo, index_die->module, - &die, ret); + err = drgn_type_from_dwarf(dbinfo, file, &die, ret); if (err) return err; /* - * For DW_TAG_base_type, we need to check that the type - * we found was the right kind. + * For base_type, we need to check that the type we + * found was the right kind. */ - if (drgn_type_kind(ret->type) == kind) + if (kinds & (UINT64_C(1) << drgn_type_kind(ret->type))) return NULL; } } @@ -6831,60 +6188,38 @@ drgn_debug_info_find_object(const char *name, size_t name_len, struct drgn_error *err; struct drgn_debug_info *dbinfo = arg; - struct drgn_namespace_dwarf_index *ns = &dbinfo->dwarf.global; - if (name_len >= 2 && memcmp(name, "::", 2) == 0) { - /* Explicit global namespace. */ - name_len -= 2; - name += 2; - } - const char *colons; - while ((colons = memmem(name, name_len, "::", 2))) { - struct drgn_dwarf_index_iterator it; - uint64_t ns_tag = DW_TAG_namespace; - err = drgn_dwarf_index_iterator_init(&it, ns, name, - colons - name, &ns_tag, 1); - if (err) - return err; - struct drgn_dwarf_index_die *index_die = - drgn_dwarf_index_iterator_next(&it); - if (!index_die) - return &drgn_not_found; - ns = index_die->namespace; - name_len -= colons + 2 - name; - name = colons + 2; - } + struct drgn_namespace_dwarf_index *ns; + err = find_enclosing_namespace(&dbinfo->dwarf.global, + &name, &name_len, &ns); + if (err) + return err; - uint64_t tags[3]; + enum drgn_dwarf_index_tag tags[3]; size_t num_tags = 0; if (flags & DRGN_FIND_OBJECT_CONSTANT) - tags[num_tags++] = DW_TAG_enumerator; + tags[num_tags++] = DRGN_DWARF_INDEX_enumerator; if (flags & DRGN_FIND_OBJECT_FUNCTION) - tags[num_tags++] = DW_TAG_subprogram; + tags[num_tags++] = DRGN_DWARF_INDEX_subprogram; if (flags & DRGN_FIND_OBJECT_VARIABLE) - tags[num_tags++] = DW_TAG_variable; + tags[num_tags++] = DRGN_DWARF_INDEX_variable; struct drgn_dwarf_index_iterator it; err = drgn_dwarf_index_iterator_init(&it, ns, name, name_len, tags, num_tags); if (err) return err; - struct drgn_dwarf_index_die *index_die; - while ((index_die = drgn_dwarf_index_iterator_next(&it))) { - Dwarf_Die die; - err = drgn_dwarf_index_get_die(index_die, &die); - if (err) - return err; + Dwarf_Die die; + struct drgn_elf_file *file; + while (drgn_dwarf_index_iterator_next(&it, &die, &file)) { if (!die_matches_filename(&die, filename)) continue; if (dwarf_tag(&die) == DW_TAG_enumeration_type) { - return drgn_object_from_dwarf_enumerator(dbinfo, - index_die->module, + return drgn_object_from_dwarf_enumerator(dbinfo, file, &die, name, ret); } else { - return drgn_object_from_dwarf(dbinfo, index_die->module, - &die, NULL, NULL, NULL, - ret); + return drgn_object_from_dwarf(dbinfo, file, &die, NULL, + NULL, NULL, ret); } } return &drgn_not_found; @@ -6895,8 +6230,6 @@ drgn_debug_info_find_object(const char *name, size_t name_len, */ struct drgn_dwarf_cie { - /* Whether this CIE is from .eh_frame. */ - bool is_eh; /* Size of an address in this CIE in bytes. */ uint8_t address_size; /* DW_EH_PE_* encoding of addresses in this CIE. */ @@ -6912,13 +6245,13 @@ struct drgn_dwarf_cie { size_t initial_instructions_size; }; -DEFINE_VECTOR(drgn_dwarf_fde_vector, struct drgn_dwarf_fde) -DEFINE_VECTOR(drgn_dwarf_cie_vector, struct drgn_dwarf_cie) +DEFINE_VECTOR(drgn_dwarf_fde_vector, struct drgn_dwarf_fde); +DEFINE_VECTOR(drgn_dwarf_cie_vector, struct drgn_dwarf_cie); DEFINE_HASH_MAP(drgn_dwarf_cie_map, size_t, size_t, int_key_hash_pair, - scalar_key_eq) + scalar_key_eq); static struct drgn_error * -drgn_dwarf_cfi_next_encoded(struct drgn_debug_info_buffer *buffer, +drgn_dwarf_cfi_next_encoded(struct drgn_elf_file_section_buffer *buffer, uint8_t address_size, uint8_t encoding, uint64_t func_addr, uint64_t *ret) { @@ -6932,21 +6265,20 @@ drgn_dwarf_cfi_next_encoded(struct drgn_debug_info_buffer *buffer, encoding); } - size_t pos = (buffer->bb.pos - - (char *)buffer->module->scn_data[buffer->scn]->d_buf); + size_t pos = buffer->bb.pos - (char *)buffer->data->d_buf; uint64_t base; switch (encoding & 0x70) { case DW_EH_PE_absptr: base = 0; break; case DW_EH_PE_pcrel: - base = buffer->module->dwarf.pcrel_base + pos; + base = buffer->file->module->dwarf.pcrel_base + pos; break; case DW_EH_PE_textrel: - base = buffer->module->dwarf.textrel_base; + base = buffer->file->module->dwarf.textrel_base; break; case DW_EH_PE_datarel: - base = buffer->module->dwarf.datarel_base; + base = buffer->file->module->dwarf.datarel_base; break; case DW_EH_PE_funcrel: /* Relative to the FDE's initial location. */ @@ -7016,18 +6348,16 @@ drgn_dwarf_cfi_next_encoded(struct drgn_debug_info_buffer *buffer, return NULL; } -static struct drgn_error * -drgn_parse_dwarf_cie(struct drgn_debug_info_module *module, - enum drgn_debug_info_scn scn, size_t cie_pointer, - struct drgn_dwarf_cie *cie) +static struct drgn_error *drgn_parse_dwarf_cie(struct drgn_elf_file *file, + enum drgn_section_index scn, + size_t cie_pointer, + struct drgn_dwarf_cie *cie) { bool is_eh = scn == DRGN_SCN_EH_FRAME; struct drgn_error *err; - cie->is_eh = is_eh; - - struct drgn_debug_info_buffer buffer; - drgn_debug_info_buffer_init(&buffer, module, scn); + struct drgn_elf_file_section_buffer buffer; + drgn_elf_file_section_buffer_init_index(&buffer, file, scn); buffer.bb.pos += cie_pointer; uint32_t tmp; @@ -7086,7 +6416,7 @@ drgn_parse_dwarf_cie(struct drgn_debug_info_module *module, case 'L': case 'P': case 'R': - if (augmentation[0] != 'z') + if (augmentation[0] != 'z' || !is_eh) goto unknown_augmentation; break; case 'S': @@ -7126,8 +6456,7 @@ drgn_parse_dwarf_cie(struct drgn_debug_info_module *module, segment_selector_size); } } else { - cie->address_size = - drgn_platform_address_size(&module->platform); + cie->address_size = drgn_elf_file_address_size(file); } if ((err = binary_buffer_next_uleb128(&buffer.bb, &cie->code_alignment_factor)) || @@ -7145,7 +6474,7 @@ drgn_parse_dwarf_cie(struct drgn_debug_info_module *module, return err; } cie->return_address_register = - module->platform.arch->dwarf_regno_to_internal(return_address_register); + file->platform.arch->dwarf_regno_to_internal(return_address_register); if (cie->return_address_register == DRGN_REGISTER_NUMBER_UNKNOWN) { return binary_buffer_error(&buffer.bb, "unknown return address register"); @@ -7193,34 +6522,72 @@ drgn_parse_dwarf_cie(struct drgn_debug_info_module *module, return NULL; } -static struct drgn_error * -drgn_parse_dwarf_frames(struct drgn_debug_info_module *module, - enum drgn_debug_info_scn scn, - struct drgn_dwarf_cie_vector *cies, - struct drgn_dwarf_fde_vector *fdes) +static void drgn_debug_info_cache_sh_addr(struct drgn_elf_file *file, + enum drgn_section_index scn, + uint64_t *addr) { - bool is_eh = scn == DRGN_SCN_EH_FRAME; + if (file->scns[scn]) { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr(file->scns[scn], &shdr_mem); + if (shdr) + *addr = shdr->sh_addr; + } +} + +static int drgn_dwarf_fde_compar(const void *_a, const void *_b) +{ + const struct drgn_dwarf_fde *a = _a; + const struct drgn_dwarf_fde *b = _b; + if (a->initial_location < b->initial_location) + return -1; + else if (a->initial_location > b->initial_location) + return 1; + else + return 0; +} + +static struct drgn_error *drgn_parse_dwarf_cfi(struct drgn_dwarf_cfi *cfi, + struct drgn_elf_file *file, + enum drgn_section_index scn) +{ + const bool is_eh = scn == DRGN_SCN_EH_FRAME; struct drgn_error *err; - if (!module->scns[scn]) + if (!file->scns[scn]) return NULL; - err = drgn_debug_info_module_cache_section(module, scn); + + if (is_eh) { + drgn_debug_info_cache_sh_addr(file, DRGN_SCN_EH_FRAME, + &file->module->dwarf.pcrel_base); + drgn_debug_info_cache_sh_addr(file, DRGN_SCN_TEXT, + &file->module->dwarf.textrel_base); + drgn_debug_info_cache_sh_addr(file, DRGN_SCN_GOT, + &file->module->dwarf.datarel_base); + } + + err = drgn_elf_file_cache_section(file, scn); if (err) return err; - Elf_Data *data = module->scn_data[scn]; - struct drgn_debug_info_buffer buffer; - drgn_debug_info_buffer_init(&buffer, module, scn); - struct drgn_dwarf_cie_map cie_map = HASH_TABLE_INIT; + _cleanup_(drgn_dwarf_cie_vector_deinit) + struct drgn_dwarf_cie_vector cies = VECTOR_INIT; + _cleanup_(drgn_dwarf_fde_vector_deinit) + struct drgn_dwarf_fde_vector fdes = VECTOR_INIT; + _cleanup_(drgn_dwarf_cie_map_deinit) + struct drgn_dwarf_cie_map cie_map = HASH_TABLE_INIT; + + Elf_Data *data = file->scn_data[scn]; + struct drgn_elf_file_section_buffer buffer; + drgn_elf_file_section_buffer_init_index(&buffer, file, scn); while (binary_buffer_has_next(&buffer.bb)) { uint32_t tmp; if ((err = binary_buffer_next_u32(&buffer.bb, &tmp))) - goto out; + return err; bool is_64_bit = tmp == UINT32_C(0xffffffff); uint64_t length; if (is_64_bit) { if ((err = binary_buffer_next_u64(&buffer.bb, &length))) - goto out; + return err; } else { length = tmp; } @@ -7232,9 +6599,8 @@ drgn_parse_dwarf_frames(struct drgn_debug_info_module *module, if (length == 0) break; if (length > buffer.bb.end - buffer.bb.pos) { - err = binary_buffer_error(&buffer.bb, - "entry length is out of bounds"); - goto out; + return binary_buffer_error(&buffer.bb, + "entry length is out of bounds"); } buffer.bb.end = buffer.bb.pos + length; @@ -7250,12 +6616,12 @@ drgn_parse_dwarf_frames(struct drgn_debug_info_module *module, if (is_64_bit) { if ((err = binary_buffer_next_u64(&buffer.bb, &cie_pointer))) - goto out; + return err; cie_id = is_eh ? 0 : UINT64_C(0xffffffffffffffff); } else { if ((err = binary_buffer_next_u32_into_u64(&buffer.bb, &cie_pointer))) - goto out; + return err; cie_id = is_eh ? 0 : UINT64_C(0xffffffff); } @@ -7266,45 +6632,39 @@ drgn_parse_dwarf_frames(struct drgn_debug_info_module *module, - (is_64_bit ? 8 : 4) - (char *)data->d_buf); if (cie_pointer > pointer_offset) { - err = binary_buffer_error(&buffer.bb, - "CIE pointer is out of bounds"); - goto out; + return binary_buffer_error(&buffer.bb, + "CIE pointer is out of bounds"); } cie_pointer = pointer_offset - cie_pointer; } else if (cie_pointer > data->d_size) { - err = binary_buffer_error(&buffer.bb, - "CIE pointer is out of bounds"); - goto out; + return binary_buffer_error(&buffer.bb, + "CIE pointer is out of bounds"); } struct drgn_dwarf_fde *fde = - drgn_dwarf_fde_vector_append_entry(fdes); - if (!fde) { - err = &drgn_enomem; - goto out; - } + drgn_dwarf_fde_vector_append_entry(&fdes); + if (!fde) + return &drgn_enomem; struct drgn_dwarf_cie_map_entry entry = { .key = cie_pointer, - .value = cies->size, + .value = drgn_dwarf_cie_vector_size(&cies), }; struct drgn_dwarf_cie_map_iterator it; int r = drgn_dwarf_cie_map_insert(&cie_map, &entry, &it); struct drgn_dwarf_cie *cie; if (r > 0) { - cie = drgn_dwarf_cie_vector_append_entry(cies); - if (!cie) { - err = &drgn_enomem; - goto out; - } - err = drgn_parse_dwarf_cie(module, scn, + cie = drgn_dwarf_cie_vector_append_entry(&cies); + if (!cie) + return &drgn_enomem; + err = drgn_parse_dwarf_cie(file, scn, cie_pointer, cie); if (err) - goto out; + return err; } else if (r == 0) { - cie = &cies->data[it.entry->value]; + cie = drgn_dwarf_cie_vector_at(&cies, + it.entry->value); } else { - err = &drgn_enomem; - goto out; + return &drgn_enomem; } if ((err = drgn_dwarf_cfi_next_encoded(&buffer, cie->address_size, @@ -7316,17 +6676,16 @@ drgn_parse_dwarf_frames(struct drgn_debug_info_module *module, cie->address_encoding & 0xf, 0, &fde->address_range))) - goto out; + return err; if (cie->have_augmentation_length) { uint64_t augmentation_length; if ((err = binary_buffer_next_uleb128(&buffer.bb, &augmentation_length))) - goto out; + return err; if (augmentation_length > buffer.bb.end - buffer.bb.pos) { - err = binary_buffer_error(&buffer.bb, - "augmentation length is out of bounds"); - goto out; + return binary_buffer_error(&buffer.bb, + "augmentation length is out of bounds"); } buffer.bb.pos += augmentation_length; } @@ -7339,127 +6698,38 @@ drgn_parse_dwarf_frames(struct drgn_debug_info_module *module, buffer.bb.end = (const char *)data->d_buf + data->d_size; } - err = NULL; -out: - drgn_dwarf_cie_map_deinit(&cie_map); - return err; -} - -static void drgn_debug_info_cache_sh_addr(struct drgn_debug_info_module *module, - enum drgn_debug_info_scn scn, - uint64_t *addr) -{ - if (module->scns[scn]) { - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr(module->scns[scn], &shdr_mem); - if (shdr) - *addr = shdr->sh_addr; - } -} - -static int drgn_dwarf_fde_compar(const void *_a, const void *_b, void *arg) -{ - const struct drgn_dwarf_fde *a = _a; - const struct drgn_dwarf_fde *b = _b; - const struct drgn_dwarf_cie *cies = arg; - if (a->initial_location < b->initial_location) - return -1; - else if (a->initial_location > b->initial_location) - return 1; - else - return cies[a->cie].is_eh - cies[b->cie].is_eh; -} - -static struct drgn_error * -drgn_debug_info_parse_frames(struct drgn_debug_info_module *module) -{ - struct drgn_error *err; - - drgn_debug_info_cache_sh_addr(module, DRGN_SCN_EH_FRAME, - &module->dwarf.pcrel_base); - drgn_debug_info_cache_sh_addr(module, DRGN_SCN_TEXT, - &module->dwarf.textrel_base); - drgn_debug_info_cache_sh_addr(module, DRGN_SCN_GOT, - &module->dwarf.datarel_base); - - struct drgn_dwarf_cie_vector cies = VECTOR_INIT; - struct drgn_dwarf_fde_vector fdes = VECTOR_INIT; - - err = drgn_parse_dwarf_frames(module, DRGN_SCN_DEBUG_FRAME, &cies, - &fdes); - if (err) - goto err; - err = drgn_parse_dwarf_frames(module, DRGN_SCN_EH_FRAME, &cies, &fdes); - if (err) - goto err; - drgn_dwarf_cie_vector_shrink_to_fit(&cies); - - /* - * Sort FDEs and remove duplicates, preferring .debug_frame over - * .eh_frame. - */ - qsort_r(fdes.data, fdes.size, sizeof(fdes.data[0]), - drgn_dwarf_fde_compar, cies.data); - if (fdes.size > 0) { - size_t src = 1, dst = 1; - for (; src < fdes.size; src++) { - if (fdes.data[src].initial_location != - fdes.data[dst - 1].initial_location) { - if (src != dst) - fdes.data[dst] = fdes.data[src]; - dst++; - } - } - fdes.size = dst; - } drgn_dwarf_fde_vector_shrink_to_fit(&fdes); - - module->dwarf.cies = cies.data; - module->dwarf.fdes = fdes.data; - module->dwarf.num_fdes = fdes.size; + qsort(drgn_dwarf_fde_vector_begin(&fdes), + drgn_dwarf_fde_vector_size(&fdes), sizeof(struct drgn_dwarf_fde), + drgn_dwarf_fde_compar); + drgn_dwarf_cie_vector_steal(&cies, &cfi->cies, NULL); + drgn_dwarf_fde_vector_steal(&fdes, &cfi->fdes, &cfi->num_fdes); return NULL; - -err: - drgn_dwarf_fde_vector_deinit(&fdes); - drgn_dwarf_cie_vector_deinit(&cies); - return err; } -static struct drgn_error * -drgn_debug_info_find_fde(struct drgn_debug_info_module *module, - uint64_t unbiased_pc, struct drgn_dwarf_fde **ret) +static struct drgn_dwarf_fde *drgn_find_dwarf_fde(struct drgn_dwarf_cfi *cfi, + uint64_t unbiased_pc) { - struct drgn_error *err; - - if (!module->parsed_frames) { - err = drgn_debug_info_parse_frames(module); - if (err) - return err; - module->parsed_frames = true; - } - /* Binary search for the containing FDE. */ - size_t lo = 0, hi = module->dwarf.num_fdes; + size_t lo = 0, hi = cfi->num_fdes; while (lo < hi) { size_t mid = lo + (hi - lo) / 2; - struct drgn_dwarf_fde *fde = &module->dwarf.fdes[mid]; - if (unbiased_pc < fde->initial_location) { + struct drgn_dwarf_fde *fde = &cfi->fdes[mid]; + if (unbiased_pc < fde->initial_location) hi = mid; - } else if (unbiased_pc - fde->initial_location >= - fde->address_range) { + else if (unbiased_pc - fde->initial_location >= + fde->address_range) lo = mid + 1; - } else { - *ret = fde; - return NULL; - } + else + return fde; } - *ret = NULL; return NULL; } static struct drgn_error * -drgn_dwarf_cfi_next_offset(struct drgn_debug_info_buffer *buffer, int64_t *ret) +drgn_dwarf_cfi_next_offset(struct drgn_elf_file_section_buffer *buffer, + int64_t *ret) { struct drgn_error *err; uint64_t offset; @@ -7472,7 +6742,7 @@ drgn_dwarf_cfi_next_offset(struct drgn_debug_info_buffer *buffer, int64_t *ret) } static struct drgn_error * -drgn_dwarf_cfi_next_offset_sf(struct drgn_debug_info_buffer *buffer, +drgn_dwarf_cfi_next_offset_sf(struct drgn_elf_file_section_buffer *buffer, struct drgn_dwarf_cie *cie, int64_t *ret) { struct drgn_error *err; @@ -7485,7 +6755,7 @@ drgn_dwarf_cfi_next_offset_sf(struct drgn_debug_info_buffer *buffer, } static struct drgn_error * -drgn_dwarf_cfi_next_offset_f(struct drgn_debug_info_buffer *buffer, +drgn_dwarf_cfi_next_offset_f(struct drgn_elf_file_section_buffer *buffer, struct drgn_dwarf_cie *cie, int64_t *ret) { struct drgn_error *err; @@ -7498,7 +6768,7 @@ drgn_dwarf_cfi_next_offset_f(struct drgn_debug_info_buffer *buffer, } static struct drgn_error * -drgn_dwarf_cfi_next_block(struct drgn_debug_info_buffer *buffer, +drgn_dwarf_cfi_next_block(struct drgn_elf_file_section_buffer *buffer, const char **buf_ret, size_t *size_ret) { struct drgn_error *err; @@ -7515,26 +6785,23 @@ drgn_dwarf_cfi_next_block(struct drgn_debug_info_buffer *buffer, return NULL; } -DEFINE_VECTOR(drgn_cfi_row_vector, struct drgn_cfi_row *) +DEFINE_VECTOR(drgn_cfi_row_vector, struct drgn_cfi_row *); static struct drgn_error * -drgn_eval_dwarf_cfi(struct drgn_debug_info_module *module, - struct drgn_dwarf_fde *fde, +drgn_eval_dwarf_cfi(struct drgn_elf_file *file, enum drgn_section_index scn, + struct drgn_dwarf_cie *cie, struct drgn_dwarf_fde *fde, const struct drgn_cfi_row *initial_row, uint64_t target, const char *instructions, size_t instructions_size, struct drgn_cfi_row **row) { struct drgn_error *err; drgn_register_number (*dwarf_regno_to_internal)(uint64_t) = - module->platform.arch->dwarf_regno_to_internal; - struct drgn_dwarf_cie *cie = &module->dwarf.cies[fde->cie]; + file->platform.arch->dwarf_regno_to_internal; uint64_t pc = fde->initial_location; struct drgn_cfi_row_vector state_stack = VECTOR_INIT; - struct drgn_debug_info_buffer buffer; - drgn_debug_info_buffer_init(&buffer, module, - cie->is_eh ? - DRGN_SCN_EH_FRAME : DRGN_SCN_DEBUG_FRAME); + struct drgn_elf_file_section_buffer buffer; + drgn_elf_file_section_buffer_init_index(&buffer, file, scn); buffer.bb.pos = instructions; buffer.bb.end = instructions + instructions_size; while (binary_buffer_has_next(&buffer.bb)) { @@ -7808,20 +7075,20 @@ drgn_eval_dwarf_cfi(struct drgn_debug_info_module *module, break; } case DW_CFA_restore_state: - if (state_stack.size == 0) { + if (drgn_cfi_row_vector_empty(&state_stack)) { err = binary_buffer_error(&buffer.bb, "DW_CFA_restore_state with empty state stack"); goto out; } drgn_cfi_row_destroy(*row); - *row = state_stack.data[--state_stack.size]; + *row = *drgn_cfi_row_vector_pop(&state_stack); break; case DW_CFA_nop: break; // Note that this is the same opcode as DW_CFA_GNU_window_save, // which is used on Sparc. case DW_CFA_AARCH64_negate_ra_state: - if (drgn_platform_arch(&module->platform) + if (drgn_platform_arch(&file->platform) == DRGN_ARCH_AARCH64) { regno = DRGN_AARCH64_RA_SIGN_STATE_REGNO; drgn_cfi_row_get_register(*row, regno, &rule); @@ -7833,7 +7100,7 @@ drgn_eval_dwarf_cfi(struct drgn_debug_info_module *module, rule.constant ^= 1; goto set_reg; } - /* fallthrough */ + fallthrough; default: err = binary_buffer_error(&buffer.bb, "unknown DWARF CFI opcode %#" PRIx8, @@ -7844,22 +7111,24 @@ drgn_eval_dwarf_cfi(struct drgn_debug_info_module *module, found: err = NULL; out: - for (size_t i = 0; i < state_stack.size; i++) - drgn_cfi_row_destroy(state_stack.data[i]); + vector_for_each(drgn_cfi_row_vector, it, &state_stack) + drgn_cfi_row_destroy(*it); drgn_cfi_row_vector_deinit(&state_stack); return err; } static struct drgn_error * -drgn_debug_info_find_cfi_in_fde(struct drgn_debug_info_module *module, - struct drgn_dwarf_fde *fde, - uint64_t unbiased_pc, struct drgn_cfi_row **ret) +drgn_find_cfi_row_in_dwarf_fde(struct drgn_dwarf_cfi *cfi, + struct drgn_elf_file *file, + enum drgn_section_index scn, + struct drgn_dwarf_fde *fde, uint64_t unbiased_pc, + struct drgn_cfi_row **ret) { struct drgn_error *err; - struct drgn_dwarf_cie *cie = &module->dwarf.cies[fde->cie]; + struct drgn_dwarf_cie *cie = &cfi->cies[fde->cie]; struct drgn_cfi_row *initial_row = - (struct drgn_cfi_row *)module->platform.arch->default_dwarf_cfi_row; - err = drgn_eval_dwarf_cfi(module, fde, NULL, unbiased_pc, + (struct drgn_cfi_row *)file->platform.arch->default_dwarf_cfi_row; + err = drgn_eval_dwarf_cfi(file, scn, cie, fde, NULL, unbiased_pc, cie->initial_instructions, cie->initial_instructions_size, &initial_row); if (err) @@ -7868,7 +7137,7 @@ drgn_debug_info_find_cfi_in_fde(struct drgn_debug_info_module *module, err = &drgn_enomem; goto out; } - err = drgn_eval_dwarf_cfi(module, fde, initial_row, unbiased_pc, + err = drgn_eval_dwarf_cfi(file, scn, cie, fde, initial_row, unbiased_pc, fde->instructions, fde->instructions_size, ret); out: @@ -7876,58 +7145,84 @@ drgn_debug_info_find_cfi_in_fde(struct drgn_debug_info_module *module, return err; } -struct drgn_error * -drgn_debug_info_find_dwarf_cfi(struct drgn_debug_info_module *module, - uint64_t unbiased_pc, - struct drgn_cfi_row **row_ret, - bool *interrupted_ret, - drgn_register_number *ret_addr_regno_ret) +static struct drgn_error * +drgn_find_dwarf_cfi(struct drgn_dwarf_cfi *cfi, bool *parsed, + struct drgn_elf_file *file, enum drgn_section_index scn, + uint64_t unbiased_pc, struct drgn_cfi_row **row_ret, + bool *interrupted_ret, + drgn_register_number *ret_addr_regno_ret) { struct drgn_error *err; - struct drgn_dwarf_fde *fde; - err = drgn_debug_info_find_fde(module, unbiased_pc, &fde); - if (err) - return err; + + if (!*parsed) { + err = drgn_parse_dwarf_cfi(cfi, file, scn); + if (err) + return err; + *parsed = true; + } + + struct drgn_dwarf_fde *fde = drgn_find_dwarf_fde(cfi, unbiased_pc); if (!fde) return &drgn_not_found; - err = drgn_debug_info_find_cfi_in_fde(module, fde, unbiased_pc, - row_ret); + err = drgn_find_cfi_row_in_dwarf_fde(cfi, file, scn, fde, unbiased_pc, + row_ret); if (err) return err; - *interrupted_ret = module->dwarf.cies[fde->cie].signal_frame; - *ret_addr_regno_ret = - module->dwarf.cies[fde->cie].return_address_register; + *interrupted_ret = cfi->cies[fde->cie].signal_frame; + *ret_addr_regno_ret = cfi->cies[fde->cie].return_address_register; return NULL; } +struct drgn_error * +drgn_module_find_dwarf_cfi(struct drgn_module *module, uint64_t pc, + struct drgn_cfi_row **row_ret, bool *interrupted_ret, + drgn_register_number *ret_addr_regno_ret) +{ + return drgn_find_dwarf_cfi(&module->dwarf.debug_frame, + &module->parsed_debug_frame, + module->debug_file, DRGN_SCN_DEBUG_FRAME, + pc - module->debug_file_bias, row_ret, + interrupted_ret, ret_addr_regno_ret); +} + +struct drgn_error * +drgn_module_find_eh_cfi(struct drgn_module *module, uint64_t pc, + struct drgn_cfi_row **row_ret, bool *interrupted_ret, + drgn_register_number *ret_addr_regno_ret) +{ + return drgn_find_dwarf_cfi(&module->dwarf.eh_frame, + &module->parsed_eh_frame, + module->loaded_file, DRGN_SCN_EH_FRAME, + pc - module->loaded_file_bias, row_ret, + interrupted_ret, ret_addr_regno_ret); +} + struct drgn_error * drgn_eval_cfi_dwarf_expression(struct drgn_program *prog, + struct drgn_elf_file *file, const struct drgn_cfi_rule *rule, const struct drgn_register_state *regs, void *buf, size_t size) { struct drgn_error *err; - struct uint64_vector stack = VECTOR_INIT; + _cleanup_(uint64_vector_deinit) struct uint64_vector stack = + VECTOR_INIT; if (rule->push_cfa) { struct optional_uint64 cfa = drgn_register_state_get_cfa(regs); - if (!cfa.has_value) { - err = &drgn_not_found; - goto out; - } - if (!uint64_vector_append(&stack, &cfa.value)) { - err = &drgn_enomem; - goto out; - } + if (!cfa.has_value) + return &drgn_not_found; + if (!uint64_vector_append(&stack, &cfa.value)) + return &drgn_enomem; } int remaining_ops = MAX_DWARF_EXPR_OPS; struct drgn_dwarf_expression_context ctx; - drgn_dwarf_expression_context_init(&ctx, prog, regs->module, NULL, NULL, - regs, rule->expr, rule->expr_size); + drgn_dwarf_expression_context_init(&ctx, prog, file, NULL, NULL, regs, + rule->expr, rule->expr_size); err = drgn_eval_dwarf_expression(&ctx, &stack, &remaining_ops); if (err) - goto out; + return err; if (binary_buffer_has_next(&ctx.bb)) { uint8_t opcode; err = binary_buffer_next_u8(&ctx.bb, &opcode); @@ -7936,23 +7231,18 @@ drgn_eval_cfi_dwarf_expression(struct drgn_program *prog, "invalid opcode %#" PRIx8 " for CFI expression", opcode); } - goto out; + return err; } - if (stack.size == 0) { - err = &drgn_not_found; + if (uint64_vector_empty(&stack)) { + return &drgn_not_found; } else if (rule->kind == DRGN_CFI_RULE_AT_DWARF_EXPRESSION) { - err = drgn_program_read_memory(prog, buf, - stack.data[stack.size - 1], size, - false); + return drgn_program_read_memory(prog, buf, + *uint64_vector_last(&stack), + size, false); } else { - copy_lsbytes(buf, size, - drgn_platform_is_little_endian(&prog->platform), - &stack.data[stack.size - 1], sizeof(uint64_t), + copy_lsbytes(buf, size, drgn_elf_file_is_little_endian(file), + uint64_vector_last(&stack), sizeof(uint64_t), HOST_LITTLE_ENDIAN); - err = NULL; + return NULL; } - -out: - uint64_vector_deinit(&stack); - return err; } diff --git a/libdrgn/dwarf_info.h b/libdrgn/dwarf_info.h index efe283ea2..b1122ba43 100644 --- a/libdrgn/dwarf_info.h +++ b/libdrgn/dwarf_info.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -26,83 +26,138 @@ #include "vector.h" struct drgn_debug_info; -struct drgn_debug_info_module; +struct drgn_elf_file; +struct drgn_module; struct drgn_register_state; /** DWARF Frame Description Entry. */ struct drgn_dwarf_fde { uint64_t initial_location; uint64_t address_range; - /* CIE for this FDE as an index into drgn_debug_info_module::cies. */ + /* CIE for this FDE as an index into drgn_dwarf_cfi::cies. */ size_t cie; const char *instructions; size_t instructions_size; }; -/** DWARF debugging information for a @ref drgn_debug_info_module. */ -struct drgn_dwarf_module_info { - /** Base for `DW_EH_PE_pcrel`. */ - uint64_t pcrel_base; - /** Base for `DW_EH_PE_textrel`. */ - uint64_t textrel_base; - /** Base for `DW_EH_PE_datarel`. */ - uint64_t datarel_base; +/** DWARF Call Frame Information. */ +struct drgn_dwarf_cfi { /** Array of DWARF Common Information Entries. */ struct drgn_dwarf_cie *cies; /** * Array of DWARF Frame Description Entries sorted by initial_location. */ struct drgn_dwarf_fde *fdes; - /** Number of elements in @ref drgn_debug_info_module::fdes. */ + /** Number of elements in @ref drgn_dwarf_cfi::fdes. */ size_t num_fdes; }; -void drgn_dwarf_module_info_deinit(struct drgn_debug_info_module *module); +/** DWARF debugging information for a @ref drgn_module. */ +struct drgn_module_dwarf_info { + /** Call Frame Information from .debug_frame. */ + struct drgn_dwarf_cfi debug_frame; + /** Call Frame Information from .eh_frame. */ + struct drgn_dwarf_cfi eh_frame; + /** Base for `DW_EH_PE_pcrel`. */ + uint64_t pcrel_base; + /** Base for `DW_EH_PE_textrel`. */ + uint64_t textrel_base; + /** Base for `DW_EH_PE_datarel`. */ + uint64_t datarel_base; +}; -DEFINE_VECTOR_TYPE(drgn_dwarf_index_pending_die_vector, - struct drgn_dwarf_index_pending_die) +void drgn_module_dwarf_info_deinit(struct drgn_module *module); + +DEFINE_VECTOR_TYPE(drgn_dwarf_index_die_vector, uintptr_t, + vector_inline_minimal, uint32_t); +DEFINE_HASH_MAP_TYPE(drgn_dwarf_index_die_map, struct nstring, + struct drgn_dwarf_index_die_vector); + +DEFINE_HASH_TABLE_TYPE(drgn_namespace_table, + struct drgn_namespace_dwarf_index *); + +/* DWARF tags that act as namespaces. */ +#define DRGN_DWARF_INDEX_NAMESPACE_TAGS \ + X(structure_type) \ + X(class_type) \ + X(union_type) \ + X(namespace) + +/* DWARF tags that we index. */ +#define DRGN_DWARF_INDEX_TAGS \ + /* \ + * These must be first for a few places where we only care about \ + * namespace-like tags (e.g., drgn_namespace_dwarf_index::dies_indexed).\ + */ \ + DRGN_DWARF_INDEX_NAMESPACE_TAGS \ + X(enumeration_type) \ + X(typedef) \ + X(enumerator) \ + X(subprogram) \ + X(variable) \ + X(base_type) + +enum drgn_dwarf_index_tag { +#define X(name) DRGN_DWARF_INDEX_##name, + DRGN_DWARF_INDEX_TAGS +#undef X +}; + +#define X(_) + 1 +enum { DRGN_DWARF_INDEX_NUM_NAMESPACE_TAGS = DRGN_DWARF_INDEX_NAMESPACE_TAGS }; +enum { DRGN_DWARF_INDEX_NUM_TAGS = DRGN_DWARF_INDEX_TAGS }; +#undef X +_Static_assert(DRGN_DWARF_INDEX_base_type == DRGN_DWARF_INDEX_NUM_TAGS - 1, + "base_type must be last"); +enum { DRGN_DWARF_INDEX_MAP_SIZE = DRGN_DWARF_INDEX_NUM_TAGS - 1 }; /** - * Index of DWARF information for a namespace by entity name. - * - * This effectively maps a name to a list of DIEs with that name in a namespace. - * DIEs with the same name and tag and declared in the same file are - * deduplicated. + * DWARF information for a namespace or nested definitions in a class, struct, + * or union. */ struct drgn_namespace_dwarf_index { + /** Debugging information cache that owns this index. */ + struct drgn_debug_info *dbinfo; + /** (Null-terminated) name of this namespace. */ + const char *name; + /** Length of @ref name. */ + size_t name_len; + /** Parent namespace, or @c NULL if it is the global namespace. */ + struct drgn_namespace_dwarf_index *parent; + /** Children namespaces indexed by name. */ + struct drgn_namespace_table children; /** - * Index shards. + * Mapping for each @ref drgn_dwarf_index_tag from name to a list of + * matching DIE addresses. + * + * This has a few quirks: * - * Indexing is parallelized, so this is sharded to reduce lock - * contention. + * - `base_type` DIEs are in @ref drgn_dwarf_info::base_types, not here. + * - `enumerator` entries store the addresses of the parent + * `enumeration_type` DIEs instead. + * - `namespace` entries also include the addresses of `class_type`, + * `structure_type`, and `union_type` DIEs that have children and + * `DW_AT_declaration`. This is because class, struct, and union + * declaration DIEs can contain nested definitions, so we want to + * index the children of those declarations, but we don't want to + * encounter the declarations when looking for the actual type. + * - Otherwise, this does not include DIEs with `DW_AT_declaration`. */ - struct drgn_dwarf_index_shard *shards; - /** Debugging information cache that owns this index. */ - struct drgn_debug_info *dbinfo; - /** DIEs we have not indexed yet. */ - struct drgn_dwarf_index_pending_die_vector pending_dies; - /** Saved error from a previous index. */ - struct drgn_error *saved_err; -}; - -/** DIE with a `DW_AT_specification` attribute. */ -struct drgn_dwarf_specification { + struct drgn_dwarf_index_die_map map[DRGN_DWARF_INDEX_MAP_SIZE]; + /** + * Number of CUs that were indexed the last time that this namespace was + * indexed. + */ + size_t cus_indexed; /** - * Address of non-defining declaration DIE referenced by - * `DW_AT_specification`. + * Number of DIEs for each namespace-like tag in the parent's index that + * were indexed the last time that this namespace was indexed. */ - uintptr_t declaration; - /** Module containing DIE. */ - struct drgn_debug_info_module *module; - /** Address of DIE. */ - uintptr_t addr; + uint32_t dies_indexed[DRGN_DWARF_INDEX_NUM_NAMESPACE_TAGS]; + /** Saved error from a previous index. */ + struct drgn_error *saved_err; }; -DEFINE_HASH_TABLE_TYPE(drgn_dwarf_specification_map, - struct drgn_dwarf_specification) - -DEFINE_VECTOR_TYPE(drgn_dwarf_index_cu_vector, struct drgn_dwarf_index_cu) - /** Cached type in a @ref drgn_debug_info. */ struct drgn_dwarf_type { struct drgn_type *type; @@ -116,20 +171,28 @@ struct drgn_dwarf_type { bool is_incomplete_array; }; -DEFINE_HASH_MAP_TYPE(drgn_dwarf_type_map, const void *, struct drgn_dwarf_type) +DEFINE_HASH_MAP_TYPE(drgn_dwarf_base_type_map, struct nstring, uintptr_t); +DEFINE_HASH_MAP_TYPE(drgn_dwarf_specification_map, uintptr_t, uintptr_t); +DEFINE_VECTOR_TYPE(drgn_dwarf_index_cu_vector, struct drgn_dwarf_index_cu); +DEFINE_HASH_MAP_TYPE(drgn_dwarf_type_map, const void *, struct drgn_dwarf_type); /** DWARF debugging information for a program/@ref drgn_debug_info. */ struct drgn_dwarf_info { /** Global namespace index. */ struct drgn_namespace_dwarf_index global; /** - * Map from address of DIE referenced by DW_AT_specification to DIE that - * references it. This is used to resolve DIEs with DW_AT_declaration to - * their definition. + * Mapping from name to `DW_TAG_base_type` DIE address with that name. * - * This is populated while indexing new DWARF information. Unlike the - * name index, it is not sharded because there typically aren't enough - * of these in a program to cause contention. + * Unlike user-defined types and variables, there can only be one base + * type with a given name in the entire program, so we don't store them + * in a @ref drgn_dwarf_index_die_map. + */ + struct drgn_dwarf_base_type_map base_types; + /** + * Map from the address of a (usually non-defining) DIE to the address + * of a DIE with a DW_AT_specification attribute that references it. + * This is used to resolve DIEs with DW_AT_declaration to their + * definition. */ struct drgn_dwarf_specification_map specifications; /** Indexed compilation units. */ @@ -157,17 +220,13 @@ struct drgn_dwarf_info { void drgn_dwarf_info_init(struct drgn_debug_info *dbinfo); void drgn_dwarf_info_deinit(struct drgn_debug_info *dbinfo); -DEFINE_VECTOR_TYPE(drgn_dwarf_index_pending_cu_vector, - struct drgn_dwarf_index_pending_cu) - /** * State tracked while indexing new DWARF information in a @ref drgn_dwarf_info. */ struct drgn_dwarf_index_state { struct drgn_debug_info *dbinfo; /** Per-thread arrays of CUs to be indexed. */ - struct drgn_dwarf_index_pending_cu_vector *cus; - size_t max_threads; + struct drgn_dwarf_index_cu_vector *cus; }; /** @@ -181,10 +240,10 @@ bool drgn_dwarf_index_state_init(struct drgn_dwarf_index_state *state, /** Deinitialize state for indexing new DWARF information. */ void drgn_dwarf_index_state_deinit(struct drgn_dwarf_index_state *state); -/** Read a @ref drgn_debug_info_module to index its DWARF information. */ +/** Read a @ref drgn_module to index its DWARF information. */ struct drgn_error * drgn_dwarf_index_read_module(struct drgn_dwarf_index_state *state, - struct drgn_debug_info_module *module); + struct drgn_module *module); /** * Index new DWARF information. @@ -196,8 +255,8 @@ struct drgn_error * drgn_dwarf_info_update_index(struct drgn_dwarf_index_state *state); /** - * Find the DWARF DIEs in a @ref drgn_debug_info_module for the scope containing - * a given program counter. + * Find the DWARF DIEs in a @ref drgn_module for the scope containing a given + * program counter. * * @param[in] module Module containing @p pc. * @param[in] pc Program counter. @@ -209,11 +268,11 @@ drgn_dwarf_info_update_index(struct drgn_dwarf_index_state *state); * grandparent, etc. Must be freed with @c free(). * @param[out] length_ret Returned length of @p dies_ret. */ -struct drgn_error * -drgn_debug_info_module_find_dwarf_scopes(struct drgn_debug_info_module *module, - uint64_t pc, uint64_t *bias_ret, - Dwarf_Die **dies_ret, - size_t *length_ret) +struct drgn_error *drgn_module_find_dwarf_scopes(struct drgn_module *module, + uint64_t pc, + uint64_t *bias_ret, + Dwarf_Die **dies_ret, + size_t *length_ret) __attribute__((__nonnull__(1, 3, 4, 5))); /** @@ -232,6 +291,19 @@ struct drgn_error *drgn_find_die_ancestors(Dwarf_Die *die, Dwarf_Die **dies_ret, size_t *length_ret) __attribute__((__nonnull__(2, 3))); +/** + * Get an array of names of `DW_TAG_variable` and `DW_TAG_formal_parameter` DIEs + * in local scopes. + * + * @param[out] names_ret Returned array of names. On success, must be freed with + * @c free(). The individual strings should not be freed. + * @param[out] count_ret Returned number of names in @p names_ret. + */ +struct drgn_error *drgn_dwarf_scopes_names(Dwarf_Die *scopes, + size_t num_scopes, + const char ***names_ret, + size_t *count_ret); + /** * Find an object DIE in an array of DWARF scopes. * @@ -264,21 +336,24 @@ struct drgn_error *drgn_find_in_dwarf_scopes(Dwarf_Die *scopes, */ struct drgn_error * drgn_object_from_dwarf(struct drgn_debug_info *dbinfo, - struct drgn_debug_info_module *module, - Dwarf_Die *die, Dwarf_Die *type_die, - Dwarf_Die *function_die, + struct drgn_elf_file *file, Dwarf_Die *die, + Dwarf_Die *type_die, Dwarf_Die *function_die, const struct drgn_register_state *regs, struct drgn_object *ret); struct drgn_error * -drgn_debug_info_find_dwarf_cfi(struct drgn_debug_info_module *module, - uint64_t unbiased_pc, - struct drgn_cfi_row **row_ret, - bool *interrupted_ret, - drgn_register_number *ret_addr_regno_ret); +drgn_module_find_dwarf_cfi(struct drgn_module *module, uint64_t pc, + struct drgn_cfi_row **row_ret, bool *interrupted_ret, + drgn_register_number *ret_addr_regno_ret); + +struct drgn_error * +drgn_module_find_eh_cfi(struct drgn_module *module, uint64_t pc, + struct drgn_cfi_row **row_ret, bool *interrupted_ret, + drgn_register_number *ret_addr_regno_ret); struct drgn_error * drgn_eval_cfi_dwarf_expression(struct drgn_program *prog, + struct drgn_elf_file *file, const struct drgn_cfi_rule *rule, const struct drgn_register_state *regs, void *buf, size_t size); diff --git a/libdrgn/elf_file.c b/libdrgn/elf_file.c new file mode 100644 index 000000000..4148cb080 --- /dev/null +++ b/libdrgn/elf_file.c @@ -0,0 +1,278 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include +#include +#include +#include +#include + +#include "array.h" +#include "drgn.h" +#include "elf_file.h" +#include "error.h" +#include "minmax.h" +#include "util.h" + +struct drgn_error *read_elf_section(Elf_Scn *scn, Elf_Data **ret) +{ + GElf_Shdr shdr_mem, *shdr; + shdr = gelf_getshdr(scn, &shdr_mem); + if (!shdr) + return drgn_error_libelf(); + if ((shdr->sh_flags & SHF_COMPRESSED) && elf_compress(scn, 0, 0) < 0) + return drgn_error_libelf(); + Elf_Data *data = elf_rawdata(scn, NULL); + if (!data) + return drgn_error_libelf(); + *ret = data; + return NULL; +} + +#include "drgn_section_name_to_index.inc" + +enum drgn_dwarf_file_type { + DRGN_DWARF_FILE_NONE, + DRGN_DWARF_FILE_GNU_LTO, + DRGN_DWARF_FILE_DWO, + DRGN_DWARF_FILE_PLAIN, +}; + +struct drgn_error *drgn_elf_file_create(struct drgn_module *module, + const char *path, Elf *elf, + struct drgn_elf_file **ret) +{ + struct drgn_error *err; + GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr(elf, &ehdr_mem); + if (!ehdr) + return drgn_error_libelf(); + size_t shstrndx; + if (elf_getshdrstrndx(elf, &shstrndx)) + return drgn_error_libelf(); + + struct drgn_elf_file *file = calloc(1, sizeof(*file)); + if (!file) + return &drgn_enomem; + file->module = module; + file->path = path; + file->elf = elf; + drgn_platform_from_elf(ehdr, &file->platform); + + // We mimic libdw's logic for choosing debug sections: we either use all + // .debug_* or .zdebug_* sections (DRGN_DWARF_FILE_PLAIN), all + // .debug_*.dwo or .zdebug_*.dwo sections (DRGN_DWARF_FILE_DWO), or all + // .gnu.debuglto_.debug_* sections (DRGN_DWARF_FILE_GNU_LTO), in that + // order of preference. + enum drgn_dwarf_file_type dwarf_file_type = DRGN_DWARF_FILE_NONE; + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn(elf, scn))) { + GElf_Shdr shdr_mem, *shdr = gelf_getshdr(scn, &shdr_mem); + if (!shdr) { + err = drgn_error_libelf(); + goto err; + } + const char *scnname = elf_strptr(elf, shstrndx, shdr->sh_name); + if (!scnname) { + err = drgn_error_libelf(); + goto err; + } + + enum drgn_dwarf_file_type dwarf_section_type; + if (strcmp(scnname, ".debug_cu_index") == 0 || + strcmp(scnname, ".debug_tu_index") == 0) { + dwarf_section_type = DRGN_DWARF_FILE_DWO; + } else if (strstartswith(scnname, ".debug_") || + strstartswith(scnname, ".zdebug_")) { + if (strcmp(scnname + strlen(scnname) - 4, ".dwo") == 0) + dwarf_section_type = DRGN_DWARF_FILE_DWO; + else + dwarf_section_type = DRGN_DWARF_FILE_PLAIN; + } else if (strstartswith(scnname, ".gnu.debuglto_.debug")) { + dwarf_section_type = DRGN_DWARF_FILE_GNU_LTO; + } else { + dwarf_section_type = DRGN_DWARF_FILE_NONE; + } + dwarf_file_type = max(dwarf_file_type, dwarf_section_type); + } + + scn = NULL; + while ((scn = elf_nextscn(elf, scn))) { + GElf_Shdr shdr_mem, *shdr = gelf_getshdr(scn, &shdr_mem); + if (!shdr) { + err = drgn_error_libelf(); + goto err; + } + + if (shdr->sh_type != SHT_PROGBITS) + continue; + + const char *scnname = elf_strptr(elf, shstrndx, shdr->sh_name); + if (!scnname) { + err = drgn_error_libelf(); + goto err; + } + + enum drgn_section_index index; + if (strstartswith(scnname, ".debug_") || + strstartswith(scnname, ".zdebug_")) { + const char *subname; + if (strstartswith(scnname, ".zdebug_")) + subname = scnname + sizeof(".zdebug_") - 1; + else + subname = scnname + sizeof(".debug_") - 1; + size_t len = strlen(subname); + if (len >= 4 + && strcmp(subname + len - 4, ".dwo") == 0) { + if (dwarf_file_type != DRGN_DWARF_FILE_DWO) + continue; + len -= 4; + } else if (dwarf_file_type != DRGN_DWARF_FILE_PLAIN) { + continue; + } + index = drgn_debug_section_name_to_index(subname, len); + } else if (strstartswith(scnname, ".gnu.debuglto_.debug_")) { + if (dwarf_file_type != DRGN_DWARF_FILE_GNU_LTO) + continue; + const char *subname = + scnname + sizeof(".gnu.debuglto_.debug_") - 1; + index = drgn_debug_section_name_to_index(subname, + strlen(subname)); + } else { + index = drgn_non_debug_section_name_to_index(scnname); + } + if (index < DRGN_SECTION_INDEX_NUM && !file->scns[index]) + file->scns[index] = scn; + } + *ret = file; + return NULL; + +err: + free(file); + return err; +} + +void drgn_elf_file_destroy(struct drgn_elf_file *file) +{ + free(file); +} + +static void truncate_null_terminated_section(Elf_Data *data) +{ + if (data) { + const char *buf = data->d_buf; + const char *nul = memrchr(buf, '\0', data->d_size); + if (nul) + data->d_size = nul - buf + 1; + else + data->d_size = 0; + } +} + +struct drgn_error *drgn_elf_file_precache_sections(struct drgn_elf_file *file) +{ + struct drgn_error *err; + + for (size_t i = 0; i < DRGN_SECTION_INDEX_NUM_PRECACHE; i++) { + if (file->scns[i]) { + err = read_elf_section(file->scns[i], + &file->scn_data[i]); + if (err) + return err; + } + } + + /* + * Truncate any extraneous bytes so that we can assume that a pointer + * within .debug_{,line_}str is always null-terminated. + */ + truncate_null_terminated_section(file->scn_data[DRGN_SCN_DEBUG_STR]); + truncate_null_terminated_section(file->scn_data[DRGN_SCN_DEBUG_LINE_STR]); + truncate_null_terminated_section(file->alt_debug_str_data); + return NULL; +} + +struct drgn_error * +drgn_elf_file_cache_section(struct drgn_elf_file *file, enum drgn_section_index scn) +{ + if (file->scn_data[scn]) + return NULL; + return read_elf_section(file->scns[scn], &file->scn_data[scn]); +} + +struct drgn_error * +drgn_elf_file_section_error(struct drgn_elf_file *file, Elf_Scn *scn, + Elf_Data *data, const char *ptr, + const char *message) +{ + // If we don't know what section the pointer came from, try to find it + // in the cached sections. + if (!scn) { + uintptr_t p = (uintptr_t)ptr; + for (size_t i = 0; i < array_size(file->scn_data); i++) { + if (!file->scn_data[i]) + continue; + uintptr_t start = (uintptr_t)file->scn_data[i]->d_buf; + uintptr_t end = start + file->scn_data[i]->d_size; + if (start <= p) { + // If the pointer matches the end of a section, + // remember the section but try to find a better + // match. + if (p <= end) { + scn = file->scns[i]; + data = file->scn_data[i]; + } + // If the pointer lies inside of the section, + // we're done. + if (p < end) + break; + } + } + } + const char *scnname = NULL; + size_t shstrndx; + GElf_Shdr shdr_mem, *shdr; + if (!elf_getshdrstrndx(file->elf, &shstrndx) && + (shdr = gelf_getshdr(scn, &shdr_mem))) + scnname = elf_strptr(file->elf, shstrndx, shdr->sh_name); + + if (scnname && data) { + return drgn_error_format(DRGN_ERROR_OTHER, "%s: %s+%#tx: %s", + file->path, scnname, + ptr - (const char *)data->d_buf, + message); + } else if (scnname) { + return drgn_error_format(DRGN_ERROR_OTHER, "%s: %s: %s", + file->path, scnname, message); + } else { + return drgn_error_format(DRGN_ERROR_OTHER, "%s: %s", file->path, + message); + } +} + +struct drgn_error * +drgn_elf_file_section_errorf(struct drgn_elf_file *file, Elf_Scn *scn, + Elf_Data *data, const char *ptr, + const char *format, ...) +{ + va_list ap; + va_start(ap, format); + char *message; + int ret = vasprintf(&message, format, ap); + va_end(ap); + if (ret < 0) + return &drgn_enomem; + struct drgn_error *err = drgn_elf_file_section_error(file, scn, data, + ptr, message); + free(message); + return err; +} + +struct drgn_error *drgn_elf_file_section_buffer_error(struct binary_buffer *bb, + const char *ptr, + const char *message) +{ + struct drgn_elf_file_section_buffer *buffer = + container_of(bb, struct drgn_elf_file_section_buffer, bb); + return drgn_elf_file_section_error(buffer->file, buffer->scn, + buffer->data, ptr, message); +} diff --git a/libdrgn/elf_file.h b/libdrgn/elf_file.h new file mode 100644 index 000000000..ae4c9677d --- /dev/null +++ b/libdrgn/elf_file.h @@ -0,0 +1,169 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// SPDX-License-Identifier: LGPL-2.1-or-later + +/** + * @file + * + * ELF files. + * + * See @ref ElfFile. + */ + +#ifndef DRGN_ELF_FILE_H +#define DRGN_ELF_FILE_H + +#include +#include +#include +#include + +#include "binary_buffer.h" +#include "elf_sections.h" // IWYU pragma: export +#include "platform.h" + +struct drgn_module; + +/** + * @ingroup Internals + * + * @defgroup ElfFile ELF files + * + * ELF file handling. + * + * @{ + */ + +/** + * Read the raw data from an ELF section, decompressing it first if it is + * compressed. + * + * @warning If the section is `SHT_NOBITS`, this returns an `Elf_Data` with + * `d_size >= 0 && d_buf == NULL`. + */ +struct drgn_error *read_elf_section(Elf_Scn *scn, Elf_Data **ret); + +static inline bool elf_data_contains_ptr(Elf_Data *data, const void *ptr) +{ + uintptr_t bufi = (uintptr_t)data->d_buf; + uintptr_t ptri = (uintptr_t)ptr; + return ptri >= bufi && ptri - bufi < data->d_size; +} + +/** An ELF file used by a @ref drgn_module. */ +struct drgn_elf_file { + /** Module using this file. */ + struct drgn_module *module; + /** Filesystem path to this file. */ + const char *path; + /** libelf handle. */ + Elf *elf; + /** libdw handle if we're using DWARF information from this file. */ + Dwarf *dwarf; + /** + * Platform of this file. + * + * This should take precedence over @ref drgn_program::platform when + * parsing this file. Note that there are some cases where it doesn't + * make sense for the program and file platforms to differ (e.g., stack + * unwinding), in which case the file should be ignored if its platform + * doesn't match the program's. + */ + struct drgn_platform platform; + /** Important ELF sections. */ + Elf_Scn *scns[DRGN_SECTION_INDEX_NUM]; + /** Data cached for important ELF sections. */ + Elf_Data *scn_data[DRGN_SECTION_INDEX_NUM_DATA]; + /** + * If the file has a debugaltlink file, the debugaltlink file's + * `.debug_info` section data. + */ + Elf_Data *alt_debug_info_data; + /** + * If the file has a debugaltlink file, the debugaltlink file's + * `.debug_str` section data. + */ + Elf_Data *alt_debug_str_data; +}; + +struct drgn_error *drgn_elf_file_create(struct drgn_module *module, + const char *path, Elf *elf, + struct drgn_elf_file **ret); + +void drgn_elf_file_destroy(struct drgn_elf_file *file); + +struct drgn_error *drgn_elf_file_precache_sections(struct drgn_elf_file *file); + +struct drgn_error * +drgn_elf_file_cache_section(struct drgn_elf_file *file, enum drgn_section_index scn); + +static inline bool +drgn_elf_file_is_little_endian(const struct drgn_elf_file *file) +{ + return drgn_platform_is_little_endian(&file->platform); +} + +static inline bool drgn_elf_file_bswap(const struct drgn_elf_file *file) +{ + return drgn_platform_bswap(&file->platform); +} + +static inline uint8_t +drgn_elf_file_address_size(const struct drgn_elf_file *file) +{ + return drgn_platform_address_size(&file->platform); +} + +static inline uint64_t +drgn_elf_file_address_mask(const struct drgn_elf_file *file) +{ + return drgn_platform_address_mask(&file->platform); +} + +struct drgn_error * +drgn_elf_file_section_error(struct drgn_elf_file *file, Elf_Scn *scn, + Elf_Data *data, const char *ptr, + const char *message) + __attribute__((__returns_nonnull__)); + +struct drgn_error * +drgn_elf_file_section_errorf(struct drgn_elf_file *file, Elf_Scn *scn, + Elf_Data *data, const char *ptr, + const char *format, ...) + __attribute__((__returns_nonnull__, __format__(__printf__, 5, 6))); + +struct drgn_elf_file_section_buffer { + struct binary_buffer bb; + struct drgn_elf_file *file; + Elf_Scn *scn; + Elf_Data *data; +}; + +struct drgn_error *drgn_elf_file_section_buffer_error(struct binary_buffer *bb, + const char *ptr, + const char *message); + +static inline void +drgn_elf_file_section_buffer_init(struct drgn_elf_file_section_buffer *buffer, + struct drgn_elf_file *file, Elf_Scn *scn, + Elf_Data *data) +{ + binary_buffer_init(&buffer->bb, data->d_buf, data->d_size, + drgn_elf_file_is_little_endian(file), + drgn_elf_file_section_buffer_error); + buffer->file = file; + buffer->scn = scn; + buffer->data = data; +} + +static inline void +drgn_elf_file_section_buffer_init_index(struct drgn_elf_file_section_buffer *buffer, + struct drgn_elf_file *file, + enum drgn_section_index scn) +{ + drgn_elf_file_section_buffer_init(buffer, file, file->scns[scn], + file->scn_data[scn]); +} + +/** @} */ + +#endif /* DRGN_ELF_FILE_H */ diff --git a/libdrgn/error.c b/libdrgn/error.c index 808af8c06..cec02eff3 100644 --- a/libdrgn/error.c +++ b/libdrgn/error.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include @@ -161,9 +161,8 @@ drgn_error_format_fault(uint64_t address, const char *format, ...) struct drgn_error *drgn_error_from_string_builder(enum drgn_error_code code, struct string_builder *sb) { - char *message; - - if (!string_builder_finalize(sb, &message)) { + char *message = string_builder_null_terminate(sb); + if (!message) { free(sb->str); return &drgn_enomem; } @@ -226,6 +225,16 @@ bool string_builder_append_error(struct string_builder *sb, #undef emit_error_format } +LIBDRGN_PUBLIC char *drgn_error_string(struct drgn_error *err) +{ + char *tmp; +#define emit_error_format(...) (asprintf(&tmp, __VA_ARGS__) < 0 ? NULL : tmp) +#define emit_error_string(s) strdup(s) + return emit_error(err); +#undef emit_error_string +#undef emit_error_format +} + LIBDRGN_PUBLIC int drgn_error_fwrite(FILE *file, struct drgn_error *err) { #define emit_error_format(format, ...) fprintf(file, format "\n", __VA_ARGS__) diff --git a/libdrgn/error.h b/libdrgn/error.h index f70dd250b..eee7c774d 100644 --- a/libdrgn/error.h +++ b/libdrgn/error.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file diff --git a/libdrgn/examples/load_debug_info.c b/libdrgn/examples/load_debug_info.c index 53e9eec73..7e1d8ded8 100644 --- a/libdrgn/examples/load_debug_info.c +++ b/libdrgn/examples/load_debug_info.c @@ -1,14 +1,57 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include +#include #include #include +#include +#include #include #include #include "drgn.h" +#include "util.h" + +extern char **environ; + +static struct drgn_error *run_command(const char *which, const char *command, + struct drgn_program *prog) +{ + if (!command) + return NULL; + + char pid_arg[max_decimal_length(long)]; + sprintf(pid_arg, "%ld", (long)getpid()); + char prog_arg[2 * sizeof(prog) + 3]; + sprintf(prog_arg, "%p", prog); + const char * const argv[] = { + "sh", "-c", command, "sh", pid_arg, prog_arg, NULL + }; + + pid_t pid; + int errnum = + posix_spawnp(&pid, "sh", NULL, NULL, (char **)argv, environ); + if (errnum) + return drgn_error_create_os("posix_spawnp", errnum, "sh"); + + int wstatus; + if (waitpid(pid, &wstatus, 0) < 0) + return drgn_error_create_os("waitpid", errno, NULL); + + if (!WIFEXITED(wstatus)) { + return drgn_error_format(DRGN_ERROR_OTHER, + "%s-exec command exited abnormally: %d", + which, wstatus); + } + if (WEXITSTATUS(wstatus) != 0) { + return drgn_error_format(DRGN_ERROR_OTHER, + "%s-exec command exited with status %d", + which, WEXITSTATUS(wstatus)); + } + return NULL; +} static inline struct timespec timespec_sub(struct timespec a, struct timespec b) { @@ -25,10 +68,10 @@ static inline struct timespec timespec_sub(struct timespec a, struct timespec b) } } -static void usage(bool error) +noreturn static void usage(bool error) { fprintf(error ? stderr : stdout, - "usage: load_debug_info [-k|-c CORE|-p PID] [PATH...]\n" + "usage: load_debug_info [OPTION...] [-k|-c CORE|-p PID] [PATH...]\n" "\n" "Example libdrgn program that loads default debug information\n" "\n" @@ -37,6 +80,11 @@ static void usage(bool error) " -c PATH, --core PATH debug the given core dump\n" " -p PID, --pid PID debug the running process with the given PID\n" " -T, --time print how long loading debug info took in seconds\n" + " --pre-exec CMD before loading debug info, execute the given shell\n" + " command with the PID of this process and the address\n" + " of the struct drgn_program in hexadecimal as arguments\n" + " --post-exec CMD after loading debug info, execute the given shell\n" + " command with the same arguments as --pre-exec\n" " -h, --help display this help message and exit\n"); exit(error ? EXIT_FAILURE : EXIT_SUCCESS); } @@ -48,6 +96,8 @@ int main(int argc, char **argv) {"core", required_argument, NULL, 'c'}, {"pid", required_argument, NULL, 'p'}, {"time", no_argument, NULL, 'T'}, + {"pre-exec", required_argument, NULL, 'x'}, + {"post-exec", required_argument, NULL, 'X'}, {"help", no_argument, NULL, 'h'}, {}, }; @@ -55,6 +105,8 @@ int main(int argc, char **argv) const char *core = NULL; const char *pid = NULL; bool print_time = false; + const char *pre_exec = NULL; + const char *post_exec = NULL; for (;;) { int c = getopt_long(argc, argv, "kc:p:Th", long_options, NULL); if (c == -1) @@ -72,6 +124,12 @@ int main(int argc, char **argv) case 'T': print_time = true; break; + case 'x': + pre_exec = optarg; + break; + case 'X': + post_exec = optarg; + break; case 'h': usage(false); default: @@ -97,30 +155,40 @@ int main(int argc, char **argv) if (err) goto out; + err = run_command("pre", pre_exec, prog); + if (err) + goto out; + struct timespec start, end; if (print_time && clock_gettime(CLOCK_MONOTONIC, &start)) abort(); err = drgn_program_load_debug_info(prog, (const char **)&argv[optind], argc - optind, kernel || core || pid, false); - if ((!err || err->code == DRGN_ERROR_MISSING_DEBUG_INFO) && print_time) { - if (clock_gettime(CLOCK_MONOTONIC, &end)) - abort(); + if ((!err || err->code == DRGN_ERROR_MISSING_DEBUG_INFO) + && print_time && clock_gettime(CLOCK_MONOTONIC, &end)) + abort(); + if (err && err->code == DRGN_ERROR_MISSING_DEBUG_INFO) { + drgn_error_fwrite(stderr, err); + drgn_error_destroy(err); + } else if (err) { + goto out; + } + + err = run_command("post", post_exec, prog); + if (err) + goto out; + + if (print_time) { struct timespec diff = timespec_sub(end, start); printf("%lld.%09ld\n", (long long)diff.tv_sec, diff.tv_nsec); } out:; - int status; + int status = err ? EXIT_FAILURE : EXIT_SUCCESS; if (err) { - if (err->code == DRGN_ERROR_MISSING_DEBUG_INFO) - status = EXIT_SUCCESS; - else - status = EXIT_FAILURE; drgn_error_fwrite(stderr, err); drgn_error_destroy(err); - } else { - status = EXIT_SUCCESS; } drgn_program_destroy(prog); return status; diff --git a/libdrgn/generics.h b/libdrgn/generics.h new file mode 100644 index 000000000..1406b7660 --- /dev/null +++ b/libdrgn/generics.h @@ -0,0 +1,39 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// SPDX-License-Identifier: LGPL-2.1-or-later + +/** + * @file + * + * Helpers for generic programming. + */ + +#ifndef DRGN_GENERICS_H +#define DRGN_GENERICS_H + +/** + * Choose a type based on a condition. + * + * @param[in] condition Controlling integer constant expression. + * @param[in] if_true Type if @p condition is non-zero. + * @param[in] if_false Type if @p condition is zero. + */ +#define type_if(condition, if_true, if_false) \ +__typeof__( \ + /* + 1 avoids a non-standard zero-length array. */ \ + *_Generic((int (*)[!(condition) + 1])0, \ + int (*)[1]: (__typeof__(if_true) *)0, \ + int (*)[2]: (__typeof__(if_false) *)0) \ +) + +/** + * Define a typedef based on a condition. + * + * @param[in] name Name of type. + * @param[in] condition Controlling integer constant expression. + * @param[in] if_true Type if @p condition is non-zero. + * @param[in] if_false Type if @p condition is zero. + */ +#define typedef_if(name, condition, if_true, if_false) \ + typedef type_if(condition, if_true, if_false) name + +#endif /* DRGN_GENERICS_H */ diff --git a/libdrgn/hash_table.c b/libdrgn/hash_table.c index 793bc0157..b5fb8a530 100644 --- a/libdrgn/hash_table.c +++ b/libdrgn/hash_table.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include "hash_table.h" diff --git a/libdrgn/hash_table.h b/libdrgn/hash_table.h index 6e6e47c27..f2c87dc64 100644 --- a/libdrgn/hash_table.h +++ b/libdrgn/hash_table.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -60,7 +60,7 @@ * key_type entry_to_key(const entry_type *entry); * struct hash_pair hash_func(const key_type *key); * bool eq_func(const key_type *a, const key_type *b); - * DEFINE_HASH_TABLE(hash_table, entry_type, entry_to_key, hash_func, eq_func) + * DEFINE_HASH_TABLE(hash_table, entry_type, entry_to_key, hash_func, eq_func); * @endcode * * @sa BinarySearchTrees @@ -301,10 +301,7 @@ struct hash_table_iterator hash_table_first(struct hash_table *table); struct hash_table_iterator hash_table_next(struct hash_table_iterator it); #endif -enum { - hash_table_chunk_alignment = max_iconst(alignof(max_align_t), - (size_t)16), -}; +enum { hash_table_chunk_alignment = max_iconst(alignof(max_align_t), 16) }; static inline size_t hash_table_probe_delta(struct hash_pair hp) { @@ -406,7 +403,8 @@ struct table { \ size_t size; \ uintptr_t first_packed; \ } basic[!table##_vector_policy]; \ -}; +}; \ +struct DEFINE_HASH_TABLE_needs_semicolon /* * Common search function implementation returning an item iterator. This is @@ -1406,7 +1404,8 @@ static struct table##_iterator table##_next(struct table##_iterator it) \ } else { \ return table##_next_impl(it, false); \ } \ -} +} \ +struct DEFINE_HASH_TABLE_needs_semicolon /** * Define a hash table interface. @@ -1425,7 +1424,7 @@ static struct table##_iterator table##_next(struct table##_iterator it) \ * * and returns a @c bool. */ #define DEFINE_HASH_TABLE(table, entry_type, entry_to_key, hash_func, eq_func) \ -DEFINE_HASH_TABLE_TYPE(table, entry_type) \ +DEFINE_HASH_TABLE_TYPE(table, entry_type); \ DEFINE_HASH_TABLE_FUNCTIONS(table, entry_to_key, hash_func, eq_func) /** @@ -1479,7 +1478,7 @@ DEFINE_HASH_TABLE_FUNCTIONS(table, HASH_MAP_ENTRY_TO_KEY, hash_func, eq_func) * @param[in] eq_func See @ref DEFINE_HASH_TABLE(). */ #define DEFINE_HASH_MAP(table, key_type, value_type, hash_func, eq_func) \ -DEFINE_HASH_MAP_TYPE(table, key_type, value_type) \ +DEFINE_HASH_MAP_TYPE(table, key_type, value_type); \ DEFINE_HASH_MAP_FUNCTIONS(table, hash_func, eq_func) /** @@ -1520,7 +1519,7 @@ DEFINE_HASH_TABLE_FUNCTIONS(table, HASH_SET_ENTRY_TO_KEY, hash_func, eq_func) * @param[in] eq_func See @ref DEFINE_HASH_TABLE(). */ #define DEFINE_HASH_SET(table, key_type, hash_func, eq_func) \ -DEFINE_HASH_SET_TYPE(table, key_type) \ +DEFINE_HASH_SET_TYPE(table, key_type); \ DEFINE_HASH_SET_FUNCTIONS(table, hash_func, eq_func) /** diff --git a/libdrgn/helpers.h b/libdrgn/helpers.h index 621714ede..b08a0b444 100644 --- a/libdrgn/helpers.h +++ b/libdrgn/helpers.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -28,16 +28,25 @@ struct drgn_error *linux_helper_read_vm(struct drgn_program *prog, uint64_t pgtable, uint64_t virt_addr, void *buf, size_t count); +struct drgn_error *linux_helper_follow_phys(struct drgn_program *prog, + uint64_t pgtable, + uint64_t virt_addr, uint64_t *ret); + struct drgn_error *linux_helper_per_cpu_ptr(struct drgn_object *res, const struct drgn_object *ptr, uint64_t cpu); +struct drgn_error *linux_helper_cpu_curr(struct drgn_object *res, uint64_t cpu); + struct drgn_error *linux_helper_idle_task(struct drgn_object *res, uint64_t cpu); +struct drgn_error *linux_helper_task_cpu(const struct drgn_object *task, + uint64_t *ret); + struct drgn_error * -linux_helper_radix_tree_lookup(struct drgn_object *res, - const struct drgn_object *root, uint64_t index); +linux_helper_xa_load(struct drgn_object *res, const struct drgn_object *xa, + uint64_t index); struct drgn_error *linux_helper_idr_find(struct drgn_object *res, const struct drgn_object *idr, diff --git a/libdrgn/include/dwarf.h b/libdrgn/include/dwarf.h deleted file mode 100644 index 19a4be966..000000000 --- a/libdrgn/include/dwarf.h +++ /dev/null @@ -1,1029 +0,0 @@ -/* This file defines standard DWARF types, structures, and macros. - Copyright (C) 2000-2011, 2014, 2016, 2017, 2018 Red Hat, Inc. - This file is part of elfutils. - - This file is free software; you can redistribute it and/or modify - it under the terms of either - - * the GNU Lesser General Public License as published by the Free - Software Foundation; either version 3 of the License, or (at - your option) any later version - - or - - * the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at - your option) any later version - - or both in parallel, as here. - - elfutils is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received copies of the GNU General Public License and - the GNU Lesser General Public License along with this program. If - not, see . */ - -#ifndef _DWARF_H -#define _DWARF_H 1 - -/* DWARF Unit Header Types. */ -enum - { - DW_UT_compile = 0x01, - DW_UT_type = 0x02, - DW_UT_partial = 0x03, - DW_UT_skeleton = 0x04, - DW_UT_split_compile = 0x05, - DW_UT_split_type = 0x06, - - DW_UT_lo_user = 0x80, - DW_UT_hi_user = 0xff - }; - -/* DWARF tags. */ -enum - { - DW_TAG_array_type = 0x01, - DW_TAG_class_type = 0x02, - DW_TAG_entry_point = 0x03, - DW_TAG_enumeration_type = 0x04, - DW_TAG_formal_parameter = 0x05, - /* 0x06 reserved. */ - /* 0x07 reserved. */ - DW_TAG_imported_declaration = 0x08, - /* 0x09 reserved. */ - DW_TAG_label = 0x0a, - DW_TAG_lexical_block = 0x0b, - /* 0x0c reserved. */ - DW_TAG_member = 0x0d, - /* 0x0e reserved. */ - DW_TAG_pointer_type = 0x0f, - DW_TAG_reference_type = 0x10, - DW_TAG_compile_unit = 0x11, - DW_TAG_string_type = 0x12, - DW_TAG_structure_type = 0x13, - /* 0x14 reserved. */ - DW_TAG_subroutine_type = 0x15, - DW_TAG_typedef = 0x16, - DW_TAG_union_type = 0x17, - DW_TAG_unspecified_parameters = 0x18, - DW_TAG_variant = 0x19, - DW_TAG_common_block = 0x1a, - DW_TAG_common_inclusion = 0x1b, - DW_TAG_inheritance = 0x1c, - DW_TAG_inlined_subroutine = 0x1d, - DW_TAG_module = 0x1e, - DW_TAG_ptr_to_member_type = 0x1f, - DW_TAG_set_type = 0x20, - DW_TAG_subrange_type = 0x21, - DW_TAG_with_stmt = 0x22, - DW_TAG_access_declaration = 0x23, - DW_TAG_base_type = 0x24, - DW_TAG_catch_block = 0x25, - DW_TAG_const_type = 0x26, - DW_TAG_constant = 0x27, - DW_TAG_enumerator = 0x28, - DW_TAG_file_type = 0x29, - DW_TAG_friend = 0x2a, - DW_TAG_namelist = 0x2b, - DW_TAG_namelist_item = 0x2c, - DW_TAG_packed_type = 0x2d, - DW_TAG_subprogram = 0x2e, - DW_TAG_template_type_parameter = 0x2f, - DW_TAG_template_value_parameter = 0x30, - DW_TAG_thrown_type = 0x31, - DW_TAG_try_block = 0x32, - DW_TAG_variant_part = 0x33, - DW_TAG_variable = 0x34, - DW_TAG_volatile_type = 0x35, - DW_TAG_dwarf_procedure = 0x36, - DW_TAG_restrict_type = 0x37, - DW_TAG_interface_type = 0x38, - DW_TAG_namespace = 0x39, - DW_TAG_imported_module = 0x3a, - DW_TAG_unspecified_type = 0x3b, - DW_TAG_partial_unit = 0x3c, - DW_TAG_imported_unit = 0x3d, - /* 0x3e reserved. Was DW_TAG_mutable_type. */ - DW_TAG_condition = 0x3f, - DW_TAG_shared_type = 0x40, - DW_TAG_type_unit = 0x41, - DW_TAG_rvalue_reference_type = 0x42, - DW_TAG_template_alias = 0x43, - DW_TAG_coarray_type = 0x44, - DW_TAG_generic_subrange = 0x45, - DW_TAG_dynamic_type = 0x46, - DW_TAG_atomic_type = 0x47, - DW_TAG_call_site = 0x48, - DW_TAG_call_site_parameter = 0x49, - DW_TAG_skeleton_unit = 0x4a, - DW_TAG_immutable_type = 0x4b, - - DW_TAG_lo_user = 0x4080, - - DW_TAG_MIPS_loop = 0x4081, - DW_TAG_format_label = 0x4101, - DW_TAG_function_template = 0x4102, - DW_TAG_class_template = 0x4103, - - DW_TAG_GNU_BINCL = 0x4104, - DW_TAG_GNU_EINCL = 0x4105, - - DW_TAG_GNU_template_template_param = 0x4106, - DW_TAG_GNU_template_parameter_pack = 0x4107, - DW_TAG_GNU_formal_parameter_pack = 0x4108, - DW_TAG_GNU_call_site = 0x4109, - DW_TAG_GNU_call_site_parameter = 0x410a, - - DW_TAG_hi_user = 0xffff - }; - - -/* Children determination encodings. */ -enum - { - DW_CHILDREN_no = 0, - DW_CHILDREN_yes = 1 - }; - - -/* DWARF attributes encodings. */ -enum - { - DW_AT_sibling = 0x01, - DW_AT_location = 0x02, - DW_AT_name = 0x03, - /* 0x04 reserved. */ - /* 0x05 reserved. */ - /* 0x06 reserved. */ - /* 0x07 reserved. */ - /* 0x08 reserved. */ - DW_AT_ordering = 0x09, - /* 0x0a reserved. */ - DW_AT_byte_size = 0x0b, - DW_AT_bit_offset = 0x0c, /* Deprecated in DWARF4. */ - DW_AT_bit_size = 0x0d, - /* 0x0e reserved. */ - /* 0x0f reserved. */ - DW_AT_stmt_list = 0x10, - DW_AT_low_pc = 0x11, - DW_AT_high_pc = 0x12, - DW_AT_language = 0x13, - /* 0x14 reserved. */ - DW_AT_discr = 0x15, - DW_AT_discr_value = 0x16, - DW_AT_visibility = 0x17, - DW_AT_import = 0x18, - DW_AT_string_length = 0x19, - DW_AT_common_reference = 0x1a, - DW_AT_comp_dir = 0x1b, - DW_AT_const_value = 0x1c, - DW_AT_containing_type = 0x1d, - DW_AT_default_value = 0x1e, - /* 0x1f reserved. */ - DW_AT_inline = 0x20, - DW_AT_is_optional = 0x21, - DW_AT_lower_bound = 0x22, - /* 0x23 reserved. */ - /* 0x24 reserved. */ - DW_AT_producer = 0x25, - /* 0x26 reserved. */ - DW_AT_prototyped = 0x27, - /* 0x28 reserved. */ - /* 0x29 reserved. */ - DW_AT_return_addr = 0x2a, - /* 0x2b reserved. */ - DW_AT_start_scope = 0x2c, - /* 0x2d reserved. */ - DW_AT_bit_stride = 0x2e, - DW_AT_upper_bound = 0x2f, - /* 0x30 reserved. */ - DW_AT_abstract_origin = 0x31, - DW_AT_accessibility = 0x32, - DW_AT_address_class = 0x33, - DW_AT_artificial = 0x34, - DW_AT_base_types = 0x35, - DW_AT_calling_convention = 0x36, - DW_AT_count = 0x37, - DW_AT_data_member_location = 0x38, - DW_AT_decl_column = 0x39, - DW_AT_decl_file = 0x3a, - DW_AT_decl_line = 0x3b, - DW_AT_declaration = 0x3c, - DW_AT_discr_list = 0x3d, - DW_AT_encoding = 0x3e, - DW_AT_external = 0x3f, - DW_AT_frame_base = 0x40, - DW_AT_friend = 0x41, - DW_AT_identifier_case = 0x42, - DW_AT_macro_info = 0x43, /* Deprecated in DWARF5. */ - DW_AT_namelist_item = 0x44, - DW_AT_priority = 0x45, - DW_AT_segment = 0x46, - DW_AT_specification = 0x47, - DW_AT_static_link = 0x48, - DW_AT_type = 0x49, - DW_AT_use_location = 0x4a, - DW_AT_variable_parameter = 0x4b, - DW_AT_virtuality = 0x4c, - DW_AT_vtable_elem_location = 0x4d, - DW_AT_allocated = 0x4e, - DW_AT_associated = 0x4f, - DW_AT_data_location = 0x50, - DW_AT_byte_stride = 0x51, - DW_AT_entry_pc = 0x52, - DW_AT_use_UTF8 = 0x53, - DW_AT_extension = 0x54, - DW_AT_ranges = 0x55, - DW_AT_trampoline = 0x56, - DW_AT_call_column = 0x57, - DW_AT_call_file = 0x58, - DW_AT_call_line = 0x59, - DW_AT_description = 0x5a, - DW_AT_binary_scale = 0x5b, - DW_AT_decimal_scale = 0x5c, - DW_AT_small = 0x5d, - DW_AT_decimal_sign = 0x5e, - DW_AT_digit_count = 0x5f, - DW_AT_picture_string = 0x60, - DW_AT_mutable = 0x61, - DW_AT_threads_scaled = 0x62, - DW_AT_explicit = 0x63, - DW_AT_object_pointer = 0x64, - DW_AT_endianity = 0x65, - DW_AT_elemental = 0x66, - DW_AT_pure = 0x67, - DW_AT_recursive = 0x68, - DW_AT_signature = 0x69, - DW_AT_main_subprogram = 0x6a, - DW_AT_data_bit_offset = 0x6b, - DW_AT_const_expr = 0x6c, - DW_AT_enum_class = 0x6d, - DW_AT_linkage_name = 0x6e, - DW_AT_string_length_bit_size = 0x6f, - DW_AT_string_length_byte_size = 0x70, - DW_AT_rank = 0x71, - DW_AT_str_offsets_base = 0x72, - DW_AT_addr_base = 0x73, - DW_AT_rnglists_base = 0x74, - /* 0x75 reserved. */ - DW_AT_dwo_name = 0x76, - DW_AT_reference = 0x77, - DW_AT_rvalue_reference = 0x78, - DW_AT_macros = 0x79, - DW_AT_call_all_calls = 0x7a, - DW_AT_call_all_source_calls = 0x7b, - DW_AT_call_all_tail_calls = 0x7c, - DW_AT_call_return_pc = 0x7d, - DW_AT_call_value = 0x7e, - DW_AT_call_origin = 0x7f, - DW_AT_call_parameter = 0x80, - DW_AT_call_pc = 0x81, - DW_AT_call_tail_call = 0x82, - DW_AT_call_target = 0x83, - DW_AT_call_target_clobbered = 0x84, - DW_AT_call_data_location = 0x85, - DW_AT_call_data_value = 0x86, - DW_AT_noreturn = 0x87, - DW_AT_alignment = 0x88, - DW_AT_export_symbols = 0x89, - DW_AT_deleted = 0x8a, - DW_AT_defaulted = 0x8b, - DW_AT_loclists_base = 0x8c, - - DW_AT_lo_user = 0x2000, - - DW_AT_MIPS_fde = 0x2001, - DW_AT_MIPS_loop_begin = 0x2002, - DW_AT_MIPS_tail_loop_begin = 0x2003, - DW_AT_MIPS_epilog_begin = 0x2004, - DW_AT_MIPS_loop_unroll_factor = 0x2005, - DW_AT_MIPS_software_pipeline_depth = 0x2006, - DW_AT_MIPS_linkage_name = 0x2007, - DW_AT_MIPS_stride = 0x2008, - DW_AT_MIPS_abstract_name = 0x2009, - DW_AT_MIPS_clone_origin = 0x200a, - DW_AT_MIPS_has_inlines = 0x200b, - DW_AT_MIPS_stride_byte = 0x200c, - DW_AT_MIPS_stride_elem = 0x200d, - DW_AT_MIPS_ptr_dopetype = 0x200e, - DW_AT_MIPS_allocatable_dopetype = 0x200f, - DW_AT_MIPS_assumed_shape_dopetype = 0x2010, - DW_AT_MIPS_assumed_size = 0x2011, - - /* GNU extensions. */ - DW_AT_sf_names = 0x2101, - DW_AT_src_info = 0x2102, - DW_AT_mac_info = 0x2103, - DW_AT_src_coords = 0x2104, - DW_AT_body_begin = 0x2105, - DW_AT_body_end = 0x2106, - DW_AT_GNU_vector = 0x2107, - DW_AT_GNU_guarded_by = 0x2108, - DW_AT_GNU_pt_guarded_by = 0x2109, - DW_AT_GNU_guarded = 0x210a, - DW_AT_GNU_pt_guarded = 0x210b, - DW_AT_GNU_locks_excluded = 0x210c, - DW_AT_GNU_exclusive_locks_required = 0x210d, - DW_AT_GNU_shared_locks_required = 0x210e, - DW_AT_GNU_odr_signature = 0x210f, - DW_AT_GNU_template_name = 0x2110, - DW_AT_GNU_call_site_value = 0x2111, - DW_AT_GNU_call_site_data_value = 0x2112, - DW_AT_GNU_call_site_target = 0x2113, - DW_AT_GNU_call_site_target_clobbered = 0x2114, - DW_AT_GNU_tail_call = 0x2115, - DW_AT_GNU_all_tail_call_sites = 0x2116, - DW_AT_GNU_all_call_sites = 0x2117, - DW_AT_GNU_all_source_call_sites = 0x2118, - DW_AT_GNU_locviews = 0x2137, - DW_AT_GNU_entry_view = 0x2138, - DW_AT_GNU_macros = 0x2119, - DW_AT_GNU_deleted = 0x211a, - /* GNU Debug Fission extensions. */ - DW_AT_GNU_dwo_name = 0x2130, - DW_AT_GNU_dwo_id = 0x2131, - DW_AT_GNU_ranges_base = 0x2132, - DW_AT_GNU_addr_base = 0x2133, - DW_AT_GNU_pubnames = 0x2134, - DW_AT_GNU_pubtypes = 0x2135, - - /* https://gcc.gnu.org/wiki/DW_AT_GNU_numerator_denominator */ - DW_AT_GNU_numerator = 0x2303, - DW_AT_GNU_denominator = 0x2304, - /* https://gcc.gnu.org/wiki/DW_AT_GNU_bias */ - DW_AT_GNU_bias = 0x2305, - - DW_AT_hi_user = 0x3fff - }; - -/* Old unofficially attribute names. Should not be used. - Will not appear in known-dwarf.h */ - -/* DWARF1 array subscripts and element data types. */ -#define DW_AT_subscr_data 0x0a -/* DWARF1 enumeration literals. */ -#define DW_AT_element_list 0x0f -/* DWARF1 reference for variable to member structure, class or union. */ -#define DW_AT_member 0x14 - -/* DWARF form encodings. */ -enum - { - DW_FORM_addr = 0x01, - DW_FORM_block2 = 0x03, - DW_FORM_block4 = 0x04, - DW_FORM_data2 = 0x05, - DW_FORM_data4 = 0x06, - DW_FORM_data8 = 0x07, - DW_FORM_string = 0x08, - DW_FORM_block = 0x09, - DW_FORM_block1 = 0x0a, - DW_FORM_data1 = 0x0b, - DW_FORM_flag = 0x0c, - DW_FORM_sdata = 0x0d, - DW_FORM_strp = 0x0e, - DW_FORM_udata = 0x0f, - DW_FORM_ref_addr = 0x10, - DW_FORM_ref1 = 0x11, - DW_FORM_ref2 = 0x12, - DW_FORM_ref4 = 0x13, - DW_FORM_ref8 = 0x14, - DW_FORM_ref_udata = 0x15, - DW_FORM_indirect = 0x16, - DW_FORM_sec_offset = 0x17, - DW_FORM_exprloc = 0x18, - DW_FORM_flag_present = 0x19, - DW_FORM_strx = 0x1a, - DW_FORM_addrx = 0x1b, - DW_FORM_ref_sup4 = 0x1c, - DW_FORM_strp_sup = 0x1d, - DW_FORM_data16 = 0x1e, - DW_FORM_line_strp = 0x1f, - DW_FORM_ref_sig8 = 0x20, - DW_FORM_implicit_const = 0x21, - DW_FORM_loclistx = 0x22, - DW_FORM_rnglistx = 0x23, - DW_FORM_ref_sup8 = 0x24, - DW_FORM_strx1 = 0x25, - DW_FORM_strx2 = 0x26, - DW_FORM_strx3 = 0x27, - DW_FORM_strx4 = 0x28, - DW_FORM_addrx1 = 0x29, - DW_FORM_addrx2 = 0x2a, - DW_FORM_addrx3 = 0x2b, - DW_FORM_addrx4 = 0x2c, - - /* GNU Debug Fission extensions. */ - DW_FORM_GNU_addr_index = 0x1f01, - DW_FORM_GNU_str_index = 0x1f02, - - DW_FORM_GNU_ref_alt = 0x1f20, /* offset in alternate .debuginfo. */ - DW_FORM_GNU_strp_alt = 0x1f21 /* offset in alternate .debug_str. */ - }; - - -/* DWARF location operation encodings. */ -enum - { - DW_OP_addr = 0x03, /* Constant address. */ - DW_OP_deref = 0x06, - DW_OP_const1u = 0x08, /* Unsigned 1-byte constant. */ - DW_OP_const1s = 0x09, /* Signed 1-byte constant. */ - DW_OP_const2u = 0x0a, /* Unsigned 2-byte constant. */ - DW_OP_const2s = 0x0b, /* Signed 2-byte constant. */ - DW_OP_const4u = 0x0c, /* Unsigned 4-byte constant. */ - DW_OP_const4s = 0x0d, /* Signed 4-byte constant. */ - DW_OP_const8u = 0x0e, /* Unsigned 8-byte constant. */ - DW_OP_const8s = 0x0f, /* Signed 8-byte constant. */ - DW_OP_constu = 0x10, /* Unsigned LEB128 constant. */ - DW_OP_consts = 0x11, /* Signed LEB128 constant. */ - DW_OP_dup = 0x12, - DW_OP_drop = 0x13, - DW_OP_over = 0x14, - DW_OP_pick = 0x15, /* 1-byte stack index. */ - DW_OP_swap = 0x16, - DW_OP_rot = 0x17, - DW_OP_xderef = 0x18, - DW_OP_abs = 0x19, - DW_OP_and = 0x1a, - DW_OP_div = 0x1b, - DW_OP_minus = 0x1c, - DW_OP_mod = 0x1d, - DW_OP_mul = 0x1e, - DW_OP_neg = 0x1f, - DW_OP_not = 0x20, - DW_OP_or = 0x21, - DW_OP_plus = 0x22, - DW_OP_plus_uconst = 0x23, /* Unsigned LEB128 addend. */ - DW_OP_shl = 0x24, - DW_OP_shr = 0x25, - DW_OP_shra = 0x26, - DW_OP_xor = 0x27, - DW_OP_bra = 0x28, /* Signed 2-byte constant. */ - DW_OP_eq = 0x29, - DW_OP_ge = 0x2a, - DW_OP_gt = 0x2b, - DW_OP_le = 0x2c, - DW_OP_lt = 0x2d, - DW_OP_ne = 0x2e, - DW_OP_skip = 0x2f, /* Signed 2-byte constant. */ - DW_OP_lit0 = 0x30, /* Literal 0. */ - DW_OP_lit1 = 0x31, /* Literal 1. */ - DW_OP_lit2 = 0x32, /* Literal 2. */ - DW_OP_lit3 = 0x33, /* Literal 3. */ - DW_OP_lit4 = 0x34, /* Literal 4. */ - DW_OP_lit5 = 0x35, /* Literal 5. */ - DW_OP_lit6 = 0x36, /* Literal 6. */ - DW_OP_lit7 = 0x37, /* Literal 7. */ - DW_OP_lit8 = 0x38, /* Literal 8. */ - DW_OP_lit9 = 0x39, /* Literal 9. */ - DW_OP_lit10 = 0x3a, /* Literal 10. */ - DW_OP_lit11 = 0x3b, /* Literal 11. */ - DW_OP_lit12 = 0x3c, /* Literal 12. */ - DW_OP_lit13 = 0x3d, /* Literal 13. */ - DW_OP_lit14 = 0x3e, /* Literal 14. */ - DW_OP_lit15 = 0x3f, /* Literal 15. */ - DW_OP_lit16 = 0x40, /* Literal 16. */ - DW_OP_lit17 = 0x41, /* Literal 17. */ - DW_OP_lit18 = 0x42, /* Literal 18. */ - DW_OP_lit19 = 0x43, /* Literal 19. */ - DW_OP_lit20 = 0x44, /* Literal 20. */ - DW_OP_lit21 = 0x45, /* Literal 21. */ - DW_OP_lit22 = 0x46, /* Literal 22. */ - DW_OP_lit23 = 0x47, /* Literal 23. */ - DW_OP_lit24 = 0x48, /* Literal 24. */ - DW_OP_lit25 = 0x49, /* Literal 25. */ - DW_OP_lit26 = 0x4a, /* Literal 26. */ - DW_OP_lit27 = 0x4b, /* Literal 27. */ - DW_OP_lit28 = 0x4c, /* Literal 28. */ - DW_OP_lit29 = 0x4d, /* Literal 29. */ - DW_OP_lit30 = 0x4e, /* Literal 30. */ - DW_OP_lit31 = 0x4f, /* Literal 31. */ - DW_OP_reg0 = 0x50, /* Register 0. */ - DW_OP_reg1 = 0x51, /* Register 1. */ - DW_OP_reg2 = 0x52, /* Register 2. */ - DW_OP_reg3 = 0x53, /* Register 3. */ - DW_OP_reg4 = 0x54, /* Register 4. */ - DW_OP_reg5 = 0x55, /* Register 5. */ - DW_OP_reg6 = 0x56, /* Register 6. */ - DW_OP_reg7 = 0x57, /* Register 7. */ - DW_OP_reg8 = 0x58, /* Register 8. */ - DW_OP_reg9 = 0x59, /* Register 9. */ - DW_OP_reg10 = 0x5a, /* Register 10. */ - DW_OP_reg11 = 0x5b, /* Register 11. */ - DW_OP_reg12 = 0x5c, /* Register 12. */ - DW_OP_reg13 = 0x5d, /* Register 13. */ - DW_OP_reg14 = 0x5e, /* Register 14. */ - DW_OP_reg15 = 0x5f, /* Register 15. */ - DW_OP_reg16 = 0x60, /* Register 16. */ - DW_OP_reg17 = 0x61, /* Register 17. */ - DW_OP_reg18 = 0x62, /* Register 18. */ - DW_OP_reg19 = 0x63, /* Register 19. */ - DW_OP_reg20 = 0x64, /* Register 20. */ - DW_OP_reg21 = 0x65, /* Register 21. */ - DW_OP_reg22 = 0x66, /* Register 22. */ - DW_OP_reg23 = 0x67, /* Register 24. */ - DW_OP_reg24 = 0x68, /* Register 24. */ - DW_OP_reg25 = 0x69, /* Register 25. */ - DW_OP_reg26 = 0x6a, /* Register 26. */ - DW_OP_reg27 = 0x6b, /* Register 27. */ - DW_OP_reg28 = 0x6c, /* Register 28. */ - DW_OP_reg29 = 0x6d, /* Register 29. */ - DW_OP_reg30 = 0x6e, /* Register 30. */ - DW_OP_reg31 = 0x6f, /* Register 31. */ - DW_OP_breg0 = 0x70, /* Base register 0. */ - DW_OP_breg1 = 0x71, /* Base register 1. */ - DW_OP_breg2 = 0x72, /* Base register 2. */ - DW_OP_breg3 = 0x73, /* Base register 3. */ - DW_OP_breg4 = 0x74, /* Base register 4. */ - DW_OP_breg5 = 0x75, /* Base register 5. */ - DW_OP_breg6 = 0x76, /* Base register 6. */ - DW_OP_breg7 = 0x77, /* Base register 7. */ - DW_OP_breg8 = 0x78, /* Base register 8. */ - DW_OP_breg9 = 0x79, /* Base register 9. */ - DW_OP_breg10 = 0x7a, /* Base register 10. */ - DW_OP_breg11 = 0x7b, /* Base register 11. */ - DW_OP_breg12 = 0x7c, /* Base register 12. */ - DW_OP_breg13 = 0x7d, /* Base register 13. */ - DW_OP_breg14 = 0x7e, /* Base register 14. */ - DW_OP_breg15 = 0x7f, /* Base register 15. */ - DW_OP_breg16 = 0x80, /* Base register 16. */ - DW_OP_breg17 = 0x81, /* Base register 17. */ - DW_OP_breg18 = 0x82, /* Base register 18. */ - DW_OP_breg19 = 0x83, /* Base register 19. */ - DW_OP_breg20 = 0x84, /* Base register 20. */ - DW_OP_breg21 = 0x85, /* Base register 21. */ - DW_OP_breg22 = 0x86, /* Base register 22. */ - DW_OP_breg23 = 0x87, /* Base register 23. */ - DW_OP_breg24 = 0x88, /* Base register 24. */ - DW_OP_breg25 = 0x89, /* Base register 25. */ - DW_OP_breg26 = 0x8a, /* Base register 26. */ - DW_OP_breg27 = 0x8b, /* Base register 27. */ - DW_OP_breg28 = 0x8c, /* Base register 28. */ - DW_OP_breg29 = 0x8d, /* Base register 29. */ - DW_OP_breg30 = 0x8e, /* Base register 30. */ - DW_OP_breg31 = 0x8f, /* Base register 31. */ - DW_OP_regx = 0x90, /* Unsigned LEB128 register. */ - DW_OP_fbreg = 0x91, /* Signed LEB128 offset. */ - DW_OP_bregx = 0x92, /* ULEB128 register followed by SLEB128 off. */ - DW_OP_piece = 0x93, /* ULEB128 size of piece addressed. */ - DW_OP_deref_size = 0x94, /* 1-byte size of data retrieved. */ - DW_OP_xderef_size = 0x95, /* 1-byte size of data retrieved. */ - DW_OP_nop = 0x96, - DW_OP_push_object_address = 0x97, - DW_OP_call2 = 0x98, - DW_OP_call4 = 0x99, - DW_OP_call_ref = 0x9a, - DW_OP_form_tls_address = 0x9b,/* TLS offset to address in current thread */ - DW_OP_call_frame_cfa = 0x9c,/* CFA as determined by CFI. */ - DW_OP_bit_piece = 0x9d, /* ULEB128 size and ULEB128 offset in bits. */ - DW_OP_implicit_value = 0x9e, /* DW_FORM_block follows opcode. */ - DW_OP_stack_value = 0x9f, /* No operands, special like DW_OP_piece. */ - - DW_OP_implicit_pointer = 0xa0, - DW_OP_addrx = 0xa1, - DW_OP_constx = 0xa2, - DW_OP_entry_value = 0xa3, - DW_OP_const_type = 0xa4, - DW_OP_regval_type = 0xa5, - DW_OP_deref_type = 0xa6, - DW_OP_xderef_type = 0xa7, - DW_OP_convert = 0xa8, - DW_OP_reinterpret = 0xa9, - - /* GNU extensions. */ - DW_OP_GNU_push_tls_address = 0xe0, - DW_OP_GNU_uninit = 0xf0, - DW_OP_GNU_encoded_addr = 0xf1, - DW_OP_GNU_implicit_pointer = 0xf2, - DW_OP_GNU_entry_value = 0xf3, - DW_OP_GNU_const_type = 0xf4, - DW_OP_GNU_regval_type = 0xf5, - DW_OP_GNU_deref_type = 0xf6, - DW_OP_GNU_convert = 0xf7, - DW_OP_GNU_reinterpret = 0xf9, - DW_OP_GNU_parameter_ref = 0xfa, - - /* GNU Debug Fission extensions. */ - DW_OP_GNU_addr_index = 0xfb, - DW_OP_GNU_const_index = 0xfc, - - DW_OP_GNU_variable_value = 0xfd, - - DW_OP_lo_user = 0xe0, /* Implementation-defined range start. */ - DW_OP_hi_user = 0xff /* Implementation-defined range end. */ - }; - - -/* DWARF base type encodings. */ -enum - { - DW_ATE_void = 0x0, - DW_ATE_address = 0x1, - DW_ATE_boolean = 0x2, - DW_ATE_complex_float = 0x3, - DW_ATE_float = 0x4, - DW_ATE_signed = 0x5, - DW_ATE_signed_char = 0x6, - DW_ATE_unsigned = 0x7, - DW_ATE_unsigned_char = 0x8, - DW_ATE_imaginary_float = 0x9, - DW_ATE_packed_decimal = 0xa, - DW_ATE_numeric_string = 0xb, - DW_ATE_edited = 0xc, - DW_ATE_signed_fixed = 0xd, - DW_ATE_unsigned_fixed = 0xe, - DW_ATE_decimal_float = 0xf, - DW_ATE_UTF = 0x10, - DW_ATE_UCS = 0x11, - DW_ATE_ASCII = 0x12, - - DW_ATE_lo_user = 0x80, - DW_ATE_hi_user = 0xff - }; - - -/* DWARF decimal sign encodings. */ -enum - { - DW_DS_unsigned = 1, - DW_DS_leading_overpunch = 2, - DW_DS_trailing_overpunch = 3, - DW_DS_leading_separate = 4, - DW_DS_trailing_separate = 5, - }; - - -/* DWARF endianity encodings. */ -enum - { - DW_END_default = 0, - DW_END_big = 1, - DW_END_little = 2, - - DW_END_lo_user = 0x40, - DW_END_hi_user = 0xff - }; - - -/* DWARF accessibility encodings. */ -enum - { - DW_ACCESS_public = 1, - DW_ACCESS_protected = 2, - DW_ACCESS_private = 3 - }; - - -/* DWARF visibility encodings. */ -enum - { - DW_VIS_local = 1, - DW_VIS_exported = 2, - DW_VIS_qualified = 3 - }; - - -/* DWARF virtuality encodings. */ -enum - { - DW_VIRTUALITY_none = 0, - DW_VIRTUALITY_virtual = 1, - DW_VIRTUALITY_pure_virtual = 2 - }; - - -/* DWARF language encodings. */ -enum - { - DW_LANG_C89 = 0x0001, /* ISO C:1989 */ - DW_LANG_C = 0x0002, /* C */ - DW_LANG_Ada83 = 0x0003, /* ISO Ada:1983 */ - DW_LANG_C_plus_plus = 0x0004, /* ISO C++:1998 */ - DW_LANG_Cobol74 = 0x0005, /* ISO Cobol:1974 */ - DW_LANG_Cobol85 = 0x0006, /* ISO Cobol:1985 */ - DW_LANG_Fortran77 = 0x0007, /* ISO FORTRAN 77 */ - DW_LANG_Fortran90 = 0x0008, /* ISO Fortran 90 */ - DW_LANG_Pascal83 = 0x0009, /* ISO Pascal:1983 */ - DW_LANG_Modula2 = 0x000a, /* ISO Modula-2:1996 */ - DW_LANG_Java = 0x000b, /* Java */ - DW_LANG_C99 = 0x000c, /* ISO C:1999 */ - DW_LANG_Ada95 = 0x000d, /* ISO Ada:1995 */ - DW_LANG_Fortran95 = 0x000e, /* ISO Fortran 95 */ - DW_LANG_PLI = 0x000f, /* ISO PL/1:1976 */ - DW_LANG_ObjC = 0x0010, /* Objective-C */ - DW_LANG_ObjC_plus_plus = 0x0011, /* Objective-C++ */ - DW_LANG_UPC = 0x0012, /* Unified Parallel C */ - DW_LANG_D = 0x0013, /* D */ - DW_LANG_Python = 0x0014, /* Python */ - DW_LANG_OpenCL = 0x0015, /* OpenCL */ - DW_LANG_Go = 0x0016, /* Go */ - DW_LANG_Modula3 = 0x0017, /* Modula-3 */ - DW_LANG_Haskell = 0x0018, /* Haskell */ - DW_LANG_C_plus_plus_03 = 0x0019, /* ISO C++:2003 */ - DW_LANG_C_plus_plus_11 = 0x001a, /* ISO C++:2011 */ - DW_LANG_OCaml = 0x001b, /* OCaml */ - DW_LANG_Rust = 0x001c, /* Rust */ - DW_LANG_C11 = 0x001d, /* ISO C:2011 */ - DW_LANG_Swift = 0x001e, /* Swift */ - DW_LANG_Julia = 0x001f, /* Julia */ - DW_LANG_Dylan = 0x0020, /* Dylan */ - DW_LANG_C_plus_plus_14 = 0x0021, /* ISO C++:2014 */ - DW_LANG_Fortran03 = 0x0022, /* ISO/IEC 1539-1:2004 */ - DW_LANG_Fortran08 = 0x0023, /* ISO/IEC 1539-1:2010 */ - DW_LANG_RenderScript = 0x0024, /* RenderScript Kernal Language */ - DW_LANG_BLISS = 0x0025, /* BLISS */ - - DW_LANG_lo_user = 0x8000, - DW_LANG_Mips_Assembler = 0x8001, /* Assembler */ - DW_LANG_hi_user = 0xffff - }; - -/* Old (typo) '1' != 'I'. */ -#define DW_LANG_PL1 DW_LANG_PLI - -/* DWARF identifier case encodings. */ -enum - { - DW_ID_case_sensitive = 0, - DW_ID_up_case = 1, - DW_ID_down_case = 2, - DW_ID_case_insensitive = 3 - }; - - -/* DWARF calling conventions encodings. - Used as values of DW_AT_calling_convention for subroutines - (normal, program or nocall) or structures, unions and class types - (normal, reference or value). */ -enum - { - DW_CC_normal = 0x1, - DW_CC_program = 0x2, - DW_CC_nocall = 0x3, - DW_CC_pass_by_reference = 0x4, - DW_CC_pass_by_value = 0x5, - DW_CC_lo_user = 0x40, - DW_CC_hi_user = 0xff - }; - - -/* DWARF inline encodings. */ -enum - { - DW_INL_not_inlined = 0, - DW_INL_inlined = 1, - DW_INL_declared_not_inlined = 2, - DW_INL_declared_inlined = 3 - }; - - -/* DWARF ordering encodings. */ -enum - { - DW_ORD_row_major = 0, - DW_ORD_col_major = 1 - }; - - -/* DWARF discriminant descriptor encodings. */ -enum - { - DW_DSC_label = 0, - DW_DSC_range = 1 - }; - -/* DWARF defaulted member function encodings. */ -enum - { - DW_DEFAULTED_no = 0, - DW_DEFAULTED_in_class = 1, - DW_DEFAULTED_out_of_class = 2 - }; - -/* DWARF line content descriptions. */ -enum - { - DW_LNCT_path = 0x1, - DW_LNCT_directory_index = 0x2, - DW_LNCT_timestamp = 0x3, - DW_LNCT_size = 0x4, - DW_LNCT_MD5 = 0x5, - DW_LNCT_lo_user = 0x2000, - DW_LNCT_hi_user = 0x3fff - }; - -/* DWARF standard opcode encodings. */ -enum - { - DW_LNS_copy = 1, - DW_LNS_advance_pc = 2, - DW_LNS_advance_line = 3, - DW_LNS_set_file = 4, - DW_LNS_set_column = 5, - DW_LNS_negate_stmt = 6, - DW_LNS_set_basic_block = 7, - DW_LNS_const_add_pc = 8, - DW_LNS_fixed_advance_pc = 9, - DW_LNS_set_prologue_end = 10, - DW_LNS_set_epilogue_begin = 11, - DW_LNS_set_isa = 12 - }; - - -/* DWARF extended opcode encodings. */ -enum - { - DW_LNE_end_sequence = 1, - DW_LNE_set_address = 2, - DW_LNE_define_file = 3, - DW_LNE_set_discriminator = 4, - - DW_LNE_lo_user = 128, - DW_LNE_hi_user = 255 - }; - - -/* DWARF macinfo type encodings. */ -enum - { - DW_MACINFO_define = 1, - DW_MACINFO_undef = 2, - DW_MACINFO_start_file = 3, - DW_MACINFO_end_file = 4, - DW_MACINFO_vendor_ext = 255 - }; - - -/* DWARF debug_macro type encodings. */ -enum - { - DW_MACRO_define = 0x01, - DW_MACRO_undef = 0x02, - DW_MACRO_start_file = 0x03, - DW_MACRO_end_file = 0x04, - DW_MACRO_define_strp = 0x05, - DW_MACRO_undef_strp = 0x06, - DW_MACRO_import = 0x07, - DW_MACRO_define_sup = 0x08, - DW_MACRO_undef_sup = 0x09, - DW_MACRO_import_sup = 0x0a, - DW_MACRO_define_strx = 0x0b, - DW_MACRO_undef_strx = 0x0c, - DW_MACRO_lo_user = 0xe0, - DW_MACRO_hi_user = 0xff - }; - -/* Old GNU extension names for DWARF5 debug_macro type encodings. - There are no equivalents for the supplementary object file (sup) - and indirect string references (strx). */ -#define DW_MACRO_GNU_define DW_MACRO_define -#define DW_MACRO_GNU_undef DW_MACRO_undef -#define DW_MACRO_GNU_start_file DW_MACRO_start_file -#define DW_MACRO_GNU_end_file DW_MACRO_end_file -#define DW_MACRO_GNU_define_indirect DW_MACRO_define_strp -#define DW_MACRO_GNU_undef_indirect DW_MACRO_undef_strp -#define DW_MACRO_GNU_transparent_include DW_MACRO_import -#define DW_MACRO_GNU_lo_user DW_MACRO_lo_user -#define DW_MACRO_GNU_hi_user DW_MACRO_hi_user - - -/* Range list entry encoding. */ -enum - { - DW_RLE_end_of_list = 0x0, - DW_RLE_base_addressx = 0x1, - DW_RLE_startx_endx = 0x2, - DW_RLE_startx_length = 0x3, - DW_RLE_offset_pair = 0x4, - DW_RLE_base_address = 0x5, - DW_RLE_start_end = 0x6, - DW_RLE_start_length = 0x7 - }; - - -/* Location list entry encoding. */ -enum - { - DW_LLE_end_of_list = 0x0, - DW_LLE_base_addressx = 0x1, - DW_LLE_startx_endx = 0x2, - DW_LLE_startx_length = 0x3, - DW_LLE_offset_pair = 0x4, - DW_LLE_default_location = 0x5, - DW_LLE_base_address = 0x6, - DW_LLE_start_end = 0x7, - DW_LLE_start_length = 0x8 - }; - - -/* GNU DebugFission list entry encodings (.debug_loc.dwo). */ -enum - { - DW_LLE_GNU_end_of_list_entry = 0x0, - DW_LLE_GNU_base_address_selection_entry = 0x1, - DW_LLE_GNU_start_end_entry = 0x2, - DW_LLE_GNU_start_length_entry = 0x3 - }; - - -/* DWARF call frame instruction encodings. */ -enum - { - DW_CFA_advance_loc = 0x40, - DW_CFA_offset = 0x80, - DW_CFA_restore = 0xc0, - DW_CFA_extended = 0, - - DW_CFA_nop = 0x00, - DW_CFA_set_loc = 0x01, - DW_CFA_advance_loc1 = 0x02, - DW_CFA_advance_loc2 = 0x03, - DW_CFA_advance_loc4 = 0x04, - DW_CFA_offset_extended = 0x05, - DW_CFA_restore_extended = 0x06, - DW_CFA_undefined = 0x07, - DW_CFA_same_value = 0x08, - DW_CFA_register = 0x09, - DW_CFA_remember_state = 0x0a, - DW_CFA_restore_state = 0x0b, - DW_CFA_def_cfa = 0x0c, - DW_CFA_def_cfa_register = 0x0d, - DW_CFA_def_cfa_offset = 0x0e, - DW_CFA_def_cfa_expression = 0x0f, - DW_CFA_expression = 0x10, - DW_CFA_offset_extended_sf = 0x11, - DW_CFA_def_cfa_sf = 0x12, - DW_CFA_def_cfa_offset_sf = 0x13, - DW_CFA_val_offset = 0x14, - DW_CFA_val_offset_sf = 0x15, - DW_CFA_val_expression = 0x16, - - DW_CFA_low_user = 0x1c, - DW_CFA_MIPS_advance_loc8 = 0x1d, - DW_CFA_GNU_window_save = 0x2d, - DW_CFA_AARCH64_negate_ra_state = 0x2d, - DW_CFA_GNU_args_size = 0x2e, - DW_CFA_GNU_negative_offset_extended = 0x2f, - DW_CFA_high_user = 0x3f - }; - -/* ID indicating CIE as opposed to FDE in .debug_frame. */ -enum - { - DW_CIE_ID_32 = 0xffffffffU, /* In 32-bit format CIE header. */ - DW_CIE_ID_64 = 0xffffffffffffffffULL /* In 64-bit format CIE header. */ - }; - - -/* Information for GNU unwind information. */ -enum - { - DW_EH_PE_absptr = 0x00, - DW_EH_PE_omit = 0xff, - - /* FDE data encoding. */ - DW_EH_PE_uleb128 = 0x01, - DW_EH_PE_udata2 = 0x02, - DW_EH_PE_udata4 = 0x03, - DW_EH_PE_udata8 = 0x04, - DW_EH_PE_sleb128 = 0x09, - DW_EH_PE_sdata2 = 0x0a, - DW_EH_PE_sdata4 = 0x0b, - DW_EH_PE_sdata8 = 0x0c, - DW_EH_PE_signed = 0x08, - - /* FDE flags. */ - DW_EH_PE_pcrel = 0x10, - DW_EH_PE_textrel = 0x20, - DW_EH_PE_datarel = 0x30, - DW_EH_PE_funcrel = 0x40, - DW_EH_PE_aligned = 0x50, - - DW_EH_PE_indirect = 0x80 - }; - - -/* DWARF XXX. */ -#define DW_ADDR_none 0 - -/* Section 7.2.2 of the DWARF3 specification defines a range of escape - codes that can appear in the length field of certain DWARF structures. - - These defines enumerate the minimum and maximum values of this range. - Currently only the maximum value is used (to indicate that 64-bit - values are going to be used in the dwarf data that accompanies the - structure). The other values are reserved. - - Note: There is a typo in DWARF3 spec (published Dec 20, 2005). In - sections 7.4, 7.5.1, 7.19, 7.20 the minimum escape code is referred to - as 0xffffff00 whereas in fact it should be 0xfffffff0. */ -#define DWARF3_LENGTH_MIN_ESCAPE_CODE 0xfffffff0u -#define DWARF3_LENGTH_MAX_ESCAPE_CODE 0xffffffffu -#define DWARF3_LENGTH_64_BIT DWARF3_LENGTH_MAX_ESCAPE_CODE - -#endif /* dwarf.h */ diff --git a/libdrgn/include/elf.h b/libdrgn/include/elf.h index 9ebd052c8..10f0ccb4c 100644 --- a/libdrgn/include/elf.h +++ b/libdrgn/include/elf.h @@ -1,4100 +1,185 @@ -/* This file defines standard ELF types, structures, and macros. - Copyright (C) 1995-2021 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, see - . */ - -#ifndef _ELF_H -#define _ELF_H 1 - -/* Standard ELF types. */ - -#include - -/* Type for a 16-bit quantity. */ -typedef uint16_t Elf32_Half; -typedef uint16_t Elf64_Half; - -/* Types for signed and unsigned 32-bit quantities. */ -typedef uint32_t Elf32_Word; -typedef int32_t Elf32_Sword; -typedef uint32_t Elf64_Word; -typedef int32_t Elf64_Sword; - -/* Types for signed and unsigned 64-bit quantities. */ -typedef uint64_t Elf32_Xword; -typedef int64_t Elf32_Sxword; -typedef uint64_t Elf64_Xword; -typedef int64_t Elf64_Sxword; - -/* Type of addresses. */ -typedef uint32_t Elf32_Addr; -typedef uint64_t Elf64_Addr; - -/* Type of file offsets. */ -typedef uint32_t Elf32_Off; -typedef uint64_t Elf64_Off; - -/* Type for section indices, which are 16-bit quantities. */ -typedef uint16_t Elf32_Section; -typedef uint16_t Elf64_Section; - -/* Type for version symbol information. */ -typedef Elf32_Half Elf32_Versym; -typedef Elf64_Half Elf64_Versym; - - -/* The ELF file header. This appears at the start of every ELF file. */ - -#define EI_NIDENT (16) - -typedef struct -{ - unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ - Elf32_Half e_type; /* Object file type */ - Elf32_Half e_machine; /* Architecture */ - Elf32_Word e_version; /* Object file version */ - Elf32_Addr e_entry; /* Entry point virtual address */ - Elf32_Off e_phoff; /* Program header table file offset */ - Elf32_Off e_shoff; /* Section header table file offset */ - Elf32_Word e_flags; /* Processor-specific flags */ - Elf32_Half e_ehsize; /* ELF header size in bytes */ - Elf32_Half e_phentsize; /* Program header table entry size */ - Elf32_Half e_phnum; /* Program header table entry count */ - Elf32_Half e_shentsize; /* Section header table entry size */ - Elf32_Half e_shnum; /* Section header table entry count */ - Elf32_Half e_shstrndx; /* Section header string table index */ -} Elf32_Ehdr; - -typedef struct -{ - unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ - Elf64_Half e_type; /* Object file type */ - Elf64_Half e_machine; /* Architecture */ - Elf64_Word e_version; /* Object file version */ - Elf64_Addr e_entry; /* Entry point virtual address */ - Elf64_Off e_phoff; /* Program header table file offset */ - Elf64_Off e_shoff; /* Section header table file offset */ - Elf64_Word e_flags; /* Processor-specific flags */ - Elf64_Half e_ehsize; /* ELF header size in bytes */ - Elf64_Half e_phentsize; /* Program header table entry size */ - Elf64_Half e_phnum; /* Program header table entry count */ - Elf64_Half e_shentsize; /* Section header table entry size */ - Elf64_Half e_shnum; /* Section header table entry count */ - Elf64_Half e_shstrndx; /* Section header string table index */ -} Elf64_Ehdr; - -/* Fields in the e_ident array. The EI_* macros are indices into the - array. The macros under each EI_* macro are the values the byte - may have. */ - -#define EI_MAG0 0 /* File identification byte 0 index */ -#define ELFMAG0 0x7f /* Magic number byte 0 */ - -#define EI_MAG1 1 /* File identification byte 1 index */ -#define ELFMAG1 'E' /* Magic number byte 1 */ - -#define EI_MAG2 2 /* File identification byte 2 index */ -#define ELFMAG2 'L' /* Magic number byte 2 */ - -#define EI_MAG3 3 /* File identification byte 3 index */ -#define ELFMAG3 'F' /* Magic number byte 3 */ - -/* Conglomeration of the identification bytes, for easy testing as a word. */ -#define ELFMAG "\177ELF" -#define SELFMAG 4 - -#define EI_CLASS 4 /* File class byte index */ -#define ELFCLASSNONE 0 /* Invalid class */ -#define ELFCLASS32 1 /* 32-bit objects */ -#define ELFCLASS64 2 /* 64-bit objects */ -#define ELFCLASSNUM 3 - -#define EI_DATA 5 /* Data encoding byte index */ -#define ELFDATANONE 0 /* Invalid data encoding */ -#define ELFDATA2LSB 1 /* 2's complement, little endian */ -#define ELFDATA2MSB 2 /* 2's complement, big endian */ -#define ELFDATANUM 3 - -#define EI_VERSION 6 /* File version byte index */ - /* Value must be EV_CURRENT */ - -#define EI_OSABI 7 /* OS ABI identification */ -#define ELFOSABI_NONE 0 /* UNIX System V ABI */ -#define ELFOSABI_SYSV 0 /* Alias. */ -#define ELFOSABI_HPUX 1 /* HP-UX */ -#define ELFOSABI_NETBSD 2 /* NetBSD. */ -#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */ -#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */ -#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ -#define ELFOSABI_AIX 7 /* IBM AIX. */ -#define ELFOSABI_IRIX 8 /* SGI Irix. */ -#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ -#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ -#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ -#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ -#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */ -#define ELFOSABI_ARM 97 /* ARM */ -#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ - -#define EI_ABIVERSION 8 /* ABI version */ - -#define EI_PAD 9 /* Byte index of padding bytes */ - -/* Legal values for e_type (object file type). */ - -#define ET_NONE 0 /* No file type */ -#define ET_REL 1 /* Relocatable file */ -#define ET_EXEC 2 /* Executable file */ -#define ET_DYN 3 /* Shared object file */ -#define ET_CORE 4 /* Core file */ -#define ET_NUM 5 /* Number of defined types */ -#define ET_LOOS 0xfe00 /* OS-specific range start */ -#define ET_HIOS 0xfeff /* OS-specific range end */ -#define ET_LOPROC 0xff00 /* Processor-specific range start */ -#define ET_HIPROC 0xffff /* Processor-specific range end */ - -/* Legal values for e_machine (architecture). */ - -#define EM_NONE 0 /* No machine */ -#define EM_M32 1 /* AT&T WE 32100 */ -#define EM_SPARC 2 /* SUN SPARC */ -#define EM_386 3 /* Intel 80386 */ -#define EM_68K 4 /* Motorola m68k family */ -#define EM_88K 5 /* Motorola m88k family */ -#define EM_IAMCU 6 /* Intel MCU */ -#define EM_860 7 /* Intel 80860 */ -#define EM_MIPS 8 /* MIPS R3000 big-endian */ -#define EM_S370 9 /* IBM System/370 */ -#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ - /* reserved 11-14 */ -#define EM_PARISC 15 /* HPPA */ - /* reserved 16 */ -#define EM_VPP500 17 /* Fujitsu VPP500 */ -#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ -#define EM_960 19 /* Intel 80960 */ -#define EM_PPC 20 /* PowerPC */ -#define EM_PPC64 21 /* PowerPC 64-bit */ -#define EM_S390 22 /* IBM S390 */ -#define EM_SPU 23 /* IBM SPU/SPC */ - /* reserved 24-35 */ -#define EM_V800 36 /* NEC V800 series */ -#define EM_FR20 37 /* Fujitsu FR20 */ -#define EM_RH32 38 /* TRW RH-32 */ -#define EM_RCE 39 /* Motorola RCE */ -#define EM_ARM 40 /* ARM */ -#define EM_FAKE_ALPHA 41 /* Digital Alpha */ -#define EM_SH 42 /* Hitachi SH */ -#define EM_SPARCV9 43 /* SPARC v9 64-bit */ -#define EM_TRICORE 44 /* Siemens Tricore */ -#define EM_ARC 45 /* Argonaut RISC Core */ -#define EM_H8_300 46 /* Hitachi H8/300 */ -#define EM_H8_300H 47 /* Hitachi H8/300H */ -#define EM_H8S 48 /* Hitachi H8S */ -#define EM_H8_500 49 /* Hitachi H8/500 */ -#define EM_IA_64 50 /* Intel Merced */ -#define EM_MIPS_X 51 /* Stanford MIPS-X */ -#define EM_COLDFIRE 52 /* Motorola Coldfire */ -#define EM_68HC12 53 /* Motorola M68HC12 */ -#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator */ -#define EM_PCP 55 /* Siemens PCP */ -#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ -#define EM_NDR1 57 /* Denso NDR1 microprocessor */ -#define EM_STARCORE 58 /* Motorola Start*Core processor */ -#define EM_ME16 59 /* Toyota ME16 processor */ -#define EM_ST100 60 /* STMicroelectronic ST100 processor */ -#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam */ -#define EM_X86_64 62 /* AMD x86-64 architecture */ -#define EM_PDSP 63 /* Sony DSP Processor */ -#define EM_PDP10 64 /* Digital PDP-10 */ -#define EM_PDP11 65 /* Digital PDP-11 */ -#define EM_FX66 66 /* Siemens FX66 microcontroller */ -#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ -#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ -#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ -#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ -#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ -#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ -#define EM_SVX 73 /* Silicon Graphics SVx */ -#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ -#define EM_VAX 75 /* Digital VAX */ -#define EM_CRIS 76 /* Axis Communications 32-bit emb.proc */ -#define EM_JAVELIN 77 /* Infineon Technologies 32-bit emb.proc */ -#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ -#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ -#define EM_MMIX 80 /* Donald Knuth's educational 64-bit proc */ -#define EM_HUANY 81 /* Harvard University machine-independent object files */ -#define EM_PRISM 82 /* SiTera Prism */ -#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ -#define EM_FR30 84 /* Fujitsu FR30 */ -#define EM_D10V 85 /* Mitsubishi D10V */ -#define EM_D30V 86 /* Mitsubishi D30V */ -#define EM_V850 87 /* NEC v850 */ -#define EM_M32R 88 /* Mitsubishi M32R */ -#define EM_MN10300 89 /* Matsushita MN10300 */ -#define EM_MN10200 90 /* Matsushita MN10200 */ -#define EM_PJ 91 /* picoJava */ -#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ -#define EM_ARC_COMPACT 93 /* ARC International ARCompact */ -#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ -#define EM_VIDEOCORE 95 /* Alphamosaic VideoCore */ -#define EM_TMM_GPP 96 /* Thompson Multimedia General Purpose Proc */ -#define EM_NS32K 97 /* National Semi. 32000 */ -#define EM_TPC 98 /* Tenor Network TPC */ -#define EM_SNP1K 99 /* Trebia SNP 1000 */ -#define EM_ST200 100 /* STMicroelectronics ST200 */ -#define EM_IP2K 101 /* Ubicom IP2xxx */ -#define EM_MAX 102 /* MAX processor */ -#define EM_CR 103 /* National Semi. CompactRISC */ -#define EM_F2MC16 104 /* Fujitsu F2MC16 */ -#define EM_MSP430 105 /* Texas Instruments msp430 */ -#define EM_BLACKFIN 106 /* Analog Devices Blackfin DSP */ -#define EM_SE_C33 107 /* Seiko Epson S1C33 family */ -#define EM_SEP 108 /* Sharp embedded microprocessor */ -#define EM_ARCA 109 /* Arca RISC */ -#define EM_UNICORE 110 /* PKU-Unity & MPRC Peking Uni. mc series */ -#define EM_EXCESS 111 /* eXcess configurable cpu */ -#define EM_DXP 112 /* Icera Semi. Deep Execution Processor */ -#define EM_ALTERA_NIOS2 113 /* Altera Nios II */ -#define EM_CRX 114 /* National Semi. CompactRISC CRX */ -#define EM_XGATE 115 /* Motorola XGATE */ -#define EM_C166 116 /* Infineon C16x/XC16x */ -#define EM_M16C 117 /* Renesas M16C */ -#define EM_DSPIC30F 118 /* Microchip Technology dsPIC30F */ -#define EM_CE 119 /* Freescale Communication Engine RISC */ -#define EM_M32C 120 /* Renesas M32C */ - /* reserved 121-130 */ -#define EM_TSK3000 131 /* Altium TSK3000 */ -#define EM_RS08 132 /* Freescale RS08 */ -#define EM_SHARC 133 /* Analog Devices SHARC family */ -#define EM_ECOG2 134 /* Cyan Technology eCOG2 */ -#define EM_SCORE7 135 /* Sunplus S+core7 RISC */ -#define EM_DSP24 136 /* New Japan Radio (NJR) 24-bit DSP */ -#define EM_VIDEOCORE3 137 /* Broadcom VideoCore III */ -#define EM_LATTICEMICO32 138 /* RISC for Lattice FPGA */ -#define EM_SE_C17 139 /* Seiko Epson C17 */ -#define EM_TI_C6000 140 /* Texas Instruments TMS320C6000 DSP */ -#define EM_TI_C2000 141 /* Texas Instruments TMS320C2000 DSP */ -#define EM_TI_C5500 142 /* Texas Instruments TMS320C55x DSP */ -#define EM_TI_ARP32 143 /* Texas Instruments App. Specific RISC */ -#define EM_TI_PRU 144 /* Texas Instruments Prog. Realtime Unit */ - /* reserved 145-159 */ -#define EM_MMDSP_PLUS 160 /* STMicroelectronics 64bit VLIW DSP */ -#define EM_CYPRESS_M8C 161 /* Cypress M8C */ -#define EM_R32C 162 /* Renesas R32C */ -#define EM_TRIMEDIA 163 /* NXP Semi. TriMedia */ -#define EM_QDSP6 164 /* QUALCOMM DSP6 */ -#define EM_8051 165 /* Intel 8051 and variants */ -#define EM_STXP7X 166 /* STMicroelectronics STxP7x */ -#define EM_NDS32 167 /* Andes Tech. compact code emb. RISC */ -#define EM_ECOG1X 168 /* Cyan Technology eCOG1X */ -#define EM_MAXQ30 169 /* Dallas Semi. MAXQ30 mc */ -#define EM_XIMO16 170 /* New Japan Radio (NJR) 16-bit DSP */ -#define EM_MANIK 171 /* M2000 Reconfigurable RISC */ -#define EM_CRAYNV2 172 /* Cray NV2 vector architecture */ -#define EM_RX 173 /* Renesas RX */ -#define EM_METAG 174 /* Imagination Tech. META */ -#define EM_MCST_ELBRUS 175 /* MCST Elbrus */ -#define EM_ECOG16 176 /* Cyan Technology eCOG16 */ -#define EM_CR16 177 /* National Semi. CompactRISC CR16 */ -#define EM_ETPU 178 /* Freescale Extended Time Processing Unit */ -#define EM_SLE9X 179 /* Infineon Tech. SLE9X */ -#define EM_L10M 180 /* Intel L10M */ -#define EM_K10M 181 /* Intel K10M */ - /* reserved 182 */ -#define EM_AARCH64 183 /* ARM AARCH64 */ - /* reserved 184 */ -#define EM_AVR32 185 /* Amtel 32-bit microprocessor */ -#define EM_STM8 186 /* STMicroelectronics STM8 */ -#define EM_TILE64 187 /* Tilera TILE64 */ -#define EM_TILEPRO 188 /* Tilera TILEPro */ -#define EM_MICROBLAZE 189 /* Xilinx MicroBlaze */ -#define EM_CUDA 190 /* NVIDIA CUDA */ -#define EM_TILEGX 191 /* Tilera TILE-Gx */ -#define EM_CLOUDSHIELD 192 /* CloudShield */ -#define EM_COREA_1ST 193 /* KIPO-KAIST Core-A 1st gen. */ -#define EM_COREA_2ND 194 /* KIPO-KAIST Core-A 2nd gen. */ -#define EM_ARCV2 195 /* Synopsys ARCv2 ISA. */ -#define EM_OPEN8 196 /* Open8 RISC */ -#define EM_RL78 197 /* Renesas RL78 */ -#define EM_VIDEOCORE5 198 /* Broadcom VideoCore V */ -#define EM_78KOR 199 /* Renesas 78KOR */ -#define EM_56800EX 200 /* Freescale 56800EX DSC */ -#define EM_BA1 201 /* Beyond BA1 */ -#define EM_BA2 202 /* Beyond BA2 */ -#define EM_XCORE 203 /* XMOS xCORE */ -#define EM_MCHP_PIC 204 /* Microchip 8-bit PIC(r) */ - /* reserved 205-209 */ -#define EM_KM32 210 /* KM211 KM32 */ -#define EM_KMX32 211 /* KM211 KMX32 */ -#define EM_EMX16 212 /* KM211 KMX16 */ -#define EM_EMX8 213 /* KM211 KMX8 */ -#define EM_KVARC 214 /* KM211 KVARC */ -#define EM_CDP 215 /* Paneve CDP */ -#define EM_COGE 216 /* Cognitive Smart Memory Processor */ -#define EM_COOL 217 /* Bluechip CoolEngine */ -#define EM_NORC 218 /* Nanoradio Optimized RISC */ -#define EM_CSR_KALIMBA 219 /* CSR Kalimba */ -#define EM_Z80 220 /* Zilog Z80 */ -#define EM_VISIUM 221 /* Controls and Data Services VISIUMcore */ -#define EM_FT32 222 /* FTDI Chip FT32 */ -#define EM_MOXIE 223 /* Moxie processor */ -#define EM_AMDGPU 224 /* AMD GPU */ - /* reserved 225-242 */ -#define EM_RISCV 243 /* RISC-V */ - -#define EM_BPF 247 /* Linux BPF -- in-kernel virtual machine */ -#define EM_CSKY 252 /* C-SKY */ - -#define EM_NUM 253 - -/* Old spellings/synonyms. */ - -#define EM_ARC_A5 EM_ARC_COMPACT - -/* If it is necessary to assign new unofficial EM_* values, please - pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the - chances of collision with official or non-GNU unofficial values. */ - -#define EM_ALPHA 0x9026 - -/* Legal values for e_version (version). */ - -#define EV_NONE 0 /* Invalid ELF version */ -#define EV_CURRENT 1 /* Current version */ -#define EV_NUM 2 - -/* Section header. */ - -typedef struct -{ - Elf32_Word sh_name; /* Section name (string tbl index) */ - Elf32_Word sh_type; /* Section type */ - Elf32_Word sh_flags; /* Section flags */ - Elf32_Addr sh_addr; /* Section virtual addr at execution */ - Elf32_Off sh_offset; /* Section file offset */ - Elf32_Word sh_size; /* Section size in bytes */ - Elf32_Word sh_link; /* Link to another section */ - Elf32_Word sh_info; /* Additional section information */ - Elf32_Word sh_addralign; /* Section alignment */ - Elf32_Word sh_entsize; /* Entry size if section holds table */ -} Elf32_Shdr; - -typedef struct -{ - Elf64_Word sh_name; /* Section name (string tbl index) */ - Elf64_Word sh_type; /* Section type */ - Elf64_Xword sh_flags; /* Section flags */ - Elf64_Addr sh_addr; /* Section virtual addr at execution */ - Elf64_Off sh_offset; /* Section file offset */ - Elf64_Xword sh_size; /* Section size in bytes */ - Elf64_Word sh_link; /* Link to another section */ - Elf64_Word sh_info; /* Additional section information */ - Elf64_Xword sh_addralign; /* Section alignment */ - Elf64_Xword sh_entsize; /* Entry size if section holds table */ -} Elf64_Shdr; - -/* Special section indices. */ - -#define SHN_UNDEF 0 /* Undefined section */ -#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ -#define SHN_LOPROC 0xff00 /* Start of processor-specific */ -#define SHN_BEFORE 0xff00 /* Order section before all others - (Solaris). */ -#define SHN_AFTER 0xff01 /* Order section after all others - (Solaris). */ -#define SHN_HIPROC 0xff1f /* End of processor-specific */ -#define SHN_LOOS 0xff20 /* Start of OS-specific */ -#define SHN_HIOS 0xff3f /* End of OS-specific */ -#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ -#define SHN_COMMON 0xfff2 /* Associated symbol is common */ -#define SHN_XINDEX 0xffff /* Index is in extra table. */ -#define SHN_HIRESERVE 0xffff /* End of reserved indices */ - -/* Legal values for sh_type (section type). */ - -#define SHT_NULL 0 /* Section header table entry unused */ -#define SHT_PROGBITS 1 /* Program data */ -#define SHT_SYMTAB 2 /* Symbol table */ -#define SHT_STRTAB 3 /* String table */ -#define SHT_RELA 4 /* Relocation entries with addends */ -#define SHT_HASH 5 /* Symbol hash table */ -#define SHT_DYNAMIC 6 /* Dynamic linking information */ -#define SHT_NOTE 7 /* Notes */ -#define SHT_NOBITS 8 /* Program space with no data (bss) */ -#define SHT_REL 9 /* Relocation entries, no addends */ -#define SHT_SHLIB 10 /* Reserved */ -#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ -#define SHT_INIT_ARRAY 14 /* Array of constructors */ -#define SHT_FINI_ARRAY 15 /* Array of destructors */ -#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ -#define SHT_GROUP 17 /* Section group */ -#define SHT_SYMTAB_SHNDX 18 /* Extended section indices */ -#define SHT_NUM 19 /* Number of defined types. */ -#define SHT_LOOS 0x60000000 /* Start OS-specific. */ -#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */ -#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ -#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ -#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ -#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ -#define SHT_SUNW_move 0x6ffffffa -#define SHT_SUNW_COMDAT 0x6ffffffb -#define SHT_SUNW_syminfo 0x6ffffffc -#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ -#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ -#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ -#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ -#define SHT_HIOS 0x6fffffff /* End OS-specific type */ -#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ -#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ -#define SHT_LOUSER 0x80000000 /* Start of application-specific */ -#define SHT_HIUSER 0x8fffffff /* End of application-specific */ - -/* Legal values for sh_flags (section flags). */ - -#define SHF_WRITE (1 << 0) /* Writable */ -#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ -#define SHF_EXECINSTR (1 << 2) /* Executable */ -#define SHF_MERGE (1 << 4) /* Might be merged */ -#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ -#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ -#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ -#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling - required */ -#define SHF_GROUP (1 << 9) /* Section is member of a group. */ -#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ -#define SHF_COMPRESSED (1 << 11) /* Section with compressed data. */ -#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ -#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ -#define SHF_GNU_RETAIN (1 << 21) /* Not to be GCed by linker. */ -#define SHF_ORDERED (1 << 30) /* Special ordering requirement - (Solaris). */ -#define SHF_EXCLUDE (1U << 31) /* Section is excluded unless - referenced or allocated (Solaris).*/ - -/* Section compression header. Used when SHF_COMPRESSED is set. */ - -typedef struct -{ - Elf32_Word ch_type; /* Compression format. */ - Elf32_Word ch_size; /* Uncompressed data size. */ - Elf32_Word ch_addralign; /* Uncompressed data alignment. */ -} Elf32_Chdr; - -typedef struct -{ - Elf64_Word ch_type; /* Compression format. */ - Elf64_Word ch_reserved; - Elf64_Xword ch_size; /* Uncompressed data size. */ - Elf64_Xword ch_addralign; /* Uncompressed data alignment. */ -} Elf64_Chdr; - -/* Legal values for ch_type (compression algorithm). */ -#define ELFCOMPRESS_ZLIB 1 /* ZLIB/DEFLATE algorithm. */ -#define ELFCOMPRESS_LOOS 0x60000000 /* Start of OS-specific. */ -#define ELFCOMPRESS_HIOS 0x6fffffff /* End of OS-specific. */ -#define ELFCOMPRESS_LOPROC 0x70000000 /* Start of processor-specific. */ -#define ELFCOMPRESS_HIPROC 0x7fffffff /* End of processor-specific. */ - -/* Section group handling. */ -#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ - -/* Symbol table entry. */ - -typedef struct -{ - Elf32_Word st_name; /* Symbol name (string tbl index) */ - Elf32_Addr st_value; /* Symbol value */ - Elf32_Word st_size; /* Symbol size */ - unsigned char st_info; /* Symbol type and binding */ - unsigned char st_other; /* Symbol visibility */ - Elf32_Section st_shndx; /* Section index */ -} Elf32_Sym; - -typedef struct -{ - Elf64_Word st_name; /* Symbol name (string tbl index) */ - unsigned char st_info; /* Symbol type and binding */ - unsigned char st_other; /* Symbol visibility */ - Elf64_Section st_shndx; /* Section index */ - Elf64_Addr st_value; /* Symbol value */ - Elf64_Xword st_size; /* Symbol size */ -} Elf64_Sym; - -/* The syminfo section if available contains additional information about - every dynamic symbol. */ - -typedef struct -{ - Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ - Elf32_Half si_flags; /* Per symbol flags */ -} Elf32_Syminfo; - -typedef struct -{ - Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ - Elf64_Half si_flags; /* Per symbol flags */ -} Elf64_Syminfo; - -/* Possible values for si_boundto. */ -#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ -#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ -#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ - -/* Possible bitmasks for si_flags. */ -#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ -#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ -#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ -#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy - loaded */ -/* Syminfo version values. */ -#define SYMINFO_NONE 0 -#define SYMINFO_CURRENT 1 -#define SYMINFO_NUM 2 - - -/* How to extract and insert information held in the st_info field. */ - -#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) -#define ELF32_ST_TYPE(val) ((val) & 0xf) -#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) - -/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ -#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) -#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) -#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) - -/* Legal values for ST_BIND subfield of st_info (symbol binding). */ - -#define STB_LOCAL 0 /* Local symbol */ -#define STB_GLOBAL 1 /* Global symbol */ -#define STB_WEAK 2 /* Weak symbol */ -#define STB_NUM 3 /* Number of defined types. */ -#define STB_LOOS 10 /* Start of OS-specific */ -#define STB_GNU_UNIQUE 10 /* Unique symbol. */ -#define STB_HIOS 12 /* End of OS-specific */ -#define STB_LOPROC 13 /* Start of processor-specific */ -#define STB_HIPROC 15 /* End of processor-specific */ - -/* Legal values for ST_TYPE subfield of st_info (symbol type). */ - -#define STT_NOTYPE 0 /* Symbol type is unspecified */ -#define STT_OBJECT 1 /* Symbol is a data object */ -#define STT_FUNC 2 /* Symbol is a code object */ -#define STT_SECTION 3 /* Symbol associated with a section */ -#define STT_FILE 4 /* Symbol's name is file name */ -#define STT_COMMON 5 /* Symbol is a common data object */ -#define STT_TLS 6 /* Symbol is thread-local data object*/ -#define STT_NUM 7 /* Number of defined types. */ -#define STT_LOOS 10 /* Start of OS-specific */ -#define STT_GNU_IFUNC 10 /* Symbol is indirect code object */ -#define STT_HIOS 12 /* End of OS-specific */ -#define STT_LOPROC 13 /* Start of processor-specific */ -#define STT_HIPROC 15 /* End of processor-specific */ - - -/* Symbol table indices are found in the hash buckets and chain table - of a symbol hash table section. This special index value indicates - the end of a chain, meaning no further symbols are found in that bucket. */ - -#define STN_UNDEF 0 /* End of a chain. */ - - -/* How to extract and insert information held in the st_other field. */ - -#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) - -/* For ELF64 the definitions are the same. */ -#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) - -/* Symbol visibility specification encoded in the st_other field. */ -#define STV_DEFAULT 0 /* Default symbol visibility rules */ -#define STV_INTERNAL 1 /* Processor specific hidden class */ -#define STV_HIDDEN 2 /* Sym unavailable in other modules */ -#define STV_PROTECTED 3 /* Not preemptible, not exported */ - - -/* Relocation table entry without addend (in section of type SHT_REL). */ - -typedef struct -{ - Elf32_Addr r_offset; /* Address */ - Elf32_Word r_info; /* Relocation type and symbol index */ -} Elf32_Rel; - -/* I have seen two different definitions of the Elf64_Rel and - Elf64_Rela structures, so we'll leave them out until Novell (or - whoever) gets their act together. */ -/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ - -typedef struct -{ - Elf64_Addr r_offset; /* Address */ - Elf64_Xword r_info; /* Relocation type and symbol index */ -} Elf64_Rel; - -/* Relocation table entry with addend (in section of type SHT_RELA). */ - -typedef struct -{ - Elf32_Addr r_offset; /* Address */ - Elf32_Word r_info; /* Relocation type and symbol index */ - Elf32_Sword r_addend; /* Addend */ -} Elf32_Rela; - -typedef struct -{ - Elf64_Addr r_offset; /* Address */ - Elf64_Xword r_info; /* Relocation type and symbol index */ - Elf64_Sxword r_addend; /* Addend */ -} Elf64_Rela; - -/* How to extract and insert information held in the r_info field. */ - -#define ELF32_R_SYM(val) ((val) >> 8) -#define ELF32_R_TYPE(val) ((val) & 0xff) -#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) - -#define ELF64_R_SYM(i) ((i) >> 32) -#define ELF64_R_TYPE(i) ((i) & 0xffffffff) -#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) - -/* Program segment header. */ - -typedef struct -{ - Elf32_Word p_type; /* Segment type */ - Elf32_Off p_offset; /* Segment file offset */ - Elf32_Addr p_vaddr; /* Segment virtual address */ - Elf32_Addr p_paddr; /* Segment physical address */ - Elf32_Word p_filesz; /* Segment size in file */ - Elf32_Word p_memsz; /* Segment size in memory */ - Elf32_Word p_flags; /* Segment flags */ - Elf32_Word p_align; /* Segment alignment */ -} Elf32_Phdr; - -typedef struct -{ - Elf64_Word p_type; /* Segment type */ - Elf64_Word p_flags; /* Segment flags */ - Elf64_Off p_offset; /* Segment file offset */ - Elf64_Addr p_vaddr; /* Segment virtual address */ - Elf64_Addr p_paddr; /* Segment physical address */ - Elf64_Xword p_filesz; /* Segment size in file */ - Elf64_Xword p_memsz; /* Segment size in memory */ - Elf64_Xword p_align; /* Segment alignment */ -} Elf64_Phdr; - -/* Special value for e_phnum. This indicates that the real number of - program headers is too large to fit into e_phnum. Instead the real - value is in the field sh_info of section 0. */ - -#define PN_XNUM 0xffff - -/* Legal values for p_type (segment type). */ - -#define PT_NULL 0 /* Program header table entry unused */ -#define PT_LOAD 1 /* Loadable program segment */ -#define PT_DYNAMIC 2 /* Dynamic linking information */ -#define PT_INTERP 3 /* Program interpreter */ -#define PT_NOTE 4 /* Auxiliary information */ -#define PT_SHLIB 5 /* Reserved */ -#define PT_PHDR 6 /* Entry for header table itself */ -#define PT_TLS 7 /* Thread-local storage segment */ -#define PT_NUM 8 /* Number of defined types */ -#define PT_LOOS 0x60000000 /* Start of OS-specific */ -#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ -#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ -#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ -#define PT_GNU_PROPERTY 0x6474e553 /* GNU property */ -#define PT_LOSUNW 0x6ffffffa -#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ -#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ -#define PT_HISUNW 0x6fffffff -#define PT_HIOS 0x6fffffff /* End of OS-specific */ -#define PT_LOPROC 0x70000000 /* Start of processor-specific */ -#define PT_HIPROC 0x7fffffff /* End of processor-specific */ - -/* Legal values for p_flags (segment flags). */ - -#define PF_X (1 << 0) /* Segment is executable */ -#define PF_W (1 << 1) /* Segment is writable */ -#define PF_R (1 << 2) /* Segment is readable */ -#define PF_MASKOS 0x0ff00000 /* OS-specific */ -#define PF_MASKPROC 0xf0000000 /* Processor-specific */ - -/* Legal values for note segment descriptor types for core files. */ - -#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ -#define NT_PRFPREG 2 /* Contains copy of fpregset - struct. */ -#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ -#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ -#define NT_PRXREG 4 /* Contains copy of prxregset struct */ -#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ -#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ -#define NT_AUXV 6 /* Contains copy of auxv array */ -#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ -#define NT_ASRS 8 /* Contains copy of asrset struct */ -#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ -#define NT_PSINFO 13 /* Contains copy of psinfo struct */ -#define NT_PRCRED 14 /* Contains copy of prcred struct */ -#define NT_UTSNAME 15 /* Contains copy of utsname struct */ -#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ -#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ -#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */ -#define NT_SIGINFO 0x53494749 /* Contains copy of siginfo_t, - size might increase */ -#define NT_FILE 0x46494c45 /* Contains information about mapped - files */ -#define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */ -#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ -#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ -#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ -#define NT_PPC_TAR 0x103 /* Target Address Register */ -#define NT_PPC_PPR 0x104 /* Program Priority Register */ -#define NT_PPC_DSCR 0x105 /* Data Stream Control Register */ -#define NT_PPC_EBB 0x106 /* Event Based Branch Registers */ -#define NT_PPC_PMU 0x107 /* Performance Monitor Registers */ -#define NT_PPC_TM_CGPR 0x108 /* TM checkpointed GPR Registers */ -#define NT_PPC_TM_CFPR 0x109 /* TM checkpointed FPR Registers */ -#define NT_PPC_TM_CVMX 0x10a /* TM checkpointed VMX Registers */ -#define NT_PPC_TM_CVSX 0x10b /* TM checkpointed VSX Registers */ -#define NT_PPC_TM_SPR 0x10c /* TM Special Purpose Registers */ -#define NT_PPC_TM_CTAR 0x10d /* TM checkpointed Target Address - Register */ -#define NT_PPC_TM_CPPR 0x10e /* TM checkpointed Program Priority - Register */ -#define NT_PPC_TM_CDSCR 0x10f /* TM checkpointed Data Stream Control - Register */ -#define NT_PPC_PKEY 0x110 /* Memory Protection Keys - registers. */ -#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ -#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ -#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ -#define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */ -#define NT_S390_TIMER 0x301 /* s390 timer register */ -#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */ -#define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */ -#define NT_S390_CTRS 0x304 /* s390 control registers */ -#define NT_S390_PREFIX 0x305 /* s390 prefix register */ -#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */ -#define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */ -#define NT_S390_TDB 0x308 /* s390 transaction diagnostic block */ -#define NT_S390_VXRS_LOW 0x309 /* s390 vector registers 0-15 - upper half. */ -#define NT_S390_VXRS_HIGH 0x30a /* s390 vector registers 16-31. */ -#define NT_S390_GS_CB 0x30b /* s390 guarded storage registers. */ -#define NT_S390_GS_BC 0x30c /* s390 guarded storage - broadcast control block. */ -#define NT_S390_RI_CB 0x30d /* s390 runtime instrumentation. */ -#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */ -#define NT_ARM_TLS 0x401 /* ARM TLS register */ -#define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ -#define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ -#define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */ -#define NT_ARM_SVE 0x405 /* ARM Scalable Vector Extension - registers */ -#define NT_ARM_PAC_MASK 0x406 /* ARM pointer authentication - code masks. */ -#define NT_ARM_PACA_KEYS 0x407 /* ARM pointer authentication - address keys. */ -#define NT_ARM_PACG_KEYS 0x408 /* ARM pointer authentication - generic key. */ -#define NT_VMCOREDD 0x700 /* Vmcore Device Dump Note. */ -#define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers. */ -#define NT_MIPS_FP_MODE 0x801 /* MIPS floating-point mode. */ -#define NT_MIPS_MSA 0x802 /* MIPS SIMD registers. */ - -/* Legal values for the note segment descriptor types for object files. */ - -#define NT_VERSION 1 /* Contains a version string. */ - - -/* Dynamic section entry. */ - -typedef struct -{ - Elf32_Sword d_tag; /* Dynamic entry type */ - union - { - Elf32_Word d_val; /* Integer value */ - Elf32_Addr d_ptr; /* Address value */ - } d_un; -} Elf32_Dyn; - -typedef struct -{ - Elf64_Sxword d_tag; /* Dynamic entry type */ - union - { - Elf64_Xword d_val; /* Integer value */ - Elf64_Addr d_ptr; /* Address value */ - } d_un; -} Elf64_Dyn; - -/* Legal values for d_tag (dynamic entry type). */ - -#define DT_NULL 0 /* Marks end of dynamic section */ -#define DT_NEEDED 1 /* Name of needed library */ -#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ -#define DT_PLTGOT 3 /* Processor defined value */ -#define DT_HASH 4 /* Address of symbol hash table */ -#define DT_STRTAB 5 /* Address of string table */ -#define DT_SYMTAB 6 /* Address of symbol table */ -#define DT_RELA 7 /* Address of Rela relocs */ -#define DT_RELASZ 8 /* Total size of Rela relocs */ -#define DT_RELAENT 9 /* Size of one Rela reloc */ -#define DT_STRSZ 10 /* Size of string table */ -#define DT_SYMENT 11 /* Size of one symbol table entry */ -#define DT_INIT 12 /* Address of init function */ -#define DT_FINI 13 /* Address of termination function */ -#define DT_SONAME 14 /* Name of shared object */ -#define DT_RPATH 15 /* Library search path (deprecated) */ -#define DT_SYMBOLIC 16 /* Start symbol search here */ -#define DT_REL 17 /* Address of Rel relocs */ -#define DT_RELSZ 18 /* Total size of Rel relocs */ -#define DT_RELENT 19 /* Size of one Rel reloc */ -#define DT_PLTREL 20 /* Type of reloc in PLT */ -#define DT_DEBUG 21 /* For debugging; unspecified */ -#define DT_TEXTREL 22 /* Reloc might modify .text */ -#define DT_JMPREL 23 /* Address of PLT relocs */ -#define DT_BIND_NOW 24 /* Process relocations of object */ -#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ -#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ -#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ -#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ -#define DT_RUNPATH 29 /* Library search path */ -#define DT_FLAGS 30 /* Flags for the object being loaded */ -#define DT_ENCODING 32 /* Start of encoded range */ -#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ -#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ -#define DT_SYMTAB_SHNDX 34 /* Address of SYMTAB_SHNDX section */ -#define DT_NUM 35 /* Number used */ -#define DT_LOOS 0x6000000d /* Start of OS-specific */ -#define DT_HIOS 0x6ffff000 /* End of OS-specific */ -#define DT_LOPROC 0x70000000 /* Start of processor-specific */ -#define DT_HIPROC 0x7fffffff /* End of processor-specific */ -#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ - -/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the - Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's - approach. */ -#define DT_VALRNGLO 0x6ffffd00 -#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ -#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ -#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ -#define DT_CHECKSUM 0x6ffffdf8 -#define DT_PLTPADSZ 0x6ffffdf9 -#define DT_MOVEENT 0x6ffffdfa -#define DT_MOVESZ 0x6ffffdfb -#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ -#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting - the following DT_* entry. */ -#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ -#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ -#define DT_VALRNGHI 0x6ffffdff -#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ -#define DT_VALNUM 12 - -/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the - Dyn.d_un.d_ptr field of the Elf*_Dyn structure. - - If any adjustment is made to the ELF object after it has been - built these entries will need to be adjusted. */ -#define DT_ADDRRNGLO 0x6ffffe00 -#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */ -#define DT_TLSDESC_PLT 0x6ffffef6 -#define DT_TLSDESC_GOT 0x6ffffef7 -#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ -#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ -#define DT_CONFIG 0x6ffffefa /* Configuration information. */ -#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ -#define DT_AUDIT 0x6ffffefc /* Object auditing. */ -#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ -#define DT_MOVETAB 0x6ffffefe /* Move table. */ -#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ -#define DT_ADDRRNGHI 0x6ffffeff -#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ -#define DT_ADDRNUM 11 - -/* The versioning entry types. The next are defined as part of the - GNU extension. */ -#define DT_VERSYM 0x6ffffff0 - -#define DT_RELACOUNT 0x6ffffff9 -#define DT_RELCOUNT 0x6ffffffa - -/* These were chosen by Sun. */ -#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ -#define DT_VERDEF 0x6ffffffc /* Address of version definition - table */ -#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ -#define DT_VERNEED 0x6ffffffe /* Address of table with needed - versions */ -#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ -#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ -#define DT_VERSIONTAGNUM 16 - -/* Sun added these machine-independent extensions in the "processor-specific" - range. Be compatible. */ -#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ -#define DT_FILTER 0x7fffffff /* Shared object to get values from */ -#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) -#define DT_EXTRANUM 3 - -/* Values of `d_un.d_val' in the DT_FLAGS entry. */ -#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ -#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ -#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ -#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ -#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ - -/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 - entry in the dynamic section. */ -#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ -#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ -#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ -#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ -#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ -#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ -#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ -#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ -#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ -#define DF_1_TRANS 0x00000200 -#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ -#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ -#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ -#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ -#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ -#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ -#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ -#define DF_1_NODIRECT 0x00020000 /* Object has no-direct binding. */ -#define DF_1_IGNMULDEF 0x00040000 -#define DF_1_NOKSYMS 0x00080000 -#define DF_1_NOHDR 0x00100000 -#define DF_1_EDITED 0x00200000 /* Object is modified after built. */ -#define DF_1_NORELOC 0x00400000 -#define DF_1_SYMINTPOSE 0x00800000 /* Object has individual interposers. */ -#define DF_1_GLOBAUDIT 0x01000000 /* Global auditing required. */ -#define DF_1_SINGLETON 0x02000000 /* Singleton symbols are used. */ -#define DF_1_STUB 0x04000000 -#define DF_1_PIE 0x08000000 -#define DF_1_KMOD 0x10000000 -#define DF_1_WEAKFILTER 0x20000000 -#define DF_1_NOCOMMON 0x40000000 - -/* Flags for the feature selection in DT_FEATURE_1. */ -#define DTF_1_PARINIT 0x00000001 -#define DTF_1_CONFEXP 0x00000002 - -/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ -#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ -#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not - generally available. */ - -/* Version definition sections. */ - -typedef struct -{ - Elf32_Half vd_version; /* Version revision */ - Elf32_Half vd_flags; /* Version information */ - Elf32_Half vd_ndx; /* Version Index */ - Elf32_Half vd_cnt; /* Number of associated aux entries */ - Elf32_Word vd_hash; /* Version name hash value */ - Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ - Elf32_Word vd_next; /* Offset in bytes to next verdef - entry */ -} Elf32_Verdef; - -typedef struct -{ - Elf64_Half vd_version; /* Version revision */ - Elf64_Half vd_flags; /* Version information */ - Elf64_Half vd_ndx; /* Version Index */ - Elf64_Half vd_cnt; /* Number of associated aux entries */ - Elf64_Word vd_hash; /* Version name hash value */ - Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ - Elf64_Word vd_next; /* Offset in bytes to next verdef - entry */ -} Elf64_Verdef; - - -/* Legal values for vd_version (version revision). */ -#define VER_DEF_NONE 0 /* No version */ -#define VER_DEF_CURRENT 1 /* Current version */ -#define VER_DEF_NUM 2 /* Given version number */ - -/* Legal values for vd_flags (version information flags). */ -#define VER_FLG_BASE 0x1 /* Version definition of file itself */ -#define VER_FLG_WEAK 0x2 /* Weak version identifier */ - -/* Versym symbol index values. */ -#define VER_NDX_LOCAL 0 /* Symbol is local. */ -#define VER_NDX_GLOBAL 1 /* Symbol is global. */ -#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ -#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ - -/* Auxiliary version information. */ - -typedef struct -{ - Elf32_Word vda_name; /* Version or dependency names */ - Elf32_Word vda_next; /* Offset in bytes to next verdaux - entry */ -} Elf32_Verdaux; - -typedef struct -{ - Elf64_Word vda_name; /* Version or dependency names */ - Elf64_Word vda_next; /* Offset in bytes to next verdaux - entry */ -} Elf64_Verdaux; - - -/* Version dependency section. */ - -typedef struct -{ - Elf32_Half vn_version; /* Version of structure */ - Elf32_Half vn_cnt; /* Number of associated aux entries */ - Elf32_Word vn_file; /* Offset of filename for this - dependency */ - Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ - Elf32_Word vn_next; /* Offset in bytes to next verneed - entry */ -} Elf32_Verneed; - -typedef struct -{ - Elf64_Half vn_version; /* Version of structure */ - Elf64_Half vn_cnt; /* Number of associated aux entries */ - Elf64_Word vn_file; /* Offset of filename for this - dependency */ - Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ - Elf64_Word vn_next; /* Offset in bytes to next verneed - entry */ -} Elf64_Verneed; - - -/* Legal values for vn_version (version revision). */ -#define VER_NEED_NONE 0 /* No version */ -#define VER_NEED_CURRENT 1 /* Current version */ -#define VER_NEED_NUM 2 /* Given version number */ - -/* Auxiliary needed version information. */ - -typedef struct -{ - Elf32_Word vna_hash; /* Hash value of dependency name */ - Elf32_Half vna_flags; /* Dependency specific information */ - Elf32_Half vna_other; /* Unused */ - Elf32_Word vna_name; /* Dependency name string offset */ - Elf32_Word vna_next; /* Offset in bytes to next vernaux - entry */ -} Elf32_Vernaux; - -typedef struct -{ - Elf64_Word vna_hash; /* Hash value of dependency name */ - Elf64_Half vna_flags; /* Dependency specific information */ - Elf64_Half vna_other; /* Unused */ - Elf64_Word vna_name; /* Dependency name string offset */ - Elf64_Word vna_next; /* Offset in bytes to next vernaux - entry */ -} Elf64_Vernaux; - - -/* Legal values for vna_flags. */ -#define VER_FLG_WEAK 0x2 /* Weak version identifier */ - - -/* Auxiliary vector. */ - -/* This vector is normally only used by the program interpreter. The - usual definition in an ABI supplement uses the name auxv_t. The - vector is not usually defined in a standard file, but it - can't hurt. We rename it to avoid conflicts. The sizes of these - types are an arrangement between the exec server and the program - interpreter, so we don't fully specify them here. */ - -typedef struct -{ - uint32_t a_type; /* Entry type */ - union - { - uint32_t a_val; /* Integer value */ - /* We use to have pointer elements added here. We cannot do that, - though, since it does not work when using 32-bit definitions - on 64-bit platforms and vice versa. */ - } a_un; -} Elf32_auxv_t; - -typedef struct -{ - uint64_t a_type; /* Entry type */ - union - { - uint64_t a_val; /* Integer value */ - /* We use to have pointer elements added here. We cannot do that, - though, since it does not work when using 32-bit definitions - on 64-bit platforms and vice versa. */ - } a_un; -} Elf64_auxv_t; - -/* Legal values for a_type (entry type). */ - -#define AT_NULL 0 /* End of vector */ -#define AT_IGNORE 1 /* Entry should be ignored */ -#define AT_EXECFD 2 /* File descriptor of program */ -#define AT_PHDR 3 /* Program headers for program */ -#define AT_PHENT 4 /* Size of program header entry */ -#define AT_PHNUM 5 /* Number of program headers */ -#define AT_PAGESZ 6 /* System page size */ -#define AT_BASE 7 /* Base address of interpreter */ -#define AT_FLAGS 8 /* Flags */ -#define AT_ENTRY 9 /* Entry point of program */ -#define AT_NOTELF 10 /* Program is not ELF */ -#define AT_UID 11 /* Real uid */ -#define AT_EUID 12 /* Effective uid */ -#define AT_GID 13 /* Real gid */ -#define AT_EGID 14 /* Effective gid */ -#define AT_CLKTCK 17 /* Frequency of times() */ - -/* Some more special a_type values describing the hardware. */ -#define AT_PLATFORM 15 /* String identifying platform. */ -#define AT_HWCAP 16 /* Machine-dependent hints about - processor capabilities. */ - -/* This entry gives some information about the FPU initialization - performed by the kernel. */ -#define AT_FPUCW 18 /* Used FPU control word. */ - -/* Cache block sizes. */ -#define AT_DCACHEBSIZE 19 /* Data cache block size. */ -#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ -#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ - -/* A special ignored value for PPC, used by the kernel to control the - interpretation of the AUXV. Must be > 16. */ -#define AT_IGNOREPPC 22 /* Entry should be ignored. */ - -#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ - -#define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/ - -#define AT_RANDOM 25 /* Address of 16 random bytes. */ - -#define AT_HWCAP2 26 /* More machine-dependent hints about - processor capabilities. */ - -#define AT_EXECFN 31 /* Filename of executable. */ - -/* Pointer to the global system page used for system calls and other - nice things. */ -#define AT_SYSINFO 32 -#define AT_SYSINFO_EHDR 33 - -/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains - log2 of line size; mask those to get cache size. */ -#define AT_L1I_CACHESHAPE 34 -#define AT_L1D_CACHESHAPE 35 -#define AT_L2_CACHESHAPE 36 -#define AT_L3_CACHESHAPE 37 - -/* Shapes of the caches, with more room to describe them. - *GEOMETRY are comprised of cache line size in bytes in the bottom 16 bits - and the cache associativity in the next 16 bits. */ -#define AT_L1I_CACHESIZE 40 -#define AT_L1I_CACHEGEOMETRY 41 -#define AT_L1D_CACHESIZE 42 -#define AT_L1D_CACHEGEOMETRY 43 -#define AT_L2_CACHESIZE 44 -#define AT_L2_CACHEGEOMETRY 45 -#define AT_L3_CACHESIZE 46 -#define AT_L3_CACHEGEOMETRY 47 - -#define AT_MINSIGSTKSZ 51 /* Stack needed for signal delivery - (AArch64). */ - -/* Note section contents. Each entry in the note section begins with - a header of a fixed form. */ - -typedef struct -{ - Elf32_Word n_namesz; /* Length of the note's name. */ - Elf32_Word n_descsz; /* Length of the note's descriptor. */ - Elf32_Word n_type; /* Type of the note. */ -} Elf32_Nhdr; - -typedef struct -{ - Elf64_Word n_namesz; /* Length of the note's name. */ - Elf64_Word n_descsz; /* Length of the note's descriptor. */ - Elf64_Word n_type; /* Type of the note. */ -} Elf64_Nhdr; - -/* Known names of notes. */ - -/* Solaris entries in the note section have this name. */ -#define ELF_NOTE_SOLARIS "SUNW Solaris" - -/* Note entries for GNU systems have this name. */ -#define ELF_NOTE_GNU "GNU" - - -/* Defined types of notes for Solaris. */ - -/* Value of descriptor (one word) is desired pagesize for the binary. */ -#define ELF_NOTE_PAGESIZE_HINT 1 - - -/* Defined note types for GNU systems. */ - -/* ABI information. The descriptor consists of words: - word 0: OS descriptor - word 1: major version of the ABI - word 2: minor version of the ABI - word 3: subminor version of the ABI -*/ -#define NT_GNU_ABI_TAG 1 -#define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name. */ - -/* Known OSes. These values can appear in word 0 of an - NT_GNU_ABI_TAG note section entry. */ -#define ELF_NOTE_OS_LINUX 0 -#define ELF_NOTE_OS_GNU 1 -#define ELF_NOTE_OS_SOLARIS2 2 -#define ELF_NOTE_OS_FREEBSD 3 - -/* Synthetic hwcap information. The descriptor begins with two words: - word 0: number of entries - word 1: bitmask of enabled entries - Then follow variable-length entries, one byte followed by a - '\0'-terminated hwcap name string. The byte gives the bit - number to test if enabled, (1U << bit) & bitmask. */ -#define NT_GNU_HWCAP 2 - -/* Build ID bits as generated by ld --build-id. - The descriptor consists of any nonzero number of bytes. */ -#define NT_GNU_BUILD_ID 3 - -/* Version note generated by GNU gold containing a version string. */ -#define NT_GNU_GOLD_VERSION 4 - -/* Program property. */ -#define NT_GNU_PROPERTY_TYPE_0 5 - -/* Note section name of program property. */ -#define NOTE_GNU_PROPERTY_SECTION_NAME ".note.gnu.property" - -/* Values used in GNU .note.gnu.property notes (NT_GNU_PROPERTY_TYPE_0). */ - -/* Stack size. */ -#define GNU_PROPERTY_STACK_SIZE 1 -/* No copy relocation on protected data symbol. */ -#define GNU_PROPERTY_NO_COPY_ON_PROTECTED 2 - -/* Processor-specific semantics, lo */ -#define GNU_PROPERTY_LOPROC 0xc0000000 -/* Processor-specific semantics, hi */ -#define GNU_PROPERTY_HIPROC 0xdfffffff -/* Application-specific semantics, lo */ -#define GNU_PROPERTY_LOUSER 0xe0000000 -/* Application-specific semantics, hi */ -#define GNU_PROPERTY_HIUSER 0xffffffff - -/* AArch64 specific GNU properties. */ -#define GNU_PROPERTY_AARCH64_FEATURE_1_AND 0xc0000000 - -#define GNU_PROPERTY_AARCH64_FEATURE_1_BTI (1U << 0) -#define GNU_PROPERTY_AARCH64_FEATURE_1_PAC (1U << 1) - -/* The x86 instruction sets indicated by the corresponding bits are - used in program. Their support in the hardware is optional. */ -#define GNU_PROPERTY_X86_ISA_1_USED 0xc0010002 -/* The x86 instruction sets indicated by the corresponding bits are - used in program and they must be supported by the hardware. */ -#define GNU_PROPERTY_X86_ISA_1_NEEDED 0xc0008002 -/* X86 processor-specific features used in program. */ -#define GNU_PROPERTY_X86_FEATURE_1_AND 0xc0000002 - -/* GNU_PROPERTY_X86_ISA_1_BASELINE: CMOV, CX8 (cmpxchg8b), FPU (fld), - MMX, OSFXSR (fxsave), SCE (syscall), SSE and SSE2. */ -#define GNU_PROPERTY_X86_ISA_1_BASELINE (1U << 0) -/* GNU_PROPERTY_X86_ISA_1_V2: GNU_PROPERTY_X86_ISA_1_BASELINE, - CMPXCHG16B (cmpxchg16b), LAHF-SAHF (lahf), POPCNT (popcnt), SSE3, - SSSE3, SSE4.1 and SSE4.2. */ -#define GNU_PROPERTY_X86_ISA_1_V2 (1U << 1) -/* GNU_PROPERTY_X86_ISA_1_V3: GNU_PROPERTY_X86_ISA_1_V2, AVX, AVX2, BMI1, - BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE. */ -#define GNU_PROPERTY_X86_ISA_1_V3 (1U << 2) -/* GNU_PROPERTY_X86_ISA_1_V4: GNU_PROPERTY_X86_ISA_1_V3, AVX512F, - AVX512BW, AVX512CD, AVX512DQ and AVX512VL. */ -#define GNU_PROPERTY_X86_ISA_1_V4 (1U << 3) - -/* This indicates that all executable sections are compatible with - IBT. */ -#define GNU_PROPERTY_X86_FEATURE_1_IBT (1U << 0) -/* This indicates that all executable sections are compatible with - SHSTK. */ -#define GNU_PROPERTY_X86_FEATURE_1_SHSTK (1U << 1) - -/* Move records. */ -typedef struct -{ - Elf32_Xword m_value; /* Symbol value. */ - Elf32_Word m_info; /* Size and index. */ - Elf32_Word m_poffset; /* Symbol offset. */ - Elf32_Half m_repeat; /* Repeat count. */ - Elf32_Half m_stride; /* Stride info. */ -} Elf32_Move; - -typedef struct -{ - Elf64_Xword m_value; /* Symbol value. */ - Elf64_Xword m_info; /* Size and index. */ - Elf64_Xword m_poffset; /* Symbol offset. */ - Elf64_Half m_repeat; /* Repeat count. */ - Elf64_Half m_stride; /* Stride info. */ -} Elf64_Move; - -/* Macro to construct move records. */ -#define ELF32_M_SYM(info) ((info) >> 8) -#define ELF32_M_SIZE(info) ((unsigned char) (info)) -#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) - -#define ELF64_M_SYM(info) ELF32_M_SYM (info) -#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) -#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) - - -/* Motorola 68k specific definitions. */ - -/* Values for Elf32_Ehdr.e_flags. */ -#define EF_CPU32 0x00810000 - -/* m68k relocs. */ - -#define R_68K_NONE 0 /* No reloc */ -#define R_68K_32 1 /* Direct 32 bit */ -#define R_68K_16 2 /* Direct 16 bit */ -#define R_68K_8 3 /* Direct 8 bit */ -#define R_68K_PC32 4 /* PC relative 32 bit */ -#define R_68K_PC16 5 /* PC relative 16 bit */ -#define R_68K_PC8 6 /* PC relative 8 bit */ -#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ -#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ -#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ -#define R_68K_GOT32O 10 /* 32 bit GOT offset */ -#define R_68K_GOT16O 11 /* 16 bit GOT offset */ -#define R_68K_GOT8O 12 /* 8 bit GOT offset */ -#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ -#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ -#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ -#define R_68K_PLT32O 16 /* 32 bit PLT offset */ -#define R_68K_PLT16O 17 /* 16 bit PLT offset */ -#define R_68K_PLT8O 18 /* 8 bit PLT offset */ -#define R_68K_COPY 19 /* Copy symbol at runtime */ -#define R_68K_GLOB_DAT 20 /* Create GOT entry */ -#define R_68K_JMP_SLOT 21 /* Create PLT entry */ -#define R_68K_RELATIVE 22 /* Adjust by program base */ -#define R_68K_TLS_GD32 25 /* 32 bit GOT offset for GD */ -#define R_68K_TLS_GD16 26 /* 16 bit GOT offset for GD */ -#define R_68K_TLS_GD8 27 /* 8 bit GOT offset for GD */ -#define R_68K_TLS_LDM32 28 /* 32 bit GOT offset for LDM */ -#define R_68K_TLS_LDM16 29 /* 16 bit GOT offset for LDM */ -#define R_68K_TLS_LDM8 30 /* 8 bit GOT offset for LDM */ -#define R_68K_TLS_LDO32 31 /* 32 bit module-relative offset */ -#define R_68K_TLS_LDO16 32 /* 16 bit module-relative offset */ -#define R_68K_TLS_LDO8 33 /* 8 bit module-relative offset */ -#define R_68K_TLS_IE32 34 /* 32 bit GOT offset for IE */ -#define R_68K_TLS_IE16 35 /* 16 bit GOT offset for IE */ -#define R_68K_TLS_IE8 36 /* 8 bit GOT offset for IE */ -#define R_68K_TLS_LE32 37 /* 32 bit offset relative to - static TLS block */ -#define R_68K_TLS_LE16 38 /* 16 bit offset relative to - static TLS block */ -#define R_68K_TLS_LE8 39 /* 8 bit offset relative to - static TLS block */ -#define R_68K_TLS_DTPMOD32 40 /* 32 bit module number */ -#define R_68K_TLS_DTPREL32 41 /* 32 bit module-relative offset */ -#define R_68K_TLS_TPREL32 42 /* 32 bit TP-relative offset */ -/* Keep this the last entry. */ -#define R_68K_NUM 43 - -/* Intel 80386 specific definitions. */ - -/* i386 relocs. */ - -#define R_386_NONE 0 /* No reloc */ -#define R_386_32 1 /* Direct 32 bit */ -#define R_386_PC32 2 /* PC relative 32 bit */ -#define R_386_GOT32 3 /* 32 bit GOT entry */ -#define R_386_PLT32 4 /* 32 bit PLT address */ -#define R_386_COPY 5 /* Copy symbol at runtime */ -#define R_386_GLOB_DAT 6 /* Create GOT entry */ -#define R_386_JMP_SLOT 7 /* Create PLT entry */ -#define R_386_RELATIVE 8 /* Adjust by program base */ -#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ -#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ -#define R_386_32PLT 11 -#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ -#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS - block offset */ -#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block - offset */ -#define R_386_TLS_LE 17 /* Offset relative to static TLS - block */ -#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of - general dynamic thread local data */ -#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of - local dynamic thread local data - in LE code */ -#define R_386_16 20 -#define R_386_PC16 21 -#define R_386_8 22 -#define R_386_PC8 23 -#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic - thread local data */ -#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ -#define R_386_TLS_GD_CALL 26 /* Relocation for call to - __tls_get_addr() */ -#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ -#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic - thread local data in LE code */ -#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ -#define R_386_TLS_LDM_CALL 30 /* Relocation for call to - __tls_get_addr() in LDM code */ -#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ -#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ -#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS - block offset */ -#define R_386_TLS_LE_32 34 /* Negated offset relative to static - TLS block */ -#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ -#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ -#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ -#define R_386_SIZE32 38 /* 32-bit symbol size */ -#define R_386_TLS_GOTDESC 39 /* GOT offset for TLS descriptor. */ -#define R_386_TLS_DESC_CALL 40 /* Marker of call through TLS - descriptor for - relaxation. */ -#define R_386_TLS_DESC 41 /* TLS descriptor containing - pointer to code and to - argument, returning the TLS - offset for the symbol. */ -#define R_386_IRELATIVE 42 /* Adjust indirectly by program base */ -#define R_386_GOT32X 43 /* Load from 32 bit GOT entry, - relaxable. */ -/* Keep this the last entry. */ -#define R_386_NUM 44 - -/* SUN SPARC specific definitions. */ - -/* Legal values for ST_TYPE subfield of st_info (symbol type). */ - -#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */ - -/* Values for Elf64_Ehdr.e_flags. */ - -#define EF_SPARCV9_MM 3 -#define EF_SPARCV9_TSO 0 -#define EF_SPARCV9_PSO 1 -#define EF_SPARCV9_RMO 2 -#define EF_SPARC_LEDATA 0x800000 /* little endian data */ -#define EF_SPARC_EXT_MASK 0xFFFF00 -#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ -#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ -#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ -#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ - -/* SPARC relocs. */ - -#define R_SPARC_NONE 0 /* No reloc */ -#define R_SPARC_8 1 /* Direct 8 bit */ -#define R_SPARC_16 2 /* Direct 16 bit */ -#define R_SPARC_32 3 /* Direct 32 bit */ -#define R_SPARC_DISP8 4 /* PC relative 8 bit */ -#define R_SPARC_DISP16 5 /* PC relative 16 bit */ -#define R_SPARC_DISP32 6 /* PC relative 32 bit */ -#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ -#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ -#define R_SPARC_HI22 9 /* High 22 bit */ -#define R_SPARC_22 10 /* Direct 22 bit */ -#define R_SPARC_13 11 /* Direct 13 bit */ -#define R_SPARC_LO10 12 /* Truncated 10 bit */ -#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ -#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ -#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ -#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ -#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ -#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ -#define R_SPARC_COPY 19 /* Copy symbol at runtime */ -#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ -#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ -#define R_SPARC_RELATIVE 22 /* Adjust by program base */ -#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ - -/* Additional Sparc64 relocs. */ - -#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ -#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ -#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ -#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ -#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ -#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ -#define R_SPARC_10 30 /* Direct 10 bit */ -#define R_SPARC_11 31 /* Direct 11 bit */ -#define R_SPARC_64 32 /* Direct 64 bit */ -#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ -#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ -#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ -#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ -#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ -#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ -#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ -#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ -#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ -#define R_SPARC_GLOB_JMP 42 /* was part of v9 ABI but was removed */ -#define R_SPARC_7 43 /* Direct 7 bit */ -#define R_SPARC_5 44 /* Direct 5 bit */ -#define R_SPARC_6 45 /* Direct 6 bit */ -#define R_SPARC_DISP64 46 /* PC relative 64 bit */ -#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ -#define R_SPARC_HIX22 48 /* High 22 bit complemented */ -#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ -#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ -#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ -#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ -#define R_SPARC_REGISTER 53 /* Global register usage */ -#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ -#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ -#define R_SPARC_TLS_GD_HI22 56 -#define R_SPARC_TLS_GD_LO10 57 -#define R_SPARC_TLS_GD_ADD 58 -#define R_SPARC_TLS_GD_CALL 59 -#define R_SPARC_TLS_LDM_HI22 60 -#define R_SPARC_TLS_LDM_LO10 61 -#define R_SPARC_TLS_LDM_ADD 62 -#define R_SPARC_TLS_LDM_CALL 63 -#define R_SPARC_TLS_LDO_HIX22 64 -#define R_SPARC_TLS_LDO_LOX10 65 -#define R_SPARC_TLS_LDO_ADD 66 -#define R_SPARC_TLS_IE_HI22 67 -#define R_SPARC_TLS_IE_LO10 68 -#define R_SPARC_TLS_IE_LD 69 -#define R_SPARC_TLS_IE_LDX 70 -#define R_SPARC_TLS_IE_ADD 71 -#define R_SPARC_TLS_LE_HIX22 72 -#define R_SPARC_TLS_LE_LOX10 73 -#define R_SPARC_TLS_DTPMOD32 74 -#define R_SPARC_TLS_DTPMOD64 75 -#define R_SPARC_TLS_DTPOFF32 76 -#define R_SPARC_TLS_DTPOFF64 77 -#define R_SPARC_TLS_TPOFF32 78 -#define R_SPARC_TLS_TPOFF64 79 -#define R_SPARC_GOTDATA_HIX22 80 -#define R_SPARC_GOTDATA_LOX10 81 -#define R_SPARC_GOTDATA_OP_HIX22 82 -#define R_SPARC_GOTDATA_OP_LOX10 83 -#define R_SPARC_GOTDATA_OP 84 -#define R_SPARC_H34 85 -#define R_SPARC_SIZE32 86 -#define R_SPARC_SIZE64 87 -#define R_SPARC_WDISP10 88 -#define R_SPARC_JMP_IREL 248 -#define R_SPARC_IRELATIVE 249 -#define R_SPARC_GNU_VTINHERIT 250 -#define R_SPARC_GNU_VTENTRY 251 -#define R_SPARC_REV32 252 -/* Keep this the last entry. */ -#define R_SPARC_NUM 253 - -/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ - -#define DT_SPARC_REGISTER 0x70000001 -#define DT_SPARC_NUM 2 - -/* MIPS R3000 specific definitions. */ - -/* Legal values for e_flags field of Elf32_Ehdr. */ - -#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used. */ -#define EF_MIPS_PIC 2 /* Contains PIC code. */ -#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence. */ -#define EF_MIPS_XGOT 8 -#define EF_MIPS_64BIT_WHIRL 16 -#define EF_MIPS_ABI2 32 -#define EF_MIPS_ABI_ON32 64 -#define EF_MIPS_FP64 512 /* Uses FP64 (12 callee-saved). */ -#define EF_MIPS_NAN2008 1024 /* Uses IEEE 754-2008 NaN encoding. */ -#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level. */ - -/* Legal values for MIPS architecture level. */ - -#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ -#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ -#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ -#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ -#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ -#define EF_MIPS_ARCH_32 0x50000000 /* MIPS32 code. */ -#define EF_MIPS_ARCH_64 0x60000000 /* MIPS64 code. */ -#define EF_MIPS_ARCH_32R2 0x70000000 /* MIPS32r2 code. */ -#define EF_MIPS_ARCH_64R2 0x80000000 /* MIPS64r2 code. */ - -/* The following are unofficial names and should not be used. */ - -#define E_MIPS_ARCH_1 EF_MIPS_ARCH_1 -#define E_MIPS_ARCH_2 EF_MIPS_ARCH_2 -#define E_MIPS_ARCH_3 EF_MIPS_ARCH_3 -#define E_MIPS_ARCH_4 EF_MIPS_ARCH_4 -#define E_MIPS_ARCH_5 EF_MIPS_ARCH_5 -#define E_MIPS_ARCH_32 EF_MIPS_ARCH_32 -#define E_MIPS_ARCH_64 EF_MIPS_ARCH_64 - -/* Special section indices. */ - -#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols. */ -#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ -#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ -#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols. */ -#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols. */ - -/* Legal values for sh_type field of Elf32_Shdr. */ - -#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link. */ -#define SHT_MIPS_MSYM 0x70000001 -#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols. */ -#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes. */ -#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ -#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging info. */ -#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information. */ -#define SHT_MIPS_PACKAGE 0x70000007 -#define SHT_MIPS_PACKSYM 0x70000008 -#define SHT_MIPS_RELD 0x70000009 -#define SHT_MIPS_IFACE 0x7000000b -#define SHT_MIPS_CONTENT 0x7000000c -#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ -#define SHT_MIPS_SHDR 0x70000010 -#define SHT_MIPS_FDESC 0x70000011 -#define SHT_MIPS_EXTSYM 0x70000012 -#define SHT_MIPS_DENSE 0x70000013 -#define SHT_MIPS_PDESC 0x70000014 -#define SHT_MIPS_LOCSYM 0x70000015 -#define SHT_MIPS_AUXSYM 0x70000016 -#define SHT_MIPS_OPTSYM 0x70000017 -#define SHT_MIPS_LOCSTR 0x70000018 -#define SHT_MIPS_LINE 0x70000019 -#define SHT_MIPS_RFDESC 0x7000001a -#define SHT_MIPS_DELTASYM 0x7000001b -#define SHT_MIPS_DELTAINST 0x7000001c -#define SHT_MIPS_DELTACLASS 0x7000001d -#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ -#define SHT_MIPS_DELTADECL 0x7000001f -#define SHT_MIPS_SYMBOL_LIB 0x70000020 -#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ -#define SHT_MIPS_TRANSLATE 0x70000022 -#define SHT_MIPS_PIXIE 0x70000023 -#define SHT_MIPS_XLATE 0x70000024 -#define SHT_MIPS_XLATE_DEBUG 0x70000025 -#define SHT_MIPS_WHIRL 0x70000026 -#define SHT_MIPS_EH_REGION 0x70000027 -#define SHT_MIPS_XLATE_OLD 0x70000028 -#define SHT_MIPS_PDR_EXCEPTION 0x70000029 -#define SHT_MIPS_XHASH 0x7000002b - -/* Legal values for sh_flags field of Elf32_Shdr. */ - -#define SHF_MIPS_GPREL 0x10000000 /* Must be in global data area. */ -#define SHF_MIPS_MERGE 0x20000000 -#define SHF_MIPS_ADDR 0x40000000 -#define SHF_MIPS_STRINGS 0x80000000 -#define SHF_MIPS_NOSTRIP 0x08000000 -#define SHF_MIPS_LOCAL 0x04000000 -#define SHF_MIPS_NAMES 0x02000000 -#define SHF_MIPS_NODUPE 0x01000000 - - -/* Symbol tables. */ - -/* MIPS specific values for `st_other'. */ -#define STO_MIPS_DEFAULT 0x0 -#define STO_MIPS_INTERNAL 0x1 -#define STO_MIPS_HIDDEN 0x2 -#define STO_MIPS_PROTECTED 0x3 -#define STO_MIPS_PLT 0x8 -#define STO_MIPS_SC_ALIGN_UNUSED 0xff - -/* MIPS specific values for `st_info'. */ -#define STB_MIPS_SPLIT_COMMON 13 - -/* Entries found in sections of type SHT_MIPS_GPTAB. */ - -typedef union -{ - struct - { - Elf32_Word gt_current_g_value; /* -G value used for compilation. */ - Elf32_Word gt_unused; /* Not used. */ - } gt_header; /* First entry in section. */ - struct - { - Elf32_Word gt_g_value; /* If this value were used for -G. */ - Elf32_Word gt_bytes; /* This many bytes would be used. */ - } gt_entry; /* Subsequent entries in section. */ -} Elf32_gptab; - -/* Entry found in sections of type SHT_MIPS_REGINFO. */ - -typedef struct -{ - Elf32_Word ri_gprmask; /* General registers used. */ - Elf32_Word ri_cprmask[4]; /* Coprocessor registers used. */ - Elf32_Sword ri_gp_value; /* $gp register value. */ -} Elf32_RegInfo; - -/* Entries found in sections of type SHT_MIPS_OPTIONS. */ - -typedef struct -{ - unsigned char kind; /* Determines interpretation of the - variable part of descriptor. */ - unsigned char size; /* Size of descriptor, including header. */ - Elf32_Section section; /* Section header index of section affected, - 0 for global options. */ - Elf32_Word info; /* Kind-specific information. */ -} Elf_Options; - -/* Values for `kind' field in Elf_Options. */ - -#define ODK_NULL 0 /* Undefined. */ -#define ODK_REGINFO 1 /* Register usage information. */ -#define ODK_EXCEPTIONS 2 /* Exception processing options. */ -#define ODK_PAD 3 /* Section padding options. */ -#define ODK_HWPATCH 4 /* Hardware workarounds performed */ -#define ODK_FILL 5 /* record the fill value used by the linker. */ -#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ -#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ -#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ - -/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ - -#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ -#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ -#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ -#define OEX_SMM 0x20000 /* Force sequential memory mode? */ -#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ -#define OEX_PRECISEFP OEX_FPDBUG -#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ - -#define OEX_FPU_INVAL 0x10 -#define OEX_FPU_DIV0 0x08 -#define OEX_FPU_OFLO 0x04 -#define OEX_FPU_UFLO 0x02 -#define OEX_FPU_INEX 0x01 - -/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ - -#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ -#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ -#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ -#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ - -#define OPAD_PREFIX 0x1 -#define OPAD_POSTFIX 0x2 -#define OPAD_SYMBOL 0x4 - -/* Entry found in `.options' section. */ - -typedef struct -{ - Elf32_Word hwp_flags1; /* Extra flags. */ - Elf32_Word hwp_flags2; /* Extra flags. */ -} Elf_Options_Hw; - -/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ - -#define OHWA0_R4KEOP_CHECKED 0x00000001 -#define OHWA1_R4KEOP_CLEAN 0x00000002 - -/* MIPS relocs. */ - -#define R_MIPS_NONE 0 /* No reloc */ -#define R_MIPS_16 1 /* Direct 16 bit */ -#define R_MIPS_32 2 /* Direct 32 bit */ -#define R_MIPS_REL32 3 /* PC relative 32 bit */ -#define R_MIPS_26 4 /* Direct 26 bit shifted */ -#define R_MIPS_HI16 5 /* High 16 bit */ -#define R_MIPS_LO16 6 /* Low 16 bit */ -#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ -#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ -#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ -#define R_MIPS_PC16 10 /* PC relative 16 bit */ -#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ -#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ - -#define R_MIPS_SHIFT5 16 -#define R_MIPS_SHIFT6 17 -#define R_MIPS_64 18 -#define R_MIPS_GOT_DISP 19 -#define R_MIPS_GOT_PAGE 20 -#define R_MIPS_GOT_OFST 21 -#define R_MIPS_GOT_HI16 22 -#define R_MIPS_GOT_LO16 23 -#define R_MIPS_SUB 24 -#define R_MIPS_INSERT_A 25 -#define R_MIPS_INSERT_B 26 -#define R_MIPS_DELETE 27 -#define R_MIPS_HIGHER 28 -#define R_MIPS_HIGHEST 29 -#define R_MIPS_CALL_HI16 30 -#define R_MIPS_CALL_LO16 31 -#define R_MIPS_SCN_DISP 32 -#define R_MIPS_REL16 33 -#define R_MIPS_ADD_IMMEDIATE 34 -#define R_MIPS_PJUMP 35 -#define R_MIPS_RELGOT 36 -#define R_MIPS_JALR 37 -#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */ -#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */ -#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */ -#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */ -#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */ -#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */ -#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */ -#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */ -#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */ -#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */ -#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */ -#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */ -#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */ -#define R_MIPS_GLOB_DAT 51 -#define R_MIPS_COPY 126 -#define R_MIPS_JUMP_SLOT 127 -/* Keep this the last entry. */ -#define R_MIPS_NUM 128 - -/* Legal values for p_type field of Elf32_Phdr. */ - -#define PT_MIPS_REGINFO 0x70000000 /* Register usage information. */ -#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ -#define PT_MIPS_OPTIONS 0x70000002 -#define PT_MIPS_ABIFLAGS 0x70000003 /* FP mode requirement. */ - -/* Special program header types. */ - -#define PF_MIPS_LOCAL 0x10000000 - -/* Legal values for d_tag field of Elf32_Dyn. */ - -#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ -#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ -#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ -#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ -#define DT_MIPS_FLAGS 0x70000005 /* Flags */ -#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ -#define DT_MIPS_MSYM 0x70000007 -#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ -#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ -#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ -#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ -#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ -#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ -#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ -#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ -#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ -#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ -#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ -#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in - DT_MIPS_DELTA_CLASS. */ -#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ -#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in - DT_MIPS_DELTA_INSTANCE. */ -#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ -#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in - DT_MIPS_DELTA_RELOC. */ -#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta - relocations refer to. */ -#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in - DT_MIPS_DELTA_SYM. */ -#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the - class declaration. */ -#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in - DT_MIPS_DELTA_CLASSSYM. */ -#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ -#define DT_MIPS_PIXIE_INIT 0x70000023 -#define DT_MIPS_SYMBOL_LIB 0x70000024 -#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 -#define DT_MIPS_LOCAL_GOTIDX 0x70000026 -#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 -#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 -#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ -#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ -#define DT_MIPS_DYNSTR_ALIGN 0x7000002b -#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ -#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve - function stored in GOT. */ -#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added - by rld on dlopen() calls. */ -#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ -#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ -#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ -/* The address of .got.plt in an executable using the new non-PIC ABI. */ -#define DT_MIPS_PLTGOT 0x70000032 -/* The base of the PLT in an executable using the new non-PIC ABI if that - PLT is writable. For a non-writable PLT, this is omitted or has a zero - value. */ -#define DT_MIPS_RWPLT 0x70000034 -/* An alternative description of the classic MIPS RLD_MAP that is usable - in a PIE as it stores a relative offset from the address of the tag - rather than an absolute address. */ -#define DT_MIPS_RLD_MAP_REL 0x70000035 -/* GNU-style hash table with xlat. */ -#define DT_MIPS_XHASH 0x70000036 -#define DT_MIPS_NUM 0x37 - -/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ - -#define RHF_NONE 0 /* No flags */ -#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ -#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ -#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ -#define RHF_NO_MOVE (1 << 3) -#define RHF_SGI_ONLY (1 << 4) -#define RHF_GUARANTEE_INIT (1 << 5) -#define RHF_DELTA_C_PLUS_PLUS (1 << 6) -#define RHF_GUARANTEE_START_INIT (1 << 7) -#define RHF_PIXIE (1 << 8) -#define RHF_DEFAULT_DELAY_LOAD (1 << 9) -#define RHF_REQUICKSTART (1 << 10) -#define RHF_REQUICKSTARTED (1 << 11) -#define RHF_CORD (1 << 12) -#define RHF_NO_UNRES_UNDEF (1 << 13) -#define RHF_RLD_ORDER_SAFE (1 << 14) - -/* Entries found in sections of type SHT_MIPS_LIBLIST. */ - -typedef struct -{ - Elf32_Word l_name; /* Name (string table index) */ - Elf32_Word l_time_stamp; /* Timestamp */ - Elf32_Word l_checksum; /* Checksum */ - Elf32_Word l_version; /* Interface version */ - Elf32_Word l_flags; /* Flags */ -} Elf32_Lib; - -typedef struct -{ - Elf64_Word l_name; /* Name (string table index) */ - Elf64_Word l_time_stamp; /* Timestamp */ - Elf64_Word l_checksum; /* Checksum */ - Elf64_Word l_version; /* Interface version */ - Elf64_Word l_flags; /* Flags */ -} Elf64_Lib; - - -/* Legal values for l_flags. */ - -#define LL_NONE 0 -#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ -#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ -#define LL_REQUIRE_MINOR (1 << 2) -#define LL_EXPORTS (1 << 3) -#define LL_DELAY_LOAD (1 << 4) -#define LL_DELTA (1 << 5) - -/* Entries found in sections of type SHT_MIPS_CONFLICT. */ - -typedef Elf32_Addr Elf32_Conflict; - -typedef struct -{ - /* Version of flags structure. */ - Elf32_Half version; - /* The level of the ISA: 1-5, 32, 64. */ - unsigned char isa_level; - /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */ - unsigned char isa_rev; - /* The size of general purpose registers. */ - unsigned char gpr_size; - /* The size of co-processor 1 registers. */ - unsigned char cpr1_size; - /* The size of co-processor 2 registers. */ - unsigned char cpr2_size; - /* The floating-point ABI. */ - unsigned char fp_abi; - /* Processor-specific extension. */ - Elf32_Word isa_ext; - /* Mask of ASEs used. */ - Elf32_Word ases; - /* Mask of general flags. */ - Elf32_Word flags1; - Elf32_Word flags2; -} Elf_MIPS_ABIFlags_v0; - -/* Values for the register size bytes of an abi flags structure. */ - -#define MIPS_AFL_REG_NONE 0x00 /* No registers. */ -#define MIPS_AFL_REG_32 0x01 /* 32-bit registers. */ -#define MIPS_AFL_REG_64 0x02 /* 64-bit registers. */ -#define MIPS_AFL_REG_128 0x03 /* 128-bit registers. */ - -/* Masks for the ases word of an ABI flags structure. */ - -#define MIPS_AFL_ASE_DSP 0x00000001 /* DSP ASE. */ -#define MIPS_AFL_ASE_DSPR2 0x00000002 /* DSP R2 ASE. */ -#define MIPS_AFL_ASE_EVA 0x00000004 /* Enhanced VA Scheme. */ -#define MIPS_AFL_ASE_MCU 0x00000008 /* MCU (MicroController) ASE. */ -#define MIPS_AFL_ASE_MDMX 0x00000010 /* MDMX ASE. */ -#define MIPS_AFL_ASE_MIPS3D 0x00000020 /* MIPS-3D ASE. */ -#define MIPS_AFL_ASE_MT 0x00000040 /* MT ASE. */ -#define MIPS_AFL_ASE_SMARTMIPS 0x00000080 /* SmartMIPS ASE. */ -#define MIPS_AFL_ASE_VIRT 0x00000100 /* VZ ASE. */ -#define MIPS_AFL_ASE_MSA 0x00000200 /* MSA ASE. */ -#define MIPS_AFL_ASE_MIPS16 0x00000400 /* MIPS16 ASE. */ -#define MIPS_AFL_ASE_MICROMIPS 0x00000800 /* MICROMIPS ASE. */ -#define MIPS_AFL_ASE_XPA 0x00001000 /* XPA ASE. */ -#define MIPS_AFL_ASE_MASK 0x00001fff /* All ASEs. */ - -/* Values for the isa_ext word of an ABI flags structure. */ - -#define MIPS_AFL_EXT_XLR 1 /* RMI Xlr instruction. */ -#define MIPS_AFL_EXT_OCTEON2 2 /* Cavium Networks Octeon2. */ -#define MIPS_AFL_EXT_OCTEONP 3 /* Cavium Networks OcteonP. */ -#define MIPS_AFL_EXT_LOONGSON_3A 4 /* Loongson 3A. */ -#define MIPS_AFL_EXT_OCTEON 5 /* Cavium Networks Octeon. */ -#define MIPS_AFL_EXT_5900 6 /* MIPS R5900 instruction. */ -#define MIPS_AFL_EXT_4650 7 /* MIPS R4650 instruction. */ -#define MIPS_AFL_EXT_4010 8 /* LSI R4010 instruction. */ -#define MIPS_AFL_EXT_4100 9 /* NEC VR4100 instruction. */ -#define MIPS_AFL_EXT_3900 10 /* Toshiba R3900 instruction. */ -#define MIPS_AFL_EXT_10000 11 /* MIPS R10000 instruction. */ -#define MIPS_AFL_EXT_SB1 12 /* Broadcom SB-1 instruction. */ -#define MIPS_AFL_EXT_4111 13 /* NEC VR4111/VR4181 instruction. */ -#define MIPS_AFL_EXT_4120 14 /* NEC VR4120 instruction. */ -#define MIPS_AFL_EXT_5400 15 /* NEC VR5400 instruction. */ -#define MIPS_AFL_EXT_5500 16 /* NEC VR5500 instruction. */ -#define MIPS_AFL_EXT_LOONGSON_2E 17 /* ST Microelectronics Loongson 2E. */ -#define MIPS_AFL_EXT_LOONGSON_2F 18 /* ST Microelectronics Loongson 2F. */ - -/* Masks for the flags1 word of an ABI flags structure. */ -#define MIPS_AFL_FLAGS1_ODDSPREG 1 /* Uses odd single-precision registers. */ - -/* Object attribute values. */ -enum -{ - /* Not tagged or not using any ABIs affected by the differences. */ - Val_GNU_MIPS_ABI_FP_ANY = 0, - /* Using hard-float -mdouble-float. */ - Val_GNU_MIPS_ABI_FP_DOUBLE = 1, - /* Using hard-float -msingle-float. */ - Val_GNU_MIPS_ABI_FP_SINGLE = 2, - /* Using soft-float. */ - Val_GNU_MIPS_ABI_FP_SOFT = 3, - /* Using -mips32r2 -mfp64. */ - Val_GNU_MIPS_ABI_FP_OLD_64 = 4, - /* Using -mfpxx. */ - Val_GNU_MIPS_ABI_FP_XX = 5, - /* Using -mips32r2 -mfp64. */ - Val_GNU_MIPS_ABI_FP_64 = 6, - /* Using -mips32r2 -mfp64 -mno-odd-spreg. */ - Val_GNU_MIPS_ABI_FP_64A = 7, - /* Maximum allocated FP ABI value. */ - Val_GNU_MIPS_ABI_FP_MAX = 7 -}; - -/* HPPA specific definitions. */ - -/* Legal values for e_flags field of Elf32_Ehdr. */ - -#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ -#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ -#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ -#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ -#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch - prediction. */ -#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ -#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ - -/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ - -#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ -#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ -#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ - -/* Additional section indices. */ - -#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tentatively declared - symbols in ANSI C. */ -#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ - -/* Legal values for sh_type field of Elf32_Shdr. */ - -#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ -#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ -#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ - -/* Legal values for sh_flags field of Elf32_Shdr. */ - -#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ -#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ -#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ - -/* Legal values for ST_TYPE subfield of st_info (symbol type). */ - -#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ - -#define STT_HP_OPAQUE (STT_LOOS + 0x1) -#define STT_HP_STUB (STT_LOOS + 0x2) - -/* HPPA relocs. */ - -#define R_PARISC_NONE 0 /* No reloc. */ -#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ -#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ -#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ -#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ -#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ -#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ -#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ -#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ -#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ -#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ -#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ -#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ -#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ -#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ -#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ -#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ -#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ -#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ -#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ -#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ -#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ -#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ -#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ -#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ -#define R_PARISC_FPTR64 64 /* 64 bits function address. */ -#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ -#define R_PARISC_PLABEL21L 66 /* Left 21 bits of fdesc address. */ -#define R_PARISC_PLABEL14R 70 /* Right 14 bits of fdesc address. */ -#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ -#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ -#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ -#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ -#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ -#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ -#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ -#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ -#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ -#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ -#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ -#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ -#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ -#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ -#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ -#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ -#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ -#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ -#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ -#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ -#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ -#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ -#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ -#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ -#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ -#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ -#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ -#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ -#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ -#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ -#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ -#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ -#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ -#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ -#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ -#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ -#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ -#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ -#define R_PARISC_LORESERVE 128 -#define R_PARISC_COPY 128 /* Copy relocation. */ -#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ -#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ -#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ -#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ -#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ -#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ -#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ -#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ -#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ -#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ -#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ -#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ -#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ -#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ -#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ -#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ -#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ -#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ -#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ -#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ -#define R_PARISC_GNU_VTENTRY 232 -#define R_PARISC_GNU_VTINHERIT 233 -#define R_PARISC_TLS_GD21L 234 /* GD 21-bit left. */ -#define R_PARISC_TLS_GD14R 235 /* GD 14-bit right. */ -#define R_PARISC_TLS_GDCALL 236 /* GD call to __t_g_a. */ -#define R_PARISC_TLS_LDM21L 237 /* LD module 21-bit left. */ -#define R_PARISC_TLS_LDM14R 238 /* LD module 14-bit right. */ -#define R_PARISC_TLS_LDMCALL 239 /* LD module call to __t_g_a. */ -#define R_PARISC_TLS_LDO21L 240 /* LD offset 21-bit left. */ -#define R_PARISC_TLS_LDO14R 241 /* LD offset 14-bit right. */ -#define R_PARISC_TLS_DTPMOD32 242 /* DTP module 32-bit. */ -#define R_PARISC_TLS_DTPMOD64 243 /* DTP module 64-bit. */ -#define R_PARISC_TLS_DTPOFF32 244 /* DTP offset 32-bit. */ -#define R_PARISC_TLS_DTPOFF64 245 /* DTP offset 32-bit. */ -#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L -#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R -#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L -#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R -#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 -#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 -#define R_PARISC_HIRESERVE 255 - -/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ - -#define PT_HP_TLS (PT_LOOS + 0x0) -#define PT_HP_CORE_NONE (PT_LOOS + 0x1) -#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) -#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) -#define PT_HP_CORE_COMM (PT_LOOS + 0x4) -#define PT_HP_CORE_PROC (PT_LOOS + 0x5) -#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) -#define PT_HP_CORE_STACK (PT_LOOS + 0x7) -#define PT_HP_CORE_SHM (PT_LOOS + 0x8) -#define PT_HP_CORE_MMF (PT_LOOS + 0x9) -#define PT_HP_PARALLEL (PT_LOOS + 0x10) -#define PT_HP_FASTBIND (PT_LOOS + 0x11) -#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) -#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) -#define PT_HP_STACK (PT_LOOS + 0x14) - -#define PT_PARISC_ARCHEXT 0x70000000 -#define PT_PARISC_UNWIND 0x70000001 - -/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ - -#define PF_PARISC_SBP 0x08000000 - -#define PF_HP_PAGE_SIZE 0x00100000 -#define PF_HP_FAR_SHARED 0x00200000 -#define PF_HP_NEAR_SHARED 0x00400000 -#define PF_HP_CODE 0x01000000 -#define PF_HP_MODIFY 0x02000000 -#define PF_HP_LAZYSWAP 0x04000000 -#define PF_HP_SBP 0x08000000 - - -/* Alpha specific definitions. */ - -/* Legal values for e_flags field of Elf64_Ehdr. */ - -#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ -#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ - -/* Legal values for sh_type field of Elf64_Shdr. */ - -/* These two are primerily concerned with ECOFF debugging info. */ -#define SHT_ALPHA_DEBUG 0x70000001 -#define SHT_ALPHA_REGINFO 0x70000002 - -/* Legal values for sh_flags field of Elf64_Shdr. */ - -#define SHF_ALPHA_GPREL 0x10000000 - -/* Legal values for st_other field of Elf64_Sym. */ -#define STO_ALPHA_NOPV 0x80 /* No PV required. */ -#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ - -/* Alpha relocs. */ - -#define R_ALPHA_NONE 0 /* No reloc */ -#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ -#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ -#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ -#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ -#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ -#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ -#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ -#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ -#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ -#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ -#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ -#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ -#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ -#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ -#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ -#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ -#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ -#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ -#define R_ALPHA_TLS_GD_HI 28 -#define R_ALPHA_TLSGD 29 -#define R_ALPHA_TLS_LDM 30 -#define R_ALPHA_DTPMOD64 31 -#define R_ALPHA_GOTDTPREL 32 -#define R_ALPHA_DTPREL64 33 -#define R_ALPHA_DTPRELHI 34 -#define R_ALPHA_DTPRELLO 35 -#define R_ALPHA_DTPREL16 36 -#define R_ALPHA_GOTTPREL 37 -#define R_ALPHA_TPREL64 38 -#define R_ALPHA_TPRELHI 39 -#define R_ALPHA_TPRELLO 40 -#define R_ALPHA_TPREL16 41 -/* Keep this the last entry. */ -#define R_ALPHA_NUM 46 - -/* Magic values of the LITUSE relocation addend. */ -#define LITUSE_ALPHA_ADDR 0 -#define LITUSE_ALPHA_BASE 1 -#define LITUSE_ALPHA_BYTOFF 2 -#define LITUSE_ALPHA_JSR 3 -#define LITUSE_ALPHA_TLS_GD 4 -#define LITUSE_ALPHA_TLS_LDM 5 - -/* Legal values for d_tag of Elf64_Dyn. */ -#define DT_ALPHA_PLTRO (DT_LOPROC + 0) -#define DT_ALPHA_NUM 1 - -/* PowerPC specific declarations */ - -/* Values for Elf32/64_Ehdr.e_flags. */ -#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ - -/* Cygnus local bits below */ -#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ -#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib - flag */ - -/* PowerPC relocations defined by the ABIs */ -#define R_PPC_NONE 0 -#define R_PPC_ADDR32 1 /* 32bit absolute address */ -#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ -#define R_PPC_ADDR16 3 /* 16bit absolute address */ -#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ -#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ -#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ -#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ -#define R_PPC_ADDR14_BRTAKEN 8 -#define R_PPC_ADDR14_BRNTAKEN 9 -#define R_PPC_REL24 10 /* PC relative 26 bit */ -#define R_PPC_REL14 11 /* PC relative 16 bit */ -#define R_PPC_REL14_BRTAKEN 12 -#define R_PPC_REL14_BRNTAKEN 13 -#define R_PPC_GOT16 14 -#define R_PPC_GOT16_LO 15 -#define R_PPC_GOT16_HI 16 -#define R_PPC_GOT16_HA 17 -#define R_PPC_PLTREL24 18 -#define R_PPC_COPY 19 -#define R_PPC_GLOB_DAT 20 -#define R_PPC_JMP_SLOT 21 -#define R_PPC_RELATIVE 22 -#define R_PPC_LOCAL24PC 23 -#define R_PPC_UADDR32 24 -#define R_PPC_UADDR16 25 -#define R_PPC_REL32 26 -#define R_PPC_PLT32 27 -#define R_PPC_PLTREL32 28 -#define R_PPC_PLT16_LO 29 -#define R_PPC_PLT16_HI 30 -#define R_PPC_PLT16_HA 31 -#define R_PPC_SDAREL16 32 -#define R_PPC_SECTOFF 33 -#define R_PPC_SECTOFF_LO 34 -#define R_PPC_SECTOFF_HI 35 -#define R_PPC_SECTOFF_HA 36 - -/* PowerPC relocations defined for the TLS access ABI. */ -#define R_PPC_TLS 67 /* none (sym+add)@tls */ -#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ -#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ -#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ -#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ -#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ -#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ -#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ -#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ -#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ -#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ -#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ -#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ -#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ -#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ -#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ -#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ -#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ -#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ -#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ -#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ -#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ -#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ -#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ -#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ -#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ -#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ -#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ -#define R_PPC_TLSGD 95 /* none (sym+add)@tlsgd */ -#define R_PPC_TLSLD 96 /* none (sym+add)@tlsld */ - -/* The remaining relocs are from the Embedded ELF ABI, and are not - in the SVR4 ELF ABI. */ -#define R_PPC_EMB_NADDR32 101 -#define R_PPC_EMB_NADDR16 102 -#define R_PPC_EMB_NADDR16_LO 103 -#define R_PPC_EMB_NADDR16_HI 104 -#define R_PPC_EMB_NADDR16_HA 105 -#define R_PPC_EMB_SDAI16 106 -#define R_PPC_EMB_SDA2I16 107 -#define R_PPC_EMB_SDA2REL 108 -#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ -#define R_PPC_EMB_MRKREF 110 -#define R_PPC_EMB_RELSEC16 111 -#define R_PPC_EMB_RELST_LO 112 -#define R_PPC_EMB_RELST_HI 113 -#define R_PPC_EMB_RELST_HA 114 -#define R_PPC_EMB_BIT_FLD 115 -#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ - -/* Diab tool relocations. */ -#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ -#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ -#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ -#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ -#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ -#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ - -/* GNU extension to support local ifunc. */ -#define R_PPC_IRELATIVE 248 - -/* GNU relocs used in PIC code sequences. */ -#define R_PPC_REL16 249 /* half16 (sym+add-.) */ -#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */ -#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */ -#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */ - -/* This is a phony reloc to handle any old fashioned TOC16 references - that may still be in object files. */ -#define R_PPC_TOC16 255 - -/* PowerPC specific values for the Dyn d_tag field. */ -#define DT_PPC_GOT (DT_LOPROC + 0) -#define DT_PPC_OPT (DT_LOPROC + 1) -#define DT_PPC_NUM 2 - -/* PowerPC specific values for the DT_PPC_OPT Dyn entry. */ -#define PPC_OPT_TLS 1 - -/* PowerPC64 relocations defined by the ABIs */ -#define R_PPC64_NONE R_PPC_NONE -#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ -#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ -#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ -#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ -#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ -#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ -#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ -#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN -#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN -#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ -#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ -#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN -#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN -#define R_PPC64_GOT16 R_PPC_GOT16 -#define R_PPC64_GOT16_LO R_PPC_GOT16_LO -#define R_PPC64_GOT16_HI R_PPC_GOT16_HI -#define R_PPC64_GOT16_HA R_PPC_GOT16_HA - -#define R_PPC64_COPY R_PPC_COPY -#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT -#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT -#define R_PPC64_RELATIVE R_PPC_RELATIVE - -#define R_PPC64_UADDR32 R_PPC_UADDR32 -#define R_PPC64_UADDR16 R_PPC_UADDR16 -#define R_PPC64_REL32 R_PPC_REL32 -#define R_PPC64_PLT32 R_PPC_PLT32 -#define R_PPC64_PLTREL32 R_PPC_PLTREL32 -#define R_PPC64_PLT16_LO R_PPC_PLT16_LO -#define R_PPC64_PLT16_HI R_PPC_PLT16_HI -#define R_PPC64_PLT16_HA R_PPC_PLT16_HA - -#define R_PPC64_SECTOFF R_PPC_SECTOFF -#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO -#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI -#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA -#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ -#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ -#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ -#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ -#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ -#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ -#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ -#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ -#define R_PPC64_PLT64 45 /* doubleword64 L + A */ -#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ -#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ -#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ -#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ -#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ -#define R_PPC64_TOC 51 /* doubleword64 .TOC */ -#define R_PPC64_PLTGOT16 52 /* half16* M + A */ -#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ -#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ -#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ - -#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ -#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ -#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ -#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ -#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ -#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ -#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ -#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ -#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ -#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ -#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ - -/* PowerPC64 relocations defined for the TLS access ABI. */ -#define R_PPC64_TLS 67 /* none (sym+add)@tls */ -#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ -#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ -#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ -#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ -#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ -#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ -#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ -#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ -#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ -#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ -#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ -#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ -#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ -#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ -#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ -#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ -#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ -#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ -#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ -#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ -#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ -#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ -#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ -#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ -#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ -#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ -#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ -#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ -#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ -#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ -#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ -#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ -#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ -#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ -#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ -#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ -#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ -#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ -#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ -#define R_PPC64_TLSGD 107 /* none (sym+add)@tlsgd */ -#define R_PPC64_TLSLD 108 /* none (sym+add)@tlsld */ -#define R_PPC64_TOCSAVE 109 /* none */ - -/* Added when HA and HI relocs were changed to report overflows. */ -#define R_PPC64_ADDR16_HIGH 110 -#define R_PPC64_ADDR16_HIGHA 111 -#define R_PPC64_TPREL16_HIGH 112 -#define R_PPC64_TPREL16_HIGHA 113 -#define R_PPC64_DTPREL16_HIGH 114 -#define R_PPC64_DTPREL16_HIGHA 115 - -/* GNU extension to support local ifunc. */ -#define R_PPC64_JMP_IREL 247 -#define R_PPC64_IRELATIVE 248 -#define R_PPC64_REL16 249 /* half16 (sym+add-.) */ -#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */ -#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */ -#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */ - -/* e_flags bits specifying ABI. - 1 for original function descriptor using ABI, - 2 for revised ABI without function descriptors, - 0 for unspecified or not using any features affected by the differences. */ -#define EF_PPC64_ABI 3 - -/* PowerPC64 specific values for the Dyn d_tag field. */ -#define DT_PPC64_GLINK (DT_LOPROC + 0) -#define DT_PPC64_OPD (DT_LOPROC + 1) -#define DT_PPC64_OPDSZ (DT_LOPROC + 2) -#define DT_PPC64_OPT (DT_LOPROC + 3) -#define DT_PPC64_NUM 4 - -/* PowerPC64 specific bits in the DT_PPC64_OPT Dyn entry. */ -#define PPC64_OPT_TLS 1 -#define PPC64_OPT_MULTI_TOC 2 -#define PPC64_OPT_LOCALENTRY 4 - -/* PowerPC64 specific values for the Elf64_Sym st_other field. */ -#define STO_PPC64_LOCAL_BIT 5 -#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT) -#define PPC64_LOCAL_ENTRY_OFFSET(other) \ - (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2) - - -/* ARM specific declarations */ - -/* Processor specific flags for the ELF header e_flags field. */ -#define EF_ARM_RELEXEC 0x01 -#define EF_ARM_HASENTRY 0x02 -#define EF_ARM_INTERWORK 0x04 -#define EF_ARM_APCS_26 0x08 -#define EF_ARM_APCS_FLOAT 0x10 -#define EF_ARM_PIC 0x20 -#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ -#define EF_ARM_NEW_ABI 0x80 -#define EF_ARM_OLD_ABI 0x100 -#define EF_ARM_SOFT_FLOAT 0x200 -#define EF_ARM_VFP_FLOAT 0x400 -#define EF_ARM_MAVERICK_FLOAT 0x800 - -#define EF_ARM_ABI_FLOAT_SOFT 0x200 /* NB conflicts with EF_ARM_SOFT_FLOAT */ -#define EF_ARM_ABI_FLOAT_HARD 0x400 /* NB conflicts with EF_ARM_VFP_FLOAT */ - - -/* Other constants defined in the ARM ELF spec. version B-01. */ -/* NB. These conflict with values defined above. */ -#define EF_ARM_SYMSARESORTED 0x04 -#define EF_ARM_DYNSYMSUSESEGIDX 0x08 -#define EF_ARM_MAPSYMSFIRST 0x10 -#define EF_ARM_EABIMASK 0XFF000000 - -/* Constants defined in AAELF. */ -#define EF_ARM_BE8 0x00800000 -#define EF_ARM_LE8 0x00400000 - -#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) -#define EF_ARM_EABI_UNKNOWN 0x00000000 -#define EF_ARM_EABI_VER1 0x01000000 -#define EF_ARM_EABI_VER2 0x02000000 -#define EF_ARM_EABI_VER3 0x03000000 -#define EF_ARM_EABI_VER4 0x04000000 -#define EF_ARM_EABI_VER5 0x05000000 - -/* Additional symbol types for Thumb. */ -#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */ -#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */ - -/* ARM-specific values for sh_flags */ -#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ -#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined - in the input to a link step. */ - -/* ARM-specific program header flags */ -#define PF_ARM_SB 0x10000000 /* Segment contains the location - addressed by the static base. */ -#define PF_ARM_PI 0x20000000 /* Position-independent segment. */ -#define PF_ARM_ABS 0x40000000 /* Absolute segment. */ - -/* Processor specific values for the Phdr p_type field. */ -#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */ - -/* Processor specific values for the Shdr sh_type field. */ -#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */ -#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */ -#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */ - - -/* AArch64 relocs. */ - -#define R_AARCH64_NONE 0 /* No relocation. */ - -/* ILP32 AArch64 relocs. */ -#define R_AARCH64_P32_ABS32 1 /* Direct 32 bit. */ -#define R_AARCH64_P32_COPY 180 /* Copy symbol at runtime. */ -#define R_AARCH64_P32_GLOB_DAT 181 /* Create GOT entry. */ -#define R_AARCH64_P32_JUMP_SLOT 182 /* Create PLT entry. */ -#define R_AARCH64_P32_RELATIVE 183 /* Adjust by program base. */ -#define R_AARCH64_P32_TLS_DTPMOD 184 /* Module number, 32 bit. */ -#define R_AARCH64_P32_TLS_DTPREL 185 /* Module-relative offset, 32 bit. */ -#define R_AARCH64_P32_TLS_TPREL 186 /* TP-relative offset, 32 bit. */ -#define R_AARCH64_P32_TLSDESC 187 /* TLS Descriptor. */ -#define R_AARCH64_P32_IRELATIVE 188 /* STT_GNU_IFUNC relocation. */ - -/* LP64 AArch64 relocs. */ -#define R_AARCH64_ABS64 257 /* Direct 64 bit. */ -#define R_AARCH64_ABS32 258 /* Direct 32 bit. */ -#define R_AARCH64_ABS16 259 /* Direct 16-bit. */ -#define R_AARCH64_PREL64 260 /* PC-relative 64-bit. */ -#define R_AARCH64_PREL32 261 /* PC-relative 32-bit. */ -#define R_AARCH64_PREL16 262 /* PC-relative 16-bit. */ -#define R_AARCH64_MOVW_UABS_G0 263 /* Dir. MOVZ imm. from bits 15:0. */ -#define R_AARCH64_MOVW_UABS_G0_NC 264 /* Likewise for MOVK; no check. */ -#define R_AARCH64_MOVW_UABS_G1 265 /* Dir. MOVZ imm. from bits 31:16. */ -#define R_AARCH64_MOVW_UABS_G1_NC 266 /* Likewise for MOVK; no check. */ -#define R_AARCH64_MOVW_UABS_G2 267 /* Dir. MOVZ imm. from bits 47:32. */ -#define R_AARCH64_MOVW_UABS_G2_NC 268 /* Likewise for MOVK; no check. */ -#define R_AARCH64_MOVW_UABS_G3 269 /* Dir. MOV{K,Z} imm. from 63:48. */ -#define R_AARCH64_MOVW_SABS_G0 270 /* Dir. MOV{N,Z} imm. from 15:0. */ -#define R_AARCH64_MOVW_SABS_G1 271 /* Dir. MOV{N,Z} imm. from 31:16. */ -#define R_AARCH64_MOVW_SABS_G2 272 /* Dir. MOV{N,Z} imm. from 47:32. */ -#define R_AARCH64_LD_PREL_LO19 273 /* PC-rel. LD imm. from bits 20:2. */ -#define R_AARCH64_ADR_PREL_LO21 274 /* PC-rel. ADR imm. from bits 20:0. */ -#define R_AARCH64_ADR_PREL_PG_HI21 275 /* Page-rel. ADRP imm. from 32:12. */ -#define R_AARCH64_ADR_PREL_PG_HI21_NC 276 /* Likewise; no overflow check. */ -#define R_AARCH64_ADD_ABS_LO12_NC 277 /* Dir. ADD imm. from bits 11:0. */ -#define R_AARCH64_LDST8_ABS_LO12_NC 278 /* Likewise for LD/ST; no check. */ -#define R_AARCH64_TSTBR14 279 /* PC-rel. TBZ/TBNZ imm. from 15:2. */ -#define R_AARCH64_CONDBR19 280 /* PC-rel. cond. br. imm. from 20:2. */ -#define R_AARCH64_JUMP26 282 /* PC-rel. B imm. from bits 27:2. */ -#define R_AARCH64_CALL26 283 /* Likewise for CALL. */ -#define R_AARCH64_LDST16_ABS_LO12_NC 284 /* Dir. ADD imm. from bits 11:1. */ -#define R_AARCH64_LDST32_ABS_LO12_NC 285 /* Likewise for bits 11:2. */ -#define R_AARCH64_LDST64_ABS_LO12_NC 286 /* Likewise for bits 11:3. */ -#define R_AARCH64_MOVW_PREL_G0 287 /* PC-rel. MOV{N,Z} imm. from 15:0. */ -#define R_AARCH64_MOVW_PREL_G0_NC 288 /* Likewise for MOVK; no check. */ -#define R_AARCH64_MOVW_PREL_G1 289 /* PC-rel. MOV{N,Z} imm. from 31:16. */ -#define R_AARCH64_MOVW_PREL_G1_NC 290 /* Likewise for MOVK; no check. */ -#define R_AARCH64_MOVW_PREL_G2 291 /* PC-rel. MOV{N,Z} imm. from 47:32. */ -#define R_AARCH64_MOVW_PREL_G2_NC 292 /* Likewise for MOVK; no check. */ -#define R_AARCH64_MOVW_PREL_G3 293 /* PC-rel. MOV{N,Z} imm. from 63:48. */ -#define R_AARCH64_LDST128_ABS_LO12_NC 299 /* Dir. ADD imm. from bits 11:4. */ -#define R_AARCH64_MOVW_GOTOFF_G0 300 /* GOT-rel. off. MOV{N,Z} imm. 15:0. */ -#define R_AARCH64_MOVW_GOTOFF_G0_NC 301 /* Likewise for MOVK; no check. */ -#define R_AARCH64_MOVW_GOTOFF_G1 302 /* GOT-rel. o. MOV{N,Z} imm. 31:16. */ -#define R_AARCH64_MOVW_GOTOFF_G1_NC 303 /* Likewise for MOVK; no check. */ -#define R_AARCH64_MOVW_GOTOFF_G2 304 /* GOT-rel. o. MOV{N,Z} imm. 47:32. */ -#define R_AARCH64_MOVW_GOTOFF_G2_NC 305 /* Likewise for MOVK; no check. */ -#define R_AARCH64_MOVW_GOTOFF_G3 306 /* GOT-rel. o. MOV{N,Z} imm. 63:48. */ -#define R_AARCH64_GOTREL64 307 /* GOT-relative 64-bit. */ -#define R_AARCH64_GOTREL32 308 /* GOT-relative 32-bit. */ -#define R_AARCH64_GOT_LD_PREL19 309 /* PC-rel. GOT off. load imm. 20:2. */ -#define R_AARCH64_LD64_GOTOFF_LO15 310 /* GOT-rel. off. LD/ST imm. 14:3. */ -#define R_AARCH64_ADR_GOT_PAGE 311 /* P-page-rel. GOT off. ADRP 32:12. */ -#define R_AARCH64_LD64_GOT_LO12_NC 312 /* Dir. GOT off. LD/ST imm. 11:3. */ -#define R_AARCH64_LD64_GOTPAGE_LO15 313 /* GOT-page-rel. GOT off. LD/ST 14:3 */ -#define R_AARCH64_TLSGD_ADR_PREL21 512 /* PC-relative ADR imm. 20:0. */ -#define R_AARCH64_TLSGD_ADR_PAGE21 513 /* page-rel. ADRP imm. 32:12. */ -#define R_AARCH64_TLSGD_ADD_LO12_NC 514 /* direct ADD imm. from 11:0. */ -#define R_AARCH64_TLSGD_MOVW_G1 515 /* GOT-rel. MOV{N,Z} 31:16. */ -#define R_AARCH64_TLSGD_MOVW_G0_NC 516 /* GOT-rel. MOVK imm. 15:0. */ -#define R_AARCH64_TLSLD_ADR_PREL21 517 /* Like 512; local dynamic model. */ -#define R_AARCH64_TLSLD_ADR_PAGE21 518 /* Like 513; local dynamic model. */ -#define R_AARCH64_TLSLD_ADD_LO12_NC 519 /* Like 514; local dynamic model. */ -#define R_AARCH64_TLSLD_MOVW_G1 520 /* Like 515; local dynamic model. */ -#define R_AARCH64_TLSLD_MOVW_G0_NC 521 /* Like 516; local dynamic model. */ -#define R_AARCH64_TLSLD_LD_PREL19 522 /* TLS PC-rel. load imm. 20:2. */ -#define R_AARCH64_TLSLD_MOVW_DTPREL_G2 523 /* TLS DTP-rel. MOV{N,Z} 47:32. */ -#define R_AARCH64_TLSLD_MOVW_DTPREL_G1 524 /* TLS DTP-rel. MOV{N,Z} 31:16. */ -#define R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC 525 /* Likewise; MOVK; no check. */ -#define R_AARCH64_TLSLD_MOVW_DTPREL_G0 526 /* TLS DTP-rel. MOV{N,Z} 15:0. */ -#define R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC 527 /* Likewise; MOVK; no check. */ -#define R_AARCH64_TLSLD_ADD_DTPREL_HI12 528 /* DTP-rel. ADD imm. from 23:12. */ -#define R_AARCH64_TLSLD_ADD_DTPREL_LO12 529 /* DTP-rel. ADD imm. from 11:0. */ -#define R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC 530 /* Likewise; no ovfl. check. */ -#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12 531 /* DTP-rel. LD/ST imm. 11:0. */ -#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC 532 /* Likewise; no check. */ -#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12 533 /* DTP-rel. LD/ST imm. 11:1. */ -#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC 534 /* Likewise; no check. */ -#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12 535 /* DTP-rel. LD/ST imm. 11:2. */ -#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC 536 /* Likewise; no check. */ -#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12 537 /* DTP-rel. LD/ST imm. 11:3. */ -#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC 538 /* Likewise; no check. */ -#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 539 /* GOT-rel. MOV{N,Z} 31:16. */ -#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC 540 /* GOT-rel. MOVK 15:0. */ -#define R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 541 /* Page-rel. ADRP 32:12. */ -#define R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC 542 /* Direct LD off. 11:3. */ -#define R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 543 /* PC-rel. load imm. 20:2. */ -#define R_AARCH64_TLSLE_MOVW_TPREL_G2 544 /* TLS TP-rel. MOV{N,Z} 47:32. */ -#define R_AARCH64_TLSLE_MOVW_TPREL_G1 545 /* TLS TP-rel. MOV{N,Z} 31:16. */ -#define R_AARCH64_TLSLE_MOVW_TPREL_G1_NC 546 /* Likewise; MOVK; no check. */ -#define R_AARCH64_TLSLE_MOVW_TPREL_G0 547 /* TLS TP-rel. MOV{N,Z} 15:0. */ -#define R_AARCH64_TLSLE_MOVW_TPREL_G0_NC 548 /* Likewise; MOVK; no check. */ -#define R_AARCH64_TLSLE_ADD_TPREL_HI12 549 /* TP-rel. ADD imm. 23:12. */ -#define R_AARCH64_TLSLE_ADD_TPREL_LO12 550 /* TP-rel. ADD imm. 11:0. */ -#define R_AARCH64_TLSLE_ADD_TPREL_LO12_NC 551 /* Likewise; no ovfl. check. */ -#define R_AARCH64_TLSLE_LDST8_TPREL_LO12 552 /* TP-rel. LD/ST off. 11:0. */ -#define R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC 553 /* Likewise; no ovfl. check. */ -#define R_AARCH64_TLSLE_LDST16_TPREL_LO12 554 /* TP-rel. LD/ST off. 11:1. */ -#define R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC 555 /* Likewise; no check. */ -#define R_AARCH64_TLSLE_LDST32_TPREL_LO12 556 /* TP-rel. LD/ST off. 11:2. */ -#define R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC 557 /* Likewise; no check. */ -#define R_AARCH64_TLSLE_LDST64_TPREL_LO12 558 /* TP-rel. LD/ST off. 11:3. */ -#define R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC 559 /* Likewise; no check. */ -#define R_AARCH64_TLSDESC_LD_PREL19 560 /* PC-rel. load immediate 20:2. */ -#define R_AARCH64_TLSDESC_ADR_PREL21 561 /* PC-rel. ADR immediate 20:0. */ -#define R_AARCH64_TLSDESC_ADR_PAGE21 562 /* Page-rel. ADRP imm. 32:12. */ -#define R_AARCH64_TLSDESC_LD64_LO12 563 /* Direct LD off. from 11:3. */ -#define R_AARCH64_TLSDESC_ADD_LO12 564 /* Direct ADD imm. from 11:0. */ -#define R_AARCH64_TLSDESC_OFF_G1 565 /* GOT-rel. MOV{N,Z} imm. 31:16. */ -#define R_AARCH64_TLSDESC_OFF_G0_NC 566 /* GOT-rel. MOVK imm. 15:0; no ck. */ -#define R_AARCH64_TLSDESC_LDR 567 /* Relax LDR. */ -#define R_AARCH64_TLSDESC_ADD 568 /* Relax ADD. */ -#define R_AARCH64_TLSDESC_CALL 569 /* Relax BLR. */ -#define R_AARCH64_TLSLE_LDST128_TPREL_LO12 570 /* TP-rel. LD/ST off. 11:4. */ -#define R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC 571 /* Likewise; no check. */ -#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12 572 /* DTP-rel. LD/ST imm. 11:4. */ -#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC 573 /* Likewise; no check. */ -#define R_AARCH64_COPY 1024 /* Copy symbol at runtime. */ -#define R_AARCH64_GLOB_DAT 1025 /* Create GOT entry. */ -#define R_AARCH64_JUMP_SLOT 1026 /* Create PLT entry. */ -#define R_AARCH64_RELATIVE 1027 /* Adjust by program base. */ -#define R_AARCH64_TLS_DTPMOD 1028 /* Module number, 64 bit. */ -#define R_AARCH64_TLS_DTPREL 1029 /* Module-relative offset, 64 bit. */ -#define R_AARCH64_TLS_TPREL 1030 /* TP-relative offset, 64 bit. */ -#define R_AARCH64_TLSDESC 1031 /* TLS Descriptor. */ -#define R_AARCH64_IRELATIVE 1032 /* STT_GNU_IFUNC relocation. */ - -/* AArch64 specific values for the Dyn d_tag field. */ -#define DT_AARCH64_BTI_PLT (DT_LOPROC + 1) -#define DT_AARCH64_PAC_PLT (DT_LOPROC + 3) -#define DT_AARCH64_VARIANT_PCS (DT_LOPROC + 5) -#define DT_AARCH64_NUM 6 - -/* AArch64 specific values for the st_other field. */ -#define STO_AARCH64_VARIANT_PCS 0x80 - -/* ARM relocs. */ - -#define R_ARM_NONE 0 /* No reloc */ -#define R_ARM_PC24 1 /* Deprecated PC relative 26 - bit branch. */ -#define R_ARM_ABS32 2 /* Direct 32 bit */ -#define R_ARM_REL32 3 /* PC relative 32 bit */ -#define R_ARM_PC13 4 -#define R_ARM_ABS16 5 /* Direct 16 bit */ -#define R_ARM_ABS12 6 /* Direct 12 bit */ -#define R_ARM_THM_ABS5 7 /* Direct & 0x7C (LDR, STR). */ -#define R_ARM_ABS8 8 /* Direct 8 bit */ -#define R_ARM_SBREL32 9 -#define R_ARM_THM_PC22 10 /* PC relative 24 bit (Thumb32 BL). */ -#define R_ARM_THM_PC8 11 /* PC relative & 0x3FC - (Thumb16 LDR, ADD, ADR). */ -#define R_ARM_AMP_VCALL9 12 -#define R_ARM_SWI24 13 /* Obsolete static relocation. */ -#define R_ARM_TLS_DESC 13 /* Dynamic relocation. */ -#define R_ARM_THM_SWI8 14 /* Reserved. */ -#define R_ARM_XPC25 15 /* Reserved. */ -#define R_ARM_THM_XPC22 16 /* Reserved. */ -#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */ -#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */ -#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */ -#define R_ARM_COPY 20 /* Copy symbol at runtime */ -#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ -#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ -#define R_ARM_RELATIVE 23 /* Adjust by program base */ -#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ -#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ -#define R_ARM_GOT32 26 /* 32 bit GOT entry */ -#define R_ARM_PLT32 27 /* Deprecated, 32 bit PLT address. */ -#define R_ARM_CALL 28 /* PC relative 24 bit (BL, BLX). */ -#define R_ARM_JUMP24 29 /* PC relative 24 bit - (B, BL). */ -#define R_ARM_THM_JUMP24 30 /* PC relative 24 bit (Thumb32 B.W). */ -#define R_ARM_BASE_ABS 31 /* Adjust by program base. */ -#define R_ARM_ALU_PCREL_7_0 32 /* Obsolete. */ -#define R_ARM_ALU_PCREL_15_8 33 /* Obsolete. */ -#define R_ARM_ALU_PCREL_23_15 34 /* Obsolete. */ -#define R_ARM_LDR_SBREL_11_0 35 /* Deprecated, prog. base relative. */ -#define R_ARM_ALU_SBREL_19_12 36 /* Deprecated, prog. base relative. */ -#define R_ARM_ALU_SBREL_27_20 37 /* Deprecated, prog. base relative. */ -#define R_ARM_TARGET1 38 -#define R_ARM_SBREL31 39 /* Program base relative. */ -#define R_ARM_V4BX 40 -#define R_ARM_TARGET2 41 -#define R_ARM_PREL31 42 /* 32 bit PC relative. */ -#define R_ARM_MOVW_ABS_NC 43 /* Direct 16-bit (MOVW). */ -#define R_ARM_MOVT_ABS 44 /* Direct high 16-bit (MOVT). */ -#define R_ARM_MOVW_PREL_NC 45 /* PC relative 16-bit (MOVW). */ -#define R_ARM_MOVT_PREL 46 /* PC relative (MOVT). */ -#define R_ARM_THM_MOVW_ABS_NC 47 /* Direct 16 bit (Thumb32 MOVW). */ -#define R_ARM_THM_MOVT_ABS 48 /* Direct high 16 bit - (Thumb32 MOVT). */ -#define R_ARM_THM_MOVW_PREL_NC 49 /* PC relative 16 bit - (Thumb32 MOVW). */ -#define R_ARM_THM_MOVT_PREL 50 /* PC relative high 16 bit - (Thumb32 MOVT). */ -#define R_ARM_THM_JUMP19 51 /* PC relative 20 bit - (Thumb32 B.W). */ -#define R_ARM_THM_JUMP6 52 /* PC relative X & 0x7E - (Thumb16 CBZ, CBNZ). */ -#define R_ARM_THM_ALU_PREL_11_0 53 /* PC relative 12 bit - (Thumb32 ADR.W). */ -#define R_ARM_THM_PC12 54 /* PC relative 12 bit - (Thumb32 LDR{D,SB,H,SH}). */ -#define R_ARM_ABS32_NOI 55 /* Direct 32-bit. */ -#define R_ARM_REL32_NOI 56 /* PC relative 32-bit. */ -#define R_ARM_ALU_PC_G0_NC 57 /* PC relative (ADD, SUB). */ -#define R_ARM_ALU_PC_G0 58 /* PC relative (ADD, SUB). */ -#define R_ARM_ALU_PC_G1_NC 59 /* PC relative (ADD, SUB). */ -#define R_ARM_ALU_PC_G1 60 /* PC relative (ADD, SUB). */ -#define R_ARM_ALU_PC_G2 61 /* PC relative (ADD, SUB). */ -#define R_ARM_LDR_PC_G1 62 /* PC relative (LDR,STR,LDRB,STRB). */ -#define R_ARM_LDR_PC_G2 63 /* PC relative (LDR,STR,LDRB,STRB). */ -#define R_ARM_LDRS_PC_G0 64 /* PC relative (STR{D,H}, - LDR{D,SB,H,SH}). */ -#define R_ARM_LDRS_PC_G1 65 /* PC relative (STR{D,H}, - LDR{D,SB,H,SH}). */ -#define R_ARM_LDRS_PC_G2 66 /* PC relative (STR{D,H}, - LDR{D,SB,H,SH}). */ -#define R_ARM_LDC_PC_G0 67 /* PC relative (LDC, STC). */ -#define R_ARM_LDC_PC_G1 68 /* PC relative (LDC, STC). */ -#define R_ARM_LDC_PC_G2 69 /* PC relative (LDC, STC). */ -#define R_ARM_ALU_SB_G0_NC 70 /* Program base relative (ADD,SUB). */ -#define R_ARM_ALU_SB_G0 71 /* Program base relative (ADD,SUB). */ -#define R_ARM_ALU_SB_G1_NC 72 /* Program base relative (ADD,SUB). */ -#define R_ARM_ALU_SB_G1 73 /* Program base relative (ADD,SUB). */ -#define R_ARM_ALU_SB_G2 74 /* Program base relative (ADD,SUB). */ -#define R_ARM_LDR_SB_G0 75 /* Program base relative (LDR, - STR, LDRB, STRB). */ -#define R_ARM_LDR_SB_G1 76 /* Program base relative - (LDR, STR, LDRB, STRB). */ -#define R_ARM_LDR_SB_G2 77 /* Program base relative - (LDR, STR, LDRB, STRB). */ -#define R_ARM_LDRS_SB_G0 78 /* Program base relative - (LDR, STR, LDRB, STRB). */ -#define R_ARM_LDRS_SB_G1 79 /* Program base relative - (LDR, STR, LDRB, STRB). */ -#define R_ARM_LDRS_SB_G2 80 /* Program base relative - (LDR, STR, LDRB, STRB). */ -#define R_ARM_LDC_SB_G0 81 /* Program base relative (LDC,STC). */ -#define R_ARM_LDC_SB_G1 82 /* Program base relative (LDC,STC). */ -#define R_ARM_LDC_SB_G2 83 /* Program base relative (LDC,STC). */ -#define R_ARM_MOVW_BREL_NC 84 /* Program base relative 16 - bit (MOVW). */ -#define R_ARM_MOVT_BREL 85 /* Program base relative high - 16 bit (MOVT). */ -#define R_ARM_MOVW_BREL 86 /* Program base relative 16 - bit (MOVW). */ -#define R_ARM_THM_MOVW_BREL_NC 87 /* Program base relative 16 - bit (Thumb32 MOVW). */ -#define R_ARM_THM_MOVT_BREL 88 /* Program base relative high - 16 bit (Thumb32 MOVT). */ -#define R_ARM_THM_MOVW_BREL 89 /* Program base relative 16 - bit (Thumb32 MOVW). */ -#define R_ARM_TLS_GOTDESC 90 -#define R_ARM_TLS_CALL 91 -#define R_ARM_TLS_DESCSEQ 92 /* TLS relaxation. */ -#define R_ARM_THM_TLS_CALL 93 -#define R_ARM_PLT32_ABS 94 -#define R_ARM_GOT_ABS 95 /* GOT entry. */ -#define R_ARM_GOT_PREL 96 /* PC relative GOT entry. */ -#define R_ARM_GOT_BREL12 97 /* GOT entry relative to GOT - origin (LDR). */ -#define R_ARM_GOTOFF12 98 /* 12 bit, GOT entry relative - to GOT origin (LDR, STR). */ -#define R_ARM_GOTRELAX 99 -#define R_ARM_GNU_VTENTRY 100 -#define R_ARM_GNU_VTINHERIT 101 -#define R_ARM_THM_PC11 102 /* PC relative & 0xFFE (Thumb16 B). */ -#define R_ARM_THM_PC9 103 /* PC relative & 0x1FE - (Thumb16 B/B). */ -#define R_ARM_TLS_GD32 104 /* PC-rel 32 bit for global dynamic - thread local data */ -#define R_ARM_TLS_LDM32 105 /* PC-rel 32 bit for local dynamic - thread local data */ -#define R_ARM_TLS_LDO32 106 /* 32 bit offset relative to TLS - block */ -#define R_ARM_TLS_IE32 107 /* PC-rel 32 bit for GOT entry of - static TLS block offset */ -#define R_ARM_TLS_LE32 108 /* 32 bit offset relative to static - TLS block */ -#define R_ARM_TLS_LDO12 109 /* 12 bit relative to TLS - block (LDR, STR). */ -#define R_ARM_TLS_LE12 110 /* 12 bit relative to static - TLS block (LDR, STR). */ -#define R_ARM_TLS_IE12GP 111 /* 12 bit GOT entry relative - to GOT origin (LDR). */ -#define R_ARM_ME_TOO 128 /* Obsolete. */ -#define R_ARM_THM_TLS_DESCSEQ 129 -#define R_ARM_THM_TLS_DESCSEQ16 129 -#define R_ARM_THM_TLS_DESCSEQ32 130 -#define R_ARM_THM_GOT_BREL12 131 /* GOT entry relative to GOT - origin, 12 bit (Thumb32 LDR). */ -#define R_ARM_IRELATIVE 160 -#define R_ARM_RXPC25 249 -#define R_ARM_RSBREL32 250 -#define R_ARM_THM_RPC22 251 -#define R_ARM_RREL32 252 -#define R_ARM_RABS22 253 -#define R_ARM_RPC24 254 -#define R_ARM_RBASE 255 -/* Keep this the last entry. */ -#define R_ARM_NUM 256 - -/* C-SKY */ -#define R_CKCORE_NONE 0 /* no reloc */ -#define R_CKCORE_ADDR32 1 /* direct 32 bit (S + A) */ -#define R_CKCORE_PCRELIMM8BY4 2 /* disp ((S + A - P) >> 2) & 0xff */ -#define R_CKCORE_PCRELIMM11BY2 3 /* disp ((S + A - P) >> 1) & 0x7ff */ -#define R_CKCORE_PCREL32 5 /* 32-bit rel (S + A - P) */ -#define R_CKCORE_PCRELJSR_IMM11BY2 6 /* disp ((S + A - P) >>1) & 0x7ff */ -#define R_CKCORE_RELATIVE 9 /* 32 bit adjust program base(B + A)*/ -#define R_CKCORE_COPY 10 /* 32 bit adjust by program base */ -#define R_CKCORE_GLOB_DAT 11 /* off between got and sym (S) */ -#define R_CKCORE_JUMP_SLOT 12 /* PLT entry (S) */ -#define R_CKCORE_GOTOFF 13 /* offset to GOT (S + A - GOT) */ -#define R_CKCORE_GOTPC 14 /* PC offset to GOT (GOT + A - P) */ -#define R_CKCORE_GOT32 15 /* 32 bit GOT entry (G) */ -#define R_CKCORE_PLT32 16 /* 32 bit PLT entry (G) */ -#define R_CKCORE_ADDRGOT 17 /* GOT entry in GLOB_DAT (GOT + G) */ -#define R_CKCORE_ADDRPLT 18 /* PLT entry in GLOB_DAT (GOT + G) */ -#define R_CKCORE_PCREL_IMM26BY2 19 /* ((S + A - P) >> 1) & 0x3ffffff */ -#define R_CKCORE_PCREL_IMM16BY2 20 /* disp ((S + A - P) >> 1) & 0xffff */ -#define R_CKCORE_PCREL_IMM16BY4 21 /* disp ((S + A - P) >> 2) & 0xffff */ -#define R_CKCORE_PCREL_IMM10BY2 22 /* disp ((S + A - P) >> 1) & 0x3ff */ -#define R_CKCORE_PCREL_IMM10BY4 23 /* disp ((S + A - P) >> 2) & 0x3ff */ -#define R_CKCORE_ADDR_HI16 24 /* high & low 16 bit ADDR */ - /* ((S + A) >> 16) & 0xffff */ -#define R_CKCORE_ADDR_LO16 25 /* (S + A) & 0xffff */ -#define R_CKCORE_GOTPC_HI16 26 /* high & low 16 bit GOTPC */ - /* ((GOT + A - P) >> 16) & 0xffff */ -#define R_CKCORE_GOTPC_LO16 27 /* (GOT + A - P) & 0xffff */ -#define R_CKCORE_GOTOFF_HI16 28 /* high & low 16 bit GOTOFF */ - /* ((S + A - GOT) >> 16) & 0xffff */ -#define R_CKCORE_GOTOFF_LO16 29 /* (S + A - GOT) & 0xffff */ -#define R_CKCORE_GOT12 30 /* 12 bit disp GOT entry (G) */ -#define R_CKCORE_GOT_HI16 31 /* high & low 16 bit GOT */ - /* (G >> 16) & 0xffff */ -#define R_CKCORE_GOT_LO16 32 /* (G & 0xffff) */ -#define R_CKCORE_PLT12 33 /* 12 bit disp PLT entry (G) */ -#define R_CKCORE_PLT_HI16 34 /* high & low 16 bit PLT */ - /* (G >> 16) & 0xffff */ -#define R_CKCORE_PLT_LO16 35 /* G & 0xffff */ -#define R_CKCORE_ADDRGOT_HI16 36 /* high & low 16 bit ADDRGOT */ - /* (GOT + G * 4) & 0xffff */ -#define R_CKCORE_ADDRGOT_LO16 37 /* (GOT + G * 4) & 0xffff */ -#define R_CKCORE_ADDRPLT_HI16 38 /* high & low 16 bit ADDRPLT */ - /* ((GOT + G * 4) >> 16) & 0xFFFF */ -#define R_CKCORE_ADDRPLT_LO16 39 /* (GOT+G*4) & 0xffff */ -#define R_CKCORE_PCREL_JSR_IMM26BY2 40 /* disp ((S+A-P) >>1) & x3ffffff */ -#define R_CKCORE_TOFFSET_LO16 41 /* (S+A-BTEXT) & 0xffff */ -#define R_CKCORE_DOFFSET_LO16 42 /* (S+A-BTEXT) & 0xffff */ -#define R_CKCORE_PCREL_IMM18BY2 43 /* disp ((S+A-P) >>1) & 0x3ffff */ -#define R_CKCORE_DOFFSET_IMM18 44 /* disp (S+A-BDATA) & 0x3ffff */ -#define R_CKCORE_DOFFSET_IMM18BY2 45 /* disp ((S+A-BDATA)>>1) & 0x3ffff */ -#define R_CKCORE_DOFFSET_IMM18BY4 46 /* disp ((S+A-BDATA)>>2) & 0x3ffff */ -#define R_CKCORE_GOT_IMM18BY4 48 /* disp (G >> 2) */ -#define R_CKCORE_PLT_IMM18BY4 49 /* disp (G >> 2) */ -#define R_CKCORE_PCREL_IMM7BY4 50 /* disp ((S+A-P) >>2) & 0x7f */ -#define R_CKCORE_TLS_LE32 51 /* 32 bit offset to TLS block */ -#define R_CKCORE_TLS_IE32 52 -#define R_CKCORE_TLS_GD32 53 -#define R_CKCORE_TLS_LDM32 54 -#define R_CKCORE_TLS_LDO32 55 -#define R_CKCORE_TLS_DTPMOD32 56 -#define R_CKCORE_TLS_DTPOFF32 57 -#define R_CKCORE_TLS_TPOFF32 58 - -/* C-SKY elf header definition. */ -#define EF_CSKY_ABIMASK 0XF0000000 -#define EF_CSKY_OTHER 0X0FFF0000 -#define EF_CSKY_PROCESSOR 0X0000FFFF - -#define EF_CSKY_ABIV1 0X10000000 -#define EF_CSKY_ABIV2 0X20000000 - -/* C-SKY attributes section. */ -#define SHT_CSKY_ATTRIBUTES (SHT_LOPROC + 1) - -/* IA-64 specific declarations. */ - -/* Processor specific flags for the Ehdr e_flags field. */ -#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ -#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ -#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ - -/* Processor specific values for the Phdr p_type field. */ -#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ -#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ -#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) -#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) -#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) - -/* Processor specific flags for the Phdr p_flags field. */ -#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ - -/* Processor specific values for the Shdr sh_type field. */ -#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ -#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ - -/* Processor specific flags for the Shdr sh_flags field. */ -#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ -#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ - -/* Processor specific values for the Dyn d_tag field. */ -#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) -#define DT_IA_64_NUM 1 - -/* IA-64 relocations. */ -#define R_IA64_NONE 0x00 /* none */ -#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ -#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ -#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ -#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ -#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ -#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ -#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ -#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ -#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ -#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ -#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ -#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ -#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ -#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ -#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ -#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ -#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ -#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ -#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ -#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ -#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ -#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ -#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ -#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ -#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ -#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ -#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ -#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ -#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ -#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ -#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ -#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ -#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ -#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ -#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ -#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ -#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ -#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ -#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ -#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ -#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ -#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ -#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ -#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ -#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ -#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ -#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ -#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ -#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ -#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ -#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ -#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ -#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ -#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ -#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ -#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ -#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ -#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ -#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ -#define R_IA64_COPY 0x84 /* copy relocation */ -#define R_IA64_SUB 0x85 /* Addend and symbol difference */ -#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ -#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ -#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ -#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ -#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ -#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ -#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ -#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ -#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ -#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ -#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ -#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ -#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ -#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ -#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ -#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ -#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ -#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ -#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ - -/* SH specific declarations */ - -/* Processor specific flags for the ELF header e_flags field. */ -#define EF_SH_MACH_MASK 0x1f -#define EF_SH_UNKNOWN 0x0 -#define EF_SH1 0x1 -#define EF_SH2 0x2 -#define EF_SH3 0x3 -#define EF_SH_DSP 0x4 -#define EF_SH3_DSP 0x5 -#define EF_SH4AL_DSP 0x6 -#define EF_SH3E 0x8 -#define EF_SH4 0x9 -#define EF_SH2E 0xb -#define EF_SH4A 0xc -#define EF_SH2A 0xd -#define EF_SH4_NOFPU 0x10 -#define EF_SH4A_NOFPU 0x11 -#define EF_SH4_NOMMU_NOFPU 0x12 -#define EF_SH2A_NOFPU 0x13 -#define EF_SH3_NOMMU 0x14 -#define EF_SH2A_SH4_NOFPU 0x15 -#define EF_SH2A_SH3_NOFPU 0x16 -#define EF_SH2A_SH4 0x17 -#define EF_SH2A_SH3E 0x18 - -/* SH relocs. */ -#define R_SH_NONE 0 -#define R_SH_DIR32 1 -#define R_SH_REL32 2 -#define R_SH_DIR8WPN 3 -#define R_SH_IND12W 4 -#define R_SH_DIR8WPL 5 -#define R_SH_DIR8WPZ 6 -#define R_SH_DIR8BP 7 -#define R_SH_DIR8W 8 -#define R_SH_DIR8L 9 -#define R_SH_SWITCH16 25 -#define R_SH_SWITCH32 26 -#define R_SH_USES 27 -#define R_SH_COUNT 28 -#define R_SH_ALIGN 29 -#define R_SH_CODE 30 -#define R_SH_DATA 31 -#define R_SH_LABEL 32 -#define R_SH_SWITCH8 33 -#define R_SH_GNU_VTINHERIT 34 -#define R_SH_GNU_VTENTRY 35 -#define R_SH_TLS_GD_32 144 -#define R_SH_TLS_LD_32 145 -#define R_SH_TLS_LDO_32 146 -#define R_SH_TLS_IE_32 147 -#define R_SH_TLS_LE_32 148 -#define R_SH_TLS_DTPMOD32 149 -#define R_SH_TLS_DTPOFF32 150 -#define R_SH_TLS_TPOFF32 151 -#define R_SH_GOT32 160 -#define R_SH_PLT32 161 -#define R_SH_COPY 162 -#define R_SH_GLOB_DAT 163 -#define R_SH_JMP_SLOT 164 -#define R_SH_RELATIVE 165 -#define R_SH_GOTOFF 166 -#define R_SH_GOTPC 167 -/* Keep this the last entry. */ -#define R_SH_NUM 256 - -/* S/390 specific definitions. */ - -/* Valid values for the e_flags field. */ - -#define EF_S390_HIGH_GPRS 0x00000001 /* High GPRs kernel facility needed. */ - -/* Additional s390 relocs */ - -#define R_390_NONE 0 /* No reloc. */ -#define R_390_8 1 /* Direct 8 bit. */ -#define R_390_12 2 /* Direct 12 bit. */ -#define R_390_16 3 /* Direct 16 bit. */ -#define R_390_32 4 /* Direct 32 bit. */ -#define R_390_PC32 5 /* PC relative 32 bit. */ -#define R_390_GOT12 6 /* 12 bit GOT offset. */ -#define R_390_GOT32 7 /* 32 bit GOT offset. */ -#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ -#define R_390_COPY 9 /* Copy symbol at runtime. */ -#define R_390_GLOB_DAT 10 /* Create GOT entry. */ -#define R_390_JMP_SLOT 11 /* Create PLT entry. */ -#define R_390_RELATIVE 12 /* Adjust by program base. */ -#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ -#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ -#define R_390_GOT16 15 /* 16 bit GOT offset. */ -#define R_390_PC16 16 /* PC relative 16 bit. */ -#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ -#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ -#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ -#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ -#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ -#define R_390_64 22 /* Direct 64 bit. */ -#define R_390_PC64 23 /* PC relative 64 bit. */ -#define R_390_GOT64 24 /* 64 bit GOT offset. */ -#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ -#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ -#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ -#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ -#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ -#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ -#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ -#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ -#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ -#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ -#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ -#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ -#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ -#define R_390_TLS_GDCALL 38 /* Tag for function call in general - dynamic TLS code. */ -#define R_390_TLS_LDCALL 39 /* Tag for function call in local - dynamic TLS code. */ -#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic - thread local data. */ -#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic - thread local data. */ -#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS - block offset. */ -#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS - block offset. */ -#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS - block offset. */ -#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic - thread local data in LE code. */ -#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic - thread local data in LE code. */ -#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for - negated static TLS block offset. */ -#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for - negated static TLS block offset. */ -#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for - negated static TLS block offset. */ -#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to - static TLS block. */ -#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to - static TLS block. */ -#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS - block. */ -#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS - block. */ -#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ -#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ -#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS - block. */ -#define R_390_20 57 /* Direct 20 bit. */ -#define R_390_GOT20 58 /* 20 bit GOT offset. */ -#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */ -#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS - block offset. */ -#define R_390_IRELATIVE 61 /* STT_GNU_IFUNC relocation. */ -/* Keep this the last entry. */ -#define R_390_NUM 62 - - -/* CRIS relocations. */ -#define R_CRIS_NONE 0 -#define R_CRIS_8 1 -#define R_CRIS_16 2 -#define R_CRIS_32 3 -#define R_CRIS_8_PCREL 4 -#define R_CRIS_16_PCREL 5 -#define R_CRIS_32_PCREL 6 -#define R_CRIS_GNU_VTINHERIT 7 -#define R_CRIS_GNU_VTENTRY 8 -#define R_CRIS_COPY 9 -#define R_CRIS_GLOB_DAT 10 -#define R_CRIS_JUMP_SLOT 11 -#define R_CRIS_RELATIVE 12 -#define R_CRIS_16_GOT 13 -#define R_CRIS_32_GOT 14 -#define R_CRIS_16_GOTPLT 15 -#define R_CRIS_32_GOTPLT 16 -#define R_CRIS_32_GOTREL 17 -#define R_CRIS_32_PLT_GOTREL 18 -#define R_CRIS_32_PLT_PCREL 19 - -#define R_CRIS_NUM 20 - - -/* AMD x86-64 relocations. */ -#define R_X86_64_NONE 0 /* No reloc */ -#define R_X86_64_64 1 /* Direct 64 bit */ -#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ -#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ -#define R_X86_64_PLT32 4 /* 32 bit PLT address */ -#define R_X86_64_COPY 5 /* Copy symbol at runtime */ -#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ -#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ -#define R_X86_64_RELATIVE 8 /* Adjust by program base */ -#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative - offset to GOT */ -#define R_X86_64_32 10 /* Direct 32 bit zero extended */ -#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ -#define R_X86_64_16 12 /* Direct 16 bit zero extended */ -#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ -#define R_X86_64_8 14 /* Direct 8 bit sign extended */ -#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ -#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ -#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ -#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ -#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset - to two GOT entries for GD symbol */ -#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset - to two GOT entries for LD symbol */ -#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ -#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset - to GOT entry for IE symbol */ -#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ -#define R_X86_64_PC64 24 /* PC relative 64 bit */ -#define R_X86_64_GOTOFF64 25 /* 64 bit offset to GOT */ -#define R_X86_64_GOTPC32 26 /* 32 bit signed pc relative - offset to GOT */ -#define R_X86_64_GOT64 27 /* 64-bit GOT entry offset */ -#define R_X86_64_GOTPCREL64 28 /* 64-bit PC relative offset - to GOT entry */ -#define R_X86_64_GOTPC64 29 /* 64-bit PC relative offset to GOT */ -#define R_X86_64_GOTPLT64 30 /* like GOT64, says PLT entry needed */ -#define R_X86_64_PLTOFF64 31 /* 64-bit GOT relative offset - to PLT entry */ -#define R_X86_64_SIZE32 32 /* Size of symbol plus 32-bit addend */ -#define R_X86_64_SIZE64 33 /* Size of symbol plus 64-bit addend */ -#define R_X86_64_GOTPC32_TLSDESC 34 /* GOT offset for TLS descriptor. */ -#define R_X86_64_TLSDESC_CALL 35 /* Marker for call through TLS - descriptor. */ -#define R_X86_64_TLSDESC 36 /* TLS descriptor. */ -#define R_X86_64_IRELATIVE 37 /* Adjust indirectly by program base */ -#define R_X86_64_RELATIVE64 38 /* 64-bit adjust by program base */ - /* 39 Reserved was R_X86_64_PC32_BND */ - /* 40 Reserved was R_X86_64_PLT32_BND */ -#define R_X86_64_GOTPCRELX 41 /* Load from 32 bit signed pc relative - offset to GOT entry without REX - prefix, relaxable. */ -#define R_X86_64_REX_GOTPCRELX 42 /* Load from 32 bit signed pc relative - offset to GOT entry with REX prefix, - relaxable. */ -#define R_X86_64_NUM 43 - -/* x86-64 sh_type values. */ -#define SHT_X86_64_UNWIND 0x70000001 /* Unwind information. */ - - -/* AM33 relocations. */ -#define R_MN10300_NONE 0 /* No reloc. */ -#define R_MN10300_32 1 /* Direct 32 bit. */ -#define R_MN10300_16 2 /* Direct 16 bit. */ -#define R_MN10300_8 3 /* Direct 8 bit. */ -#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */ -#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */ -#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */ -#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */ -#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */ -#define R_MN10300_24 9 /* Direct 24 bit. */ -#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */ -#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */ -#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */ -#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */ -#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */ -#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */ -#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */ -#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */ -#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */ -#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */ -#define R_MN10300_COPY 20 /* Copy symbol at runtime. */ -#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */ -#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */ -#define R_MN10300_RELATIVE 23 /* Adjust by program base. */ -#define R_MN10300_TLS_GD 24 /* 32-bit offset for global dynamic. */ -#define R_MN10300_TLS_LD 25 /* 32-bit offset for local dynamic. */ -#define R_MN10300_TLS_LDO 26 /* Module-relative offset. */ -#define R_MN10300_TLS_GOTIE 27 /* GOT offset for static TLS block - offset. */ -#define R_MN10300_TLS_IE 28 /* GOT address for static TLS block - offset. */ -#define R_MN10300_TLS_LE 29 /* Offset relative to static TLS - block. */ -#define R_MN10300_TLS_DTPMOD 30 /* ID of module containing symbol. */ -#define R_MN10300_TLS_DTPOFF 31 /* Offset in module TLS block. */ -#define R_MN10300_TLS_TPOFF 32 /* Offset in static TLS block. */ -#define R_MN10300_SYM_DIFF 33 /* Adjustment for next reloc as needed - by linker relaxation. */ -#define R_MN10300_ALIGN 34 /* Alignment requirement for linker - relaxation. */ -#define R_MN10300_NUM 35 - - -/* M32R relocs. */ -#define R_M32R_NONE 0 /* No reloc. */ -#define R_M32R_16 1 /* Direct 16 bit. */ -#define R_M32R_32 2 /* Direct 32 bit. */ -#define R_M32R_24 3 /* Direct 24 bit. */ -#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */ -#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */ -#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */ -#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */ -#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */ -#define R_M32R_LO16 9 /* Low 16 bit. */ -#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */ -#define R_M32R_GNU_VTINHERIT 11 -#define R_M32R_GNU_VTENTRY 12 -/* M32R relocs use SHT_RELA. */ -#define R_M32R_16_RELA 33 /* Direct 16 bit. */ -#define R_M32R_32_RELA 34 /* Direct 32 bit. */ -#define R_M32R_24_RELA 35 /* Direct 24 bit. */ -#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */ -#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */ -#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */ -#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */ -#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */ -#define R_M32R_LO16_RELA 41 /* Low 16 bit */ -#define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */ -#define R_M32R_RELA_GNU_VTINHERIT 43 -#define R_M32R_RELA_GNU_VTENTRY 44 -#define R_M32R_REL32 45 /* PC relative 32 bit. */ - -#define R_M32R_GOT24 48 /* 24 bit GOT entry */ -#define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */ -#define R_M32R_COPY 50 /* Copy symbol at runtime */ -#define R_M32R_GLOB_DAT 51 /* Create GOT entry */ -#define R_M32R_JMP_SLOT 52 /* Create PLT entry */ -#define R_M32R_RELATIVE 53 /* Adjust by program base */ -#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */ -#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */ -#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned - low */ -#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed - low */ -#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */ -#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to - GOT with unsigned low */ -#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to - GOT with signed low */ -#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to - GOT */ -#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT - with unsigned low */ -#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT - with signed low */ -#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */ -#define R_M32R_NUM 256 /* Keep this the last entry. */ - -/* MicroBlaze relocations */ -#define R_MICROBLAZE_NONE 0 /* No reloc. */ -#define R_MICROBLAZE_32 1 /* Direct 32 bit. */ -#define R_MICROBLAZE_32_PCREL 2 /* PC relative 32 bit. */ -#define R_MICROBLAZE_64_PCREL 3 /* PC relative 64 bit. */ -#define R_MICROBLAZE_32_PCREL_LO 4 /* Low 16 bits of PCREL32. */ -#define R_MICROBLAZE_64 5 /* Direct 64 bit. */ -#define R_MICROBLAZE_32_LO 6 /* Low 16 bit. */ -#define R_MICROBLAZE_SRO32 7 /* Read-only small data area. */ -#define R_MICROBLAZE_SRW32 8 /* Read-write small data area. */ -#define R_MICROBLAZE_64_NONE 9 /* No reloc. */ -#define R_MICROBLAZE_32_SYM_OP_SYM 10 /* Symbol Op Symbol relocation. */ -#define R_MICROBLAZE_GNU_VTINHERIT 11 /* GNU C++ vtable hierarchy. */ -#define R_MICROBLAZE_GNU_VTENTRY 12 /* GNU C++ vtable member usage. */ -#define R_MICROBLAZE_GOTPC_64 13 /* PC-relative GOT offset. */ -#define R_MICROBLAZE_GOT_64 14 /* GOT entry offset. */ -#define R_MICROBLAZE_PLT_64 15 /* PLT offset (PC-relative). */ -#define R_MICROBLAZE_REL 16 /* Adjust by program base. */ -#define R_MICROBLAZE_JUMP_SLOT 17 /* Create PLT entry. */ -#define R_MICROBLAZE_GLOB_DAT 18 /* Create GOT entry. */ -#define R_MICROBLAZE_GOTOFF_64 19 /* 64 bit offset to GOT. */ -#define R_MICROBLAZE_GOTOFF_32 20 /* 32 bit offset to GOT. */ -#define R_MICROBLAZE_COPY 21 /* Runtime copy. */ -#define R_MICROBLAZE_TLS 22 /* TLS Reloc. */ -#define R_MICROBLAZE_TLSGD 23 /* TLS General Dynamic. */ -#define R_MICROBLAZE_TLSLD 24 /* TLS Local Dynamic. */ -#define R_MICROBLAZE_TLSDTPMOD32 25 /* TLS Module ID. */ -#define R_MICROBLAZE_TLSDTPREL32 26 /* TLS Offset Within TLS Block. */ -#define R_MICROBLAZE_TLSDTPREL64 27 /* TLS Offset Within TLS Block. */ -#define R_MICROBLAZE_TLSGOTTPREL32 28 /* TLS Offset From Thread Pointer. */ -#define R_MICROBLAZE_TLSTPREL32 29 /* TLS Offset From Thread Pointer. */ - -/* Legal values for d_tag (dynamic entry type). */ -#define DT_NIOS2_GP 0x70000002 /* Address of _gp. */ - -/* Nios II relocations. */ -#define R_NIOS2_NONE 0 /* No reloc. */ -#define R_NIOS2_S16 1 /* Direct signed 16 bit. */ -#define R_NIOS2_U16 2 /* Direct unsigned 16 bit. */ -#define R_NIOS2_PCREL16 3 /* PC relative 16 bit. */ -#define R_NIOS2_CALL26 4 /* Direct call. */ -#define R_NIOS2_IMM5 5 /* 5 bit constant expression. */ -#define R_NIOS2_CACHE_OPX 6 /* 5 bit expression, shift 22. */ -#define R_NIOS2_IMM6 7 /* 6 bit constant expression. */ -#define R_NIOS2_IMM8 8 /* 8 bit constant expression. */ -#define R_NIOS2_HI16 9 /* High 16 bit. */ -#define R_NIOS2_LO16 10 /* Low 16 bit. */ -#define R_NIOS2_HIADJ16 11 /* High 16 bit, adjusted. */ -#define R_NIOS2_BFD_RELOC_32 12 /* 32 bit symbol value + addend. */ -#define R_NIOS2_BFD_RELOC_16 13 /* 16 bit symbol value + addend. */ -#define R_NIOS2_BFD_RELOC_8 14 /* 8 bit symbol value + addend. */ -#define R_NIOS2_GPREL 15 /* 16 bit GP pointer offset. */ -#define R_NIOS2_GNU_VTINHERIT 16 /* GNU C++ vtable hierarchy. */ -#define R_NIOS2_GNU_VTENTRY 17 /* GNU C++ vtable member usage. */ -#define R_NIOS2_UJMP 18 /* Unconditional branch. */ -#define R_NIOS2_CJMP 19 /* Conditional branch. */ -#define R_NIOS2_CALLR 20 /* Indirect call through register. */ -#define R_NIOS2_ALIGN 21 /* Alignment requirement for - linker relaxation. */ -#define R_NIOS2_GOT16 22 /* 16 bit GOT entry. */ -#define R_NIOS2_CALL16 23 /* 16 bit GOT entry for function. */ -#define R_NIOS2_GOTOFF_LO 24 /* %lo of offset to GOT pointer. */ -#define R_NIOS2_GOTOFF_HA 25 /* %hiadj of offset to GOT pointer. */ -#define R_NIOS2_PCREL_LO 26 /* %lo of PC relative offset. */ -#define R_NIOS2_PCREL_HA 27 /* %hiadj of PC relative offset. */ -#define R_NIOS2_TLS_GD16 28 /* 16 bit GOT offset for TLS GD. */ -#define R_NIOS2_TLS_LDM16 29 /* 16 bit GOT offset for TLS LDM. */ -#define R_NIOS2_TLS_LDO16 30 /* 16 bit module relative offset. */ -#define R_NIOS2_TLS_IE16 31 /* 16 bit GOT offset for TLS IE. */ -#define R_NIOS2_TLS_LE16 32 /* 16 bit LE TP-relative offset. */ -#define R_NIOS2_TLS_DTPMOD 33 /* Module number. */ -#define R_NIOS2_TLS_DTPREL 34 /* Module-relative offset. */ -#define R_NIOS2_TLS_TPREL 35 /* TP-relative offset. */ -#define R_NIOS2_COPY 36 /* Copy symbol at runtime. */ -#define R_NIOS2_GLOB_DAT 37 /* Create GOT entry. */ -#define R_NIOS2_JUMP_SLOT 38 /* Create PLT entry. */ -#define R_NIOS2_RELATIVE 39 /* Adjust by program base. */ -#define R_NIOS2_GOTOFF 40 /* 16 bit offset to GOT pointer. */ -#define R_NIOS2_CALL26_NOAT 41 /* Direct call in .noat section. */ -#define R_NIOS2_GOT_LO 42 /* %lo() of GOT entry. */ -#define R_NIOS2_GOT_HA 43 /* %hiadj() of GOT entry. */ -#define R_NIOS2_CALL_LO 44 /* %lo() of function GOT entry. */ -#define R_NIOS2_CALL_HA 45 /* %hiadj() of function GOT entry. */ - -/* TILEPro relocations. */ -#define R_TILEPRO_NONE 0 /* No reloc */ -#define R_TILEPRO_32 1 /* Direct 32 bit */ -#define R_TILEPRO_16 2 /* Direct 16 bit */ -#define R_TILEPRO_8 3 /* Direct 8 bit */ -#define R_TILEPRO_32_PCREL 4 /* PC relative 32 bit */ -#define R_TILEPRO_16_PCREL 5 /* PC relative 16 bit */ -#define R_TILEPRO_8_PCREL 6 /* PC relative 8 bit */ -#define R_TILEPRO_LO16 7 /* Low 16 bit */ -#define R_TILEPRO_HI16 8 /* High 16 bit */ -#define R_TILEPRO_HA16 9 /* High 16 bit, adjusted */ -#define R_TILEPRO_COPY 10 /* Copy relocation */ -#define R_TILEPRO_GLOB_DAT 11 /* Create GOT entry */ -#define R_TILEPRO_JMP_SLOT 12 /* Create PLT entry */ -#define R_TILEPRO_RELATIVE 13 /* Adjust by program base */ -#define R_TILEPRO_BROFF_X1 14 /* X1 pipe branch offset */ -#define R_TILEPRO_JOFFLONG_X1 15 /* X1 pipe jump offset */ -#define R_TILEPRO_JOFFLONG_X1_PLT 16 /* X1 pipe jump offset to PLT */ -#define R_TILEPRO_IMM8_X0 17 /* X0 pipe 8-bit */ -#define R_TILEPRO_IMM8_Y0 18 /* Y0 pipe 8-bit */ -#define R_TILEPRO_IMM8_X1 19 /* X1 pipe 8-bit */ -#define R_TILEPRO_IMM8_Y1 20 /* Y1 pipe 8-bit */ -#define R_TILEPRO_MT_IMM15_X1 21 /* X1 pipe mtspr */ -#define R_TILEPRO_MF_IMM15_X1 22 /* X1 pipe mfspr */ -#define R_TILEPRO_IMM16_X0 23 /* X0 pipe 16-bit */ -#define R_TILEPRO_IMM16_X1 24 /* X1 pipe 16-bit */ -#define R_TILEPRO_IMM16_X0_LO 25 /* X0 pipe low 16-bit */ -#define R_TILEPRO_IMM16_X1_LO 26 /* X1 pipe low 16-bit */ -#define R_TILEPRO_IMM16_X0_HI 27 /* X0 pipe high 16-bit */ -#define R_TILEPRO_IMM16_X1_HI 28 /* X1 pipe high 16-bit */ -#define R_TILEPRO_IMM16_X0_HA 29 /* X0 pipe high 16-bit, adjusted */ -#define R_TILEPRO_IMM16_X1_HA 30 /* X1 pipe high 16-bit, adjusted */ -#define R_TILEPRO_IMM16_X0_PCREL 31 /* X0 pipe PC relative 16 bit */ -#define R_TILEPRO_IMM16_X1_PCREL 32 /* X1 pipe PC relative 16 bit */ -#define R_TILEPRO_IMM16_X0_LO_PCREL 33 /* X0 pipe PC relative low 16 bit */ -#define R_TILEPRO_IMM16_X1_LO_PCREL 34 /* X1 pipe PC relative low 16 bit */ -#define R_TILEPRO_IMM16_X0_HI_PCREL 35 /* X0 pipe PC relative high 16 bit */ -#define R_TILEPRO_IMM16_X1_HI_PCREL 36 /* X1 pipe PC relative high 16 bit */ -#define R_TILEPRO_IMM16_X0_HA_PCREL 37 /* X0 pipe PC relative ha() 16 bit */ -#define R_TILEPRO_IMM16_X1_HA_PCREL 38 /* X1 pipe PC relative ha() 16 bit */ -#define R_TILEPRO_IMM16_X0_GOT 39 /* X0 pipe 16-bit GOT offset */ -#define R_TILEPRO_IMM16_X1_GOT 40 /* X1 pipe 16-bit GOT offset */ -#define R_TILEPRO_IMM16_X0_GOT_LO 41 /* X0 pipe low 16-bit GOT offset */ -#define R_TILEPRO_IMM16_X1_GOT_LO 42 /* X1 pipe low 16-bit GOT offset */ -#define R_TILEPRO_IMM16_X0_GOT_HI 43 /* X0 pipe high 16-bit GOT offset */ -#define R_TILEPRO_IMM16_X1_GOT_HI 44 /* X1 pipe high 16-bit GOT offset */ -#define R_TILEPRO_IMM16_X0_GOT_HA 45 /* X0 pipe ha() 16-bit GOT offset */ -#define R_TILEPRO_IMM16_X1_GOT_HA 46 /* X1 pipe ha() 16-bit GOT offset */ -#define R_TILEPRO_MMSTART_X0 47 /* X0 pipe mm "start" */ -#define R_TILEPRO_MMEND_X0 48 /* X0 pipe mm "end" */ -#define R_TILEPRO_MMSTART_X1 49 /* X1 pipe mm "start" */ -#define R_TILEPRO_MMEND_X1 50 /* X1 pipe mm "end" */ -#define R_TILEPRO_SHAMT_X0 51 /* X0 pipe shift amount */ -#define R_TILEPRO_SHAMT_X1 52 /* X1 pipe shift amount */ -#define R_TILEPRO_SHAMT_Y0 53 /* Y0 pipe shift amount */ -#define R_TILEPRO_SHAMT_Y1 54 /* Y1 pipe shift amount */ -#define R_TILEPRO_DEST_IMM8_X1 55 /* X1 pipe destination 8-bit */ -/* Relocs 56-59 are currently not defined. */ -#define R_TILEPRO_TLS_GD_CALL 60 /* "jal" for TLS GD */ -#define R_TILEPRO_IMM8_X0_TLS_GD_ADD 61 /* X0 pipe "addi" for TLS GD */ -#define R_TILEPRO_IMM8_X1_TLS_GD_ADD 62 /* X1 pipe "addi" for TLS GD */ -#define R_TILEPRO_IMM8_Y0_TLS_GD_ADD 63 /* Y0 pipe "addi" for TLS GD */ -#define R_TILEPRO_IMM8_Y1_TLS_GD_ADD 64 /* Y1 pipe "addi" for TLS GD */ -#define R_TILEPRO_TLS_IE_LOAD 65 /* "lw_tls" for TLS IE */ -#define R_TILEPRO_IMM16_X0_TLS_GD 66 /* X0 pipe 16-bit TLS GD offset */ -#define R_TILEPRO_IMM16_X1_TLS_GD 67 /* X1 pipe 16-bit TLS GD offset */ -#define R_TILEPRO_IMM16_X0_TLS_GD_LO 68 /* X0 pipe low 16-bit TLS GD offset */ -#define R_TILEPRO_IMM16_X1_TLS_GD_LO 69 /* X1 pipe low 16-bit TLS GD offset */ -#define R_TILEPRO_IMM16_X0_TLS_GD_HI 70 /* X0 pipe high 16-bit TLS GD offset */ -#define R_TILEPRO_IMM16_X1_TLS_GD_HI 71 /* X1 pipe high 16-bit TLS GD offset */ -#define R_TILEPRO_IMM16_X0_TLS_GD_HA 72 /* X0 pipe ha() 16-bit TLS GD offset */ -#define R_TILEPRO_IMM16_X1_TLS_GD_HA 73 /* X1 pipe ha() 16-bit TLS GD offset */ -#define R_TILEPRO_IMM16_X0_TLS_IE 74 /* X0 pipe 16-bit TLS IE offset */ -#define R_TILEPRO_IMM16_X1_TLS_IE 75 /* X1 pipe 16-bit TLS IE offset */ -#define R_TILEPRO_IMM16_X0_TLS_IE_LO 76 /* X0 pipe low 16-bit TLS IE offset */ -#define R_TILEPRO_IMM16_X1_TLS_IE_LO 77 /* X1 pipe low 16-bit TLS IE offset */ -#define R_TILEPRO_IMM16_X0_TLS_IE_HI 78 /* X0 pipe high 16-bit TLS IE offset */ -#define R_TILEPRO_IMM16_X1_TLS_IE_HI 79 /* X1 pipe high 16-bit TLS IE offset */ -#define R_TILEPRO_IMM16_X0_TLS_IE_HA 80 /* X0 pipe ha() 16-bit TLS IE offset */ -#define R_TILEPRO_IMM16_X1_TLS_IE_HA 81 /* X1 pipe ha() 16-bit TLS IE offset */ -#define R_TILEPRO_TLS_DTPMOD32 82 /* ID of module containing symbol */ -#define R_TILEPRO_TLS_DTPOFF32 83 /* Offset in TLS block */ -#define R_TILEPRO_TLS_TPOFF32 84 /* Offset in static TLS block */ -#define R_TILEPRO_IMM16_X0_TLS_LE 85 /* X0 pipe 16-bit TLS LE offset */ -#define R_TILEPRO_IMM16_X1_TLS_LE 86 /* X1 pipe 16-bit TLS LE offset */ -#define R_TILEPRO_IMM16_X0_TLS_LE_LO 87 /* X0 pipe low 16-bit TLS LE offset */ -#define R_TILEPRO_IMM16_X1_TLS_LE_LO 88 /* X1 pipe low 16-bit TLS LE offset */ -#define R_TILEPRO_IMM16_X0_TLS_LE_HI 89 /* X0 pipe high 16-bit TLS LE offset */ -#define R_TILEPRO_IMM16_X1_TLS_LE_HI 90 /* X1 pipe high 16-bit TLS LE offset */ -#define R_TILEPRO_IMM16_X0_TLS_LE_HA 91 /* X0 pipe ha() 16-bit TLS LE offset */ -#define R_TILEPRO_IMM16_X1_TLS_LE_HA 92 /* X1 pipe ha() 16-bit TLS LE offset */ - -#define R_TILEPRO_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ -#define R_TILEPRO_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ - -#define R_TILEPRO_NUM 130 - - -/* TILE-Gx relocations. */ -#define R_TILEGX_NONE 0 /* No reloc */ -#define R_TILEGX_64 1 /* Direct 64 bit */ -#define R_TILEGX_32 2 /* Direct 32 bit */ -#define R_TILEGX_16 3 /* Direct 16 bit */ -#define R_TILEGX_8 4 /* Direct 8 bit */ -#define R_TILEGX_64_PCREL 5 /* PC relative 64 bit */ -#define R_TILEGX_32_PCREL 6 /* PC relative 32 bit */ -#define R_TILEGX_16_PCREL 7 /* PC relative 16 bit */ -#define R_TILEGX_8_PCREL 8 /* PC relative 8 bit */ -#define R_TILEGX_HW0 9 /* hword 0 16-bit */ -#define R_TILEGX_HW1 10 /* hword 1 16-bit */ -#define R_TILEGX_HW2 11 /* hword 2 16-bit */ -#define R_TILEGX_HW3 12 /* hword 3 16-bit */ -#define R_TILEGX_HW0_LAST 13 /* last hword 0 16-bit */ -#define R_TILEGX_HW1_LAST 14 /* last hword 1 16-bit */ -#define R_TILEGX_HW2_LAST 15 /* last hword 2 16-bit */ -#define R_TILEGX_COPY 16 /* Copy relocation */ -#define R_TILEGX_GLOB_DAT 17 /* Create GOT entry */ -#define R_TILEGX_JMP_SLOT 18 /* Create PLT entry */ -#define R_TILEGX_RELATIVE 19 /* Adjust by program base */ -#define R_TILEGX_BROFF_X1 20 /* X1 pipe branch offset */ -#define R_TILEGX_JUMPOFF_X1 21 /* X1 pipe jump offset */ -#define R_TILEGX_JUMPOFF_X1_PLT 22 /* X1 pipe jump offset to PLT */ -#define R_TILEGX_IMM8_X0 23 /* X0 pipe 8-bit */ -#define R_TILEGX_IMM8_Y0 24 /* Y0 pipe 8-bit */ -#define R_TILEGX_IMM8_X1 25 /* X1 pipe 8-bit */ -#define R_TILEGX_IMM8_Y1 26 /* Y1 pipe 8-bit */ -#define R_TILEGX_DEST_IMM8_X1 27 /* X1 pipe destination 8-bit */ -#define R_TILEGX_MT_IMM14_X1 28 /* X1 pipe mtspr */ -#define R_TILEGX_MF_IMM14_X1 29 /* X1 pipe mfspr */ -#define R_TILEGX_MMSTART_X0 30 /* X0 pipe mm "start" */ -#define R_TILEGX_MMEND_X0 31 /* X0 pipe mm "end" */ -#define R_TILEGX_SHAMT_X0 32 /* X0 pipe shift amount */ -#define R_TILEGX_SHAMT_X1 33 /* X1 pipe shift amount */ -#define R_TILEGX_SHAMT_Y0 34 /* Y0 pipe shift amount */ -#define R_TILEGX_SHAMT_Y1 35 /* Y1 pipe shift amount */ -#define R_TILEGX_IMM16_X0_HW0 36 /* X0 pipe hword 0 */ -#define R_TILEGX_IMM16_X1_HW0 37 /* X1 pipe hword 0 */ -#define R_TILEGX_IMM16_X0_HW1 38 /* X0 pipe hword 1 */ -#define R_TILEGX_IMM16_X1_HW1 39 /* X1 pipe hword 1 */ -#define R_TILEGX_IMM16_X0_HW2 40 /* X0 pipe hword 2 */ -#define R_TILEGX_IMM16_X1_HW2 41 /* X1 pipe hword 2 */ -#define R_TILEGX_IMM16_X0_HW3 42 /* X0 pipe hword 3 */ -#define R_TILEGX_IMM16_X1_HW3 43 /* X1 pipe hword 3 */ -#define R_TILEGX_IMM16_X0_HW0_LAST 44 /* X0 pipe last hword 0 */ -#define R_TILEGX_IMM16_X1_HW0_LAST 45 /* X1 pipe last hword 0 */ -#define R_TILEGX_IMM16_X0_HW1_LAST 46 /* X0 pipe last hword 1 */ -#define R_TILEGX_IMM16_X1_HW1_LAST 47 /* X1 pipe last hword 1 */ -#define R_TILEGX_IMM16_X0_HW2_LAST 48 /* X0 pipe last hword 2 */ -#define R_TILEGX_IMM16_X1_HW2_LAST 49 /* X1 pipe last hword 2 */ -#define R_TILEGX_IMM16_X0_HW0_PCREL 50 /* X0 pipe PC relative hword 0 */ -#define R_TILEGX_IMM16_X1_HW0_PCREL 51 /* X1 pipe PC relative hword 0 */ -#define R_TILEGX_IMM16_X0_HW1_PCREL 52 /* X0 pipe PC relative hword 1 */ -#define R_TILEGX_IMM16_X1_HW1_PCREL 53 /* X1 pipe PC relative hword 1 */ -#define R_TILEGX_IMM16_X0_HW2_PCREL 54 /* X0 pipe PC relative hword 2 */ -#define R_TILEGX_IMM16_X1_HW2_PCREL 55 /* X1 pipe PC relative hword 2 */ -#define R_TILEGX_IMM16_X0_HW3_PCREL 56 /* X0 pipe PC relative hword 3 */ -#define R_TILEGX_IMM16_X1_HW3_PCREL 57 /* X1 pipe PC relative hword 3 */ -#define R_TILEGX_IMM16_X0_HW0_LAST_PCREL 58 /* X0 pipe PC-rel last hword 0 */ -#define R_TILEGX_IMM16_X1_HW0_LAST_PCREL 59 /* X1 pipe PC-rel last hword 0 */ -#define R_TILEGX_IMM16_X0_HW1_LAST_PCREL 60 /* X0 pipe PC-rel last hword 1 */ -#define R_TILEGX_IMM16_X1_HW1_LAST_PCREL 61 /* X1 pipe PC-rel last hword 1 */ -#define R_TILEGX_IMM16_X0_HW2_LAST_PCREL 62 /* X0 pipe PC-rel last hword 2 */ -#define R_TILEGX_IMM16_X1_HW2_LAST_PCREL 63 /* X1 pipe PC-rel last hword 2 */ -#define R_TILEGX_IMM16_X0_HW0_GOT 64 /* X0 pipe hword 0 GOT offset */ -#define R_TILEGX_IMM16_X1_HW0_GOT 65 /* X1 pipe hword 0 GOT offset */ -#define R_TILEGX_IMM16_X0_HW0_PLT_PCREL 66 /* X0 pipe PC-rel PLT hword 0 */ -#define R_TILEGX_IMM16_X1_HW0_PLT_PCREL 67 /* X1 pipe PC-rel PLT hword 0 */ -#define R_TILEGX_IMM16_X0_HW1_PLT_PCREL 68 /* X0 pipe PC-rel PLT hword 1 */ -#define R_TILEGX_IMM16_X1_HW1_PLT_PCREL 69 /* X1 pipe PC-rel PLT hword 1 */ -#define R_TILEGX_IMM16_X0_HW2_PLT_PCREL 70 /* X0 pipe PC-rel PLT hword 2 */ -#define R_TILEGX_IMM16_X1_HW2_PLT_PCREL 71 /* X1 pipe PC-rel PLT hword 2 */ -#define R_TILEGX_IMM16_X0_HW0_LAST_GOT 72 /* X0 pipe last hword 0 GOT offset */ -#define R_TILEGX_IMM16_X1_HW0_LAST_GOT 73 /* X1 pipe last hword 0 GOT offset */ -#define R_TILEGX_IMM16_X0_HW1_LAST_GOT 74 /* X0 pipe last hword 1 GOT offset */ -#define R_TILEGX_IMM16_X1_HW1_LAST_GOT 75 /* X1 pipe last hword 1 GOT offset */ -#define R_TILEGX_IMM16_X0_HW3_PLT_PCREL 76 /* X0 pipe PC-rel PLT hword 3 */ -#define R_TILEGX_IMM16_X1_HW3_PLT_PCREL 77 /* X1 pipe PC-rel PLT hword 3 */ -#define R_TILEGX_IMM16_X0_HW0_TLS_GD 78 /* X0 pipe hword 0 TLS GD offset */ -#define R_TILEGX_IMM16_X1_HW0_TLS_GD 79 /* X1 pipe hword 0 TLS GD offset */ -#define R_TILEGX_IMM16_X0_HW0_TLS_LE 80 /* X0 pipe hword 0 TLS LE offset */ -#define R_TILEGX_IMM16_X1_HW0_TLS_LE 81 /* X1 pipe hword 0 TLS LE offset */ -#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE 82 /* X0 pipe last hword 0 LE off */ -#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE 83 /* X1 pipe last hword 0 LE off */ -#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE 84 /* X0 pipe last hword 1 LE off */ -#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE 85 /* X1 pipe last hword 1 LE off */ -#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD 86 /* X0 pipe last hword 0 GD off */ -#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD 87 /* X1 pipe last hword 0 GD off */ -#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD 88 /* X0 pipe last hword 1 GD off */ -#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD 89 /* X1 pipe last hword 1 GD off */ -/* Relocs 90-91 are currently not defined. */ -#define R_TILEGX_IMM16_X0_HW0_TLS_IE 92 /* X0 pipe hword 0 TLS IE offset */ -#define R_TILEGX_IMM16_X1_HW0_TLS_IE 93 /* X1 pipe hword 0 TLS IE offset */ -#define R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL 94 /* X0 pipe PC-rel PLT last hword 0 */ -#define R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL 95 /* X1 pipe PC-rel PLT last hword 0 */ -#define R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL 96 /* X0 pipe PC-rel PLT last hword 1 */ -#define R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL 97 /* X1 pipe PC-rel PLT last hword 1 */ -#define R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL 98 /* X0 pipe PC-rel PLT last hword 2 */ -#define R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL 99 /* X1 pipe PC-rel PLT last hword 2 */ -#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE 100 /* X0 pipe last hword 0 IE off */ -#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE 101 /* X1 pipe last hword 0 IE off */ -#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE 102 /* X0 pipe last hword 1 IE off */ -#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE 103 /* X1 pipe last hword 1 IE off */ -/* Relocs 104-105 are currently not defined. */ -#define R_TILEGX_TLS_DTPMOD64 106 /* 64-bit ID of symbol's module */ -#define R_TILEGX_TLS_DTPOFF64 107 /* 64-bit offset in TLS block */ -#define R_TILEGX_TLS_TPOFF64 108 /* 64-bit offset in static TLS block */ -#define R_TILEGX_TLS_DTPMOD32 109 /* 32-bit ID of symbol's module */ -#define R_TILEGX_TLS_DTPOFF32 110 /* 32-bit offset in TLS block */ -#define R_TILEGX_TLS_TPOFF32 111 /* 32-bit offset in static TLS block */ -#define R_TILEGX_TLS_GD_CALL 112 /* "jal" for TLS GD */ -#define R_TILEGX_IMM8_X0_TLS_GD_ADD 113 /* X0 pipe "addi" for TLS GD */ -#define R_TILEGX_IMM8_X1_TLS_GD_ADD 114 /* X1 pipe "addi" for TLS GD */ -#define R_TILEGX_IMM8_Y0_TLS_GD_ADD 115 /* Y0 pipe "addi" for TLS GD */ -#define R_TILEGX_IMM8_Y1_TLS_GD_ADD 116 /* Y1 pipe "addi" for TLS GD */ -#define R_TILEGX_TLS_IE_LOAD 117 /* "ld_tls" for TLS IE */ -#define R_TILEGX_IMM8_X0_TLS_ADD 118 /* X0 pipe "addi" for TLS GD/IE */ -#define R_TILEGX_IMM8_X1_TLS_ADD 119 /* X1 pipe "addi" for TLS GD/IE */ -#define R_TILEGX_IMM8_Y0_TLS_ADD 120 /* Y0 pipe "addi" for TLS GD/IE */ -#define R_TILEGX_IMM8_Y1_TLS_ADD 121 /* Y1 pipe "addi" for TLS GD/IE */ - -#define R_TILEGX_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ -#define R_TILEGX_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ - -#define R_TILEGX_NUM 130 - -/* RISC-V ELF Flags */ -#define EF_RISCV_RVC 0x0001 -#define EF_RISCV_FLOAT_ABI 0x0006 -#define EF_RISCV_FLOAT_ABI_SOFT 0x0000 -#define EF_RISCV_FLOAT_ABI_SINGLE 0x0002 -#define EF_RISCV_FLOAT_ABI_DOUBLE 0x0004 -#define EF_RISCV_FLOAT_ABI_QUAD 0x0006 - -/* RISC-V relocations. */ -#define R_RISCV_NONE 0 -#define R_RISCV_32 1 -#define R_RISCV_64 2 -#define R_RISCV_RELATIVE 3 -#define R_RISCV_COPY 4 -#define R_RISCV_JUMP_SLOT 5 -#define R_RISCV_TLS_DTPMOD32 6 -#define R_RISCV_TLS_DTPMOD64 7 -#define R_RISCV_TLS_DTPREL32 8 -#define R_RISCV_TLS_DTPREL64 9 -#define R_RISCV_TLS_TPREL32 10 -#define R_RISCV_TLS_TPREL64 11 -#define R_RISCV_BRANCH 16 -#define R_RISCV_JAL 17 -#define R_RISCV_CALL 18 -#define R_RISCV_CALL_PLT 19 -#define R_RISCV_GOT_HI20 20 -#define R_RISCV_TLS_GOT_HI20 21 -#define R_RISCV_TLS_GD_HI20 22 -#define R_RISCV_PCREL_HI20 23 -#define R_RISCV_PCREL_LO12_I 24 -#define R_RISCV_PCREL_LO12_S 25 -#define R_RISCV_HI20 26 -#define R_RISCV_LO12_I 27 -#define R_RISCV_LO12_S 28 -#define R_RISCV_TPREL_HI20 29 -#define R_RISCV_TPREL_LO12_I 30 -#define R_RISCV_TPREL_LO12_S 31 -#define R_RISCV_TPREL_ADD 32 -#define R_RISCV_ADD8 33 -#define R_RISCV_ADD16 34 -#define R_RISCV_ADD32 35 -#define R_RISCV_ADD64 36 -#define R_RISCV_SUB8 37 -#define R_RISCV_SUB16 38 -#define R_RISCV_SUB32 39 -#define R_RISCV_SUB64 40 -#define R_RISCV_GNU_VTINHERIT 41 -#define R_RISCV_GNU_VTENTRY 42 -#define R_RISCV_ALIGN 43 -#define R_RISCV_RVC_BRANCH 44 -#define R_RISCV_RVC_JUMP 45 -#define R_RISCV_RVC_LUI 46 -#define R_RISCV_GPREL_I 47 -#define R_RISCV_GPREL_S 48 -#define R_RISCV_TPREL_I 49 -#define R_RISCV_TPREL_S 50 -#define R_RISCV_RELAX 51 -#define R_RISCV_SUB6 52 -#define R_RISCV_SET6 53 -#define R_RISCV_SET8 54 -#define R_RISCV_SET16 55 -#define R_RISCV_SET32 56 -#define R_RISCV_32_PCREL 57 -#define R_RISCV_IRELATIVE 58 - -#define R_RISCV_NUM 59 - -/* BPF specific declarations. */ - -#define R_BPF_NONE 0 /* No reloc */ -#define R_BPF_64_64 1 -#define R_BPF_64_32 10 - -/* Imagination Meta specific relocations. */ - -#define R_METAG_HIADDR16 0 -#define R_METAG_LOADDR16 1 -#define R_METAG_ADDR32 2 /* 32bit absolute address */ -#define R_METAG_NONE 3 /* No reloc */ -#define R_METAG_RELBRANCH 4 -#define R_METAG_GETSETOFF 5 - -/* Backward compatibility */ -#define R_METAG_REG32OP1 6 -#define R_METAG_REG32OP2 7 -#define R_METAG_REG32OP3 8 -#define R_METAG_REG16OP1 9 -#define R_METAG_REG16OP2 10 -#define R_METAG_REG16OP3 11 -#define R_METAG_REG32OP4 12 - -#define R_METAG_HIOG 13 -#define R_METAG_LOOG 14 - -#define R_METAG_REL8 15 -#define R_METAG_REL16 16 - -/* GNU */ -#define R_METAG_GNU_VTINHERIT 30 -#define R_METAG_GNU_VTENTRY 31 - -/* PIC relocations */ -#define R_METAG_HI16_GOTOFF 32 -#define R_METAG_LO16_GOTOFF 33 -#define R_METAG_GETSET_GOTOFF 34 -#define R_METAG_GETSET_GOT 35 -#define R_METAG_HI16_GOTPC 36 -#define R_METAG_LO16_GOTPC 37 -#define R_METAG_HI16_PLT 38 -#define R_METAG_LO16_PLT 39 -#define R_METAG_RELBRANCH_PLT 40 -#define R_METAG_GOTOFF 41 -#define R_METAG_PLT 42 -#define R_METAG_COPY 43 -#define R_METAG_JMP_SLOT 44 -#define R_METAG_RELATIVE 45 -#define R_METAG_GLOB_DAT 46 - -/* TLS relocations */ -#define R_METAG_TLS_GD 47 -#define R_METAG_TLS_LDM 48 -#define R_METAG_TLS_LDO_HI16 49 -#define R_METAG_TLS_LDO_LO16 50 -#define R_METAG_TLS_LDO 51 -#define R_METAG_TLS_IE 52 -#define R_METAG_TLS_IENONPIC 53 -#define R_METAG_TLS_IENONPIC_HI16 54 -#define R_METAG_TLS_IENONPIC_LO16 55 -#define R_METAG_TLS_TPOFF 56 -#define R_METAG_TLS_DTPMOD 57 -#define R_METAG_TLS_DTPOFF 58 -#define R_METAG_TLS_LE 59 -#define R_METAG_TLS_LE_HI16 60 -#define R_METAG_TLS_LE_LO16 61 - -/* NDS32 relocations. */ -#define R_NDS32_NONE 0 -#define R_NDS32_32_RELA 20 -#define R_NDS32_COPY 39 -#define R_NDS32_GLOB_DAT 40 -#define R_NDS32_JMP_SLOT 41 -#define R_NDS32_RELATIVE 42 -#define R_NDS32_TLS_TPOFF 102 -#define R_NDS32_TLS_DESC 119 - -/* ARCompact/ARCv2 specific relocs. */ -#define R_ARC_NONE 0x0 -#define R_ARC_8 0x1 -#define R_ARC_16 0x2 -#define R_ARC_24 0x3 -#define R_ARC_32 0x4 -#define R_ARC_B26 0x5 -#define R_ARC_B22_PCREL 0x6 -#define R_ARC_H30 0x7 -#define R_ARC_N8 0x8 -#define R_ARC_N16 0x9 -#define R_ARC_N24 0xA -#define R_ARC_N32 0xB -#define R_ARC_SDA 0xC -#define R_ARC_SECTOFF 0xD -#define R_ARC_S21H_PCREL 0xE -#define R_ARC_S21W_PCREL 0xF -#define R_ARC_S25H_PCREL 0x10 -#define R_ARC_S25W_PCREL 0x11 -#define R_ARC_SDA32 0x12 -#define R_ARC_SDA_LDST 0x13 -#define R_ARC_SDA_LDST1 0x14 -#define R_ARC_SDA_LDST2 0x15 -#define R_ARC_SDA16_LD 0x16 -#define R_ARC_SDA16_LD1 0x17 -#define R_ARC_SDA16_LD2 0x18 -#define R_ARC_S13_PCREL 0x19 -#define R_ARC_W 0x1A -#define R_ARC_32_ME 0x1B -#define R_ARC_N32_ME 0x1C -#define R_ARC_SECTOFF_ME 0x1D -#define R_ARC_SDA32_ME 0x1E -#define R_ARC_W_ME 0x1F -#define R_ARC_H30_ME 0x20 -#define R_ARC_SECTOFF_U8 0x21 -#define R_ARC_SECTOFF_S9 0x22 -#define R_AC_SECTOFF_U8 0x23 -#define R_AC_SECTOFF_U8_1 0x24 -#define R_AC_SECTOFF_U8_2 0x25 -#define R_AC_SECTOFF_S9 0x26 -#define R_AC_SECTOFF_S9_1 0x27 -#define R_AC_SECTOFF_S9_2 0x28 -#define R_ARC_SECTOFF_ME_1 0x29 -#define R_ARC_SECTOFF_ME_2 0x2A -#define R_ARC_SECTOFF_1 0x2B -#define R_ARC_SECTOFF_2 0x2C -#define R_ARC_PC32 0x32 -#define R_ARC_GOTPC32 0x33 -#define R_ARC_PLT32 0x34 -#define R_ARC_COPY 0x35 -#define R_ARC_GLOB_DAT 0x36 -#define R_ARC_JUMP_SLOT 0x37 -#define R_ARC_RELATIVE 0x38 -#define R_ARC_GOTOFF 0x39 -#define R_ARC_GOTPC 0x3A -#define R_ARC_GOT32 0x3B - -#define R_ARC_TLS_DTPMOD 0x42 -#define R_ARC_TLS_DTPOFF 0x43 -#define R_ARC_TLS_TPOFF 0x44 -#define R_ARC_TLS_GD_GOT 0x45 -#define R_ARC_TLS_GD_LD 0x46 -#define R_ARC_TLS_GD_CALL 0x47 -#define R_ARC_TLS_IE_GOT 0x48 -#define R_ARC_TLS_DTPOFF_S9 0x4a -#define R_ARC_TLS_LE_S9 0x4a -#define R_ARC_TLS_LE_32 0x4b - -#endif /* elf.h */ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// SPDX-License-Identifier: LGPL-2.1-or-later + +// Wrapper around elf.h adding definitions that are not available in older +// versions of glibc. + +#ifndef DRGN_ELF_H +#define DRGN_ELF_H + +#include_next + +// Generated by scripts/gen_elf_compat.py. +#ifndef NT_FILE +#define NT_FILE 0x46494c45 +#endif +#ifndef EM_RISCV +#define EM_RISCV 243 +#endif +#ifndef R_RISCV_NONE +#define R_RISCV_NONE 0 +#endif +#ifndef R_RISCV_32 +#define R_RISCV_32 1 +#endif +#ifndef R_RISCV_64 +#define R_RISCV_64 2 +#endif +#ifndef R_RISCV_RELATIVE +#define R_RISCV_RELATIVE 3 +#endif +#ifndef R_RISCV_COPY +#define R_RISCV_COPY 4 +#endif +#ifndef R_RISCV_JUMP_SLOT +#define R_RISCV_JUMP_SLOT 5 +#endif +#ifndef R_RISCV_TLS_DTPMOD32 +#define R_RISCV_TLS_DTPMOD32 6 +#endif +#ifndef R_RISCV_TLS_DTPMOD64 +#define R_RISCV_TLS_DTPMOD64 7 +#endif +#ifndef R_RISCV_TLS_DTPREL32 +#define R_RISCV_TLS_DTPREL32 8 +#endif +#ifndef R_RISCV_TLS_DTPREL64 +#define R_RISCV_TLS_DTPREL64 9 +#endif +#ifndef R_RISCV_TLS_TPREL32 +#define R_RISCV_TLS_TPREL32 10 +#endif +#ifndef R_RISCV_TLS_TPREL64 +#define R_RISCV_TLS_TPREL64 11 +#endif +#ifndef R_RISCV_BRANCH +#define R_RISCV_BRANCH 16 +#endif +#ifndef R_RISCV_JAL +#define R_RISCV_JAL 17 +#endif +#ifndef R_RISCV_CALL +#define R_RISCV_CALL 18 +#endif +#ifndef R_RISCV_CALL_PLT +#define R_RISCV_CALL_PLT 19 +#endif +#ifndef R_RISCV_GOT_HI20 +#define R_RISCV_GOT_HI20 20 +#endif +#ifndef R_RISCV_TLS_GOT_HI20 +#define R_RISCV_TLS_GOT_HI20 21 +#endif +#ifndef R_RISCV_TLS_GD_HI20 +#define R_RISCV_TLS_GD_HI20 22 +#endif +#ifndef R_RISCV_PCREL_HI20 +#define R_RISCV_PCREL_HI20 23 +#endif +#ifndef R_RISCV_PCREL_LO12_I +#define R_RISCV_PCREL_LO12_I 24 +#endif +#ifndef R_RISCV_PCREL_LO12_S +#define R_RISCV_PCREL_LO12_S 25 +#endif +#ifndef R_RISCV_HI20 +#define R_RISCV_HI20 26 +#endif +#ifndef R_RISCV_LO12_I +#define R_RISCV_LO12_I 27 +#endif +#ifndef R_RISCV_LO12_S +#define R_RISCV_LO12_S 28 +#endif +#ifndef R_RISCV_TPREL_HI20 +#define R_RISCV_TPREL_HI20 29 +#endif +#ifndef R_RISCV_TPREL_LO12_I +#define R_RISCV_TPREL_LO12_I 30 +#endif +#ifndef R_RISCV_TPREL_LO12_S +#define R_RISCV_TPREL_LO12_S 31 +#endif +#ifndef R_RISCV_TPREL_ADD +#define R_RISCV_TPREL_ADD 32 +#endif +#ifndef R_RISCV_ADD8 +#define R_RISCV_ADD8 33 +#endif +#ifndef R_RISCV_ADD16 +#define R_RISCV_ADD16 34 +#endif +#ifndef R_RISCV_ADD32 +#define R_RISCV_ADD32 35 +#endif +#ifndef R_RISCV_ADD64 +#define R_RISCV_ADD64 36 +#endif +#ifndef R_RISCV_SUB8 +#define R_RISCV_SUB8 37 +#endif +#ifndef R_RISCV_SUB16 +#define R_RISCV_SUB16 38 +#endif +#ifndef R_RISCV_SUB32 +#define R_RISCV_SUB32 39 +#endif +#ifndef R_RISCV_SUB64 +#define R_RISCV_SUB64 40 +#endif +#ifndef R_RISCV_GNU_VTINHERIT +#define R_RISCV_GNU_VTINHERIT 41 +#endif +#ifndef R_RISCV_GNU_VTENTRY +#define R_RISCV_GNU_VTENTRY 42 +#endif +#ifndef R_RISCV_ALIGN +#define R_RISCV_ALIGN 43 +#endif +#ifndef R_RISCV_RVC_BRANCH +#define R_RISCV_RVC_BRANCH 44 +#endif +#ifndef R_RISCV_RVC_JUMP +#define R_RISCV_RVC_JUMP 45 +#endif +#ifndef R_RISCV_RVC_LUI +#define R_RISCV_RVC_LUI 46 +#endif +#ifndef R_RISCV_GPREL_I +#define R_RISCV_GPREL_I 47 +#endif +#ifndef R_RISCV_GPREL_S +#define R_RISCV_GPREL_S 48 +#endif +#ifndef R_RISCV_TPREL_I +#define R_RISCV_TPREL_I 49 +#endif +#ifndef R_RISCV_TPREL_S +#define R_RISCV_TPREL_S 50 +#endif +#ifndef R_RISCV_RELAX +#define R_RISCV_RELAX 51 +#endif +#ifndef R_RISCV_SUB6 +#define R_RISCV_SUB6 52 +#endif +#ifndef R_RISCV_SET6 +#define R_RISCV_SET6 53 +#endif +#ifndef R_RISCV_SET8 +#define R_RISCV_SET8 54 +#endif +#ifndef R_RISCV_SET16 +#define R_RISCV_SET16 55 +#endif +#ifndef R_RISCV_SET32 +#define R_RISCV_SET32 56 +#endif +#ifndef R_RISCV_32_PCREL +#define R_RISCV_32_PCREL 57 +#endif +#ifndef NT_ARM_PAC_MASK +#define NT_ARM_PAC_MASK 0x406 +#endif + +#endif /* DRGN_ELF_H */ diff --git a/libdrgn/include/elfutils/known-dwarf.h b/libdrgn/include/elfutils/known-dwarf.h deleted file mode 100644 index 4b8a83c9a..000000000 --- a/libdrgn/include/elfutils/known-dwarf.h +++ /dev/null @@ -1,782 +0,0 @@ -/* Generated by config/known-dwarf.awk from libdw/dwarf.h contents. */ - -#define DWARF_ALL_KNOWN_DW_ACCESS \ - DWARF_ONE_KNOWN_DW_ACCESS (private, DW_ACCESS_private) \ - DWARF_ONE_KNOWN_DW_ACCESS (protected, DW_ACCESS_protected) \ - DWARF_ONE_KNOWN_DW_ACCESS (public, DW_ACCESS_public) \ - /* End of DW_ACCESS_*. */ - -#define DWARF_ALL_KNOWN_DW_AT \ - DWARF_ONE_KNOWN_DW_AT (GNU_addr_base, DW_AT_GNU_addr_base) \ - DWARF_ONE_KNOWN_DW_AT (GNU_all_call_sites, DW_AT_GNU_all_call_sites) \ - DWARF_ONE_KNOWN_DW_AT (GNU_all_source_call_sites, DW_AT_GNU_all_source_call_sites) \ - DWARF_ONE_KNOWN_DW_AT (GNU_all_tail_call_sites, DW_AT_GNU_all_tail_call_sites) \ - DWARF_ONE_KNOWN_DW_AT (GNU_bias, DW_AT_GNU_bias) \ - DWARF_ONE_KNOWN_DW_AT (GNU_call_site_data_value, DW_AT_GNU_call_site_data_value) \ - DWARF_ONE_KNOWN_DW_AT (GNU_call_site_target, DW_AT_GNU_call_site_target) \ - DWARF_ONE_KNOWN_DW_AT (GNU_call_site_target_clobbered, DW_AT_GNU_call_site_target_clobbered) \ - DWARF_ONE_KNOWN_DW_AT (GNU_call_site_value, DW_AT_GNU_call_site_value) \ - DWARF_ONE_KNOWN_DW_AT (GNU_deleted, DW_AT_GNU_deleted) \ - DWARF_ONE_KNOWN_DW_AT (GNU_denominator, DW_AT_GNU_denominator) \ - DWARF_ONE_KNOWN_DW_AT (GNU_dwo_id, DW_AT_GNU_dwo_id) \ - DWARF_ONE_KNOWN_DW_AT (GNU_dwo_name, DW_AT_GNU_dwo_name) \ - DWARF_ONE_KNOWN_DW_AT (GNU_entry_view, DW_AT_GNU_entry_view) \ - DWARF_ONE_KNOWN_DW_AT (GNU_exclusive_locks_required, DW_AT_GNU_exclusive_locks_required) \ - DWARF_ONE_KNOWN_DW_AT (GNU_guarded, DW_AT_GNU_guarded) \ - DWARF_ONE_KNOWN_DW_AT (GNU_guarded_by, DW_AT_GNU_guarded_by) \ - DWARF_ONE_KNOWN_DW_AT (GNU_locks_excluded, DW_AT_GNU_locks_excluded) \ - DWARF_ONE_KNOWN_DW_AT (GNU_locviews, DW_AT_GNU_locviews) \ - DWARF_ONE_KNOWN_DW_AT (GNU_macros, DW_AT_GNU_macros) \ - DWARF_ONE_KNOWN_DW_AT (GNU_numerator, DW_AT_GNU_numerator) \ - DWARF_ONE_KNOWN_DW_AT (GNU_odr_signature, DW_AT_GNU_odr_signature) \ - DWARF_ONE_KNOWN_DW_AT (GNU_pt_guarded, DW_AT_GNU_pt_guarded) \ - DWARF_ONE_KNOWN_DW_AT (GNU_pt_guarded_by, DW_AT_GNU_pt_guarded_by) \ - DWARF_ONE_KNOWN_DW_AT (GNU_pubnames, DW_AT_GNU_pubnames) \ - DWARF_ONE_KNOWN_DW_AT (GNU_pubtypes, DW_AT_GNU_pubtypes) \ - DWARF_ONE_KNOWN_DW_AT (GNU_ranges_base, DW_AT_GNU_ranges_base) \ - DWARF_ONE_KNOWN_DW_AT (GNU_shared_locks_required, DW_AT_GNU_shared_locks_required) \ - DWARF_ONE_KNOWN_DW_AT (GNU_tail_call, DW_AT_GNU_tail_call) \ - DWARF_ONE_KNOWN_DW_AT (GNU_template_name, DW_AT_GNU_template_name) \ - DWARF_ONE_KNOWN_DW_AT (GNU_vector, DW_AT_GNU_vector) \ - DWARF_ONE_KNOWN_DW_AT (MIPS_abstract_name, DW_AT_MIPS_abstract_name) \ - DWARF_ONE_KNOWN_DW_AT (MIPS_allocatable_dopetype, DW_AT_MIPS_allocatable_dopetype) \ - DWARF_ONE_KNOWN_DW_AT (MIPS_assumed_shape_dopetype, DW_AT_MIPS_assumed_shape_dopetype) \ - DWARF_ONE_KNOWN_DW_AT (MIPS_assumed_size, DW_AT_MIPS_assumed_size) \ - DWARF_ONE_KNOWN_DW_AT (MIPS_clone_origin, DW_AT_MIPS_clone_origin) \ - DWARF_ONE_KNOWN_DW_AT (MIPS_epilog_begin, DW_AT_MIPS_epilog_begin) \ - DWARF_ONE_KNOWN_DW_AT (MIPS_fde, DW_AT_MIPS_fde) \ - DWARF_ONE_KNOWN_DW_AT (MIPS_has_inlines, DW_AT_MIPS_has_inlines) \ - DWARF_ONE_KNOWN_DW_AT (MIPS_linkage_name, DW_AT_MIPS_linkage_name) \ - DWARF_ONE_KNOWN_DW_AT (MIPS_loop_begin, DW_AT_MIPS_loop_begin) \ - DWARF_ONE_KNOWN_DW_AT (MIPS_loop_unroll_factor, DW_AT_MIPS_loop_unroll_factor) \ - DWARF_ONE_KNOWN_DW_AT (MIPS_ptr_dopetype, DW_AT_MIPS_ptr_dopetype) \ - DWARF_ONE_KNOWN_DW_AT (MIPS_software_pipeline_depth, DW_AT_MIPS_software_pipeline_depth) \ - DWARF_ONE_KNOWN_DW_AT (MIPS_stride, DW_AT_MIPS_stride) \ - DWARF_ONE_KNOWN_DW_AT (MIPS_stride_byte, DW_AT_MIPS_stride_byte) \ - DWARF_ONE_KNOWN_DW_AT (MIPS_stride_elem, DW_AT_MIPS_stride_elem) \ - DWARF_ONE_KNOWN_DW_AT (MIPS_tail_loop_begin, DW_AT_MIPS_tail_loop_begin) \ - DWARF_ONE_KNOWN_DW_AT (abstract_origin, DW_AT_abstract_origin) \ - DWARF_ONE_KNOWN_DW_AT (accessibility, DW_AT_accessibility) \ - DWARF_ONE_KNOWN_DW_AT (addr_base, DW_AT_addr_base) \ - DWARF_ONE_KNOWN_DW_AT (address_class, DW_AT_address_class) \ - DWARF_ONE_KNOWN_DW_AT (alignment, DW_AT_alignment) \ - DWARF_ONE_KNOWN_DW_AT (allocated, DW_AT_allocated) \ - DWARF_ONE_KNOWN_DW_AT (artificial, DW_AT_artificial) \ - DWARF_ONE_KNOWN_DW_AT (associated, DW_AT_associated) \ - DWARF_ONE_KNOWN_DW_AT (base_types, DW_AT_base_types) \ - DWARF_ONE_KNOWN_DW_AT (binary_scale, DW_AT_binary_scale) \ - DWARF_ONE_KNOWN_DW_AT (bit_offset, DW_AT_bit_offset) \ - DWARF_ONE_KNOWN_DW_AT (bit_size, DW_AT_bit_size) \ - DWARF_ONE_KNOWN_DW_AT (bit_stride, DW_AT_bit_stride) \ - DWARF_ONE_KNOWN_DW_AT (body_begin, DW_AT_body_begin) \ - DWARF_ONE_KNOWN_DW_AT (body_end, DW_AT_body_end) \ - DWARF_ONE_KNOWN_DW_AT (byte_size, DW_AT_byte_size) \ - DWARF_ONE_KNOWN_DW_AT (byte_stride, DW_AT_byte_stride) \ - DWARF_ONE_KNOWN_DW_AT (call_all_calls, DW_AT_call_all_calls) \ - DWARF_ONE_KNOWN_DW_AT (call_all_source_calls, DW_AT_call_all_source_calls) \ - DWARF_ONE_KNOWN_DW_AT (call_all_tail_calls, DW_AT_call_all_tail_calls) \ - DWARF_ONE_KNOWN_DW_AT (call_column, DW_AT_call_column) \ - DWARF_ONE_KNOWN_DW_AT (call_data_location, DW_AT_call_data_location) \ - DWARF_ONE_KNOWN_DW_AT (call_data_value, DW_AT_call_data_value) \ - DWARF_ONE_KNOWN_DW_AT (call_file, DW_AT_call_file) \ - DWARF_ONE_KNOWN_DW_AT (call_line, DW_AT_call_line) \ - DWARF_ONE_KNOWN_DW_AT (call_origin, DW_AT_call_origin) \ - DWARF_ONE_KNOWN_DW_AT (call_parameter, DW_AT_call_parameter) \ - DWARF_ONE_KNOWN_DW_AT (call_pc, DW_AT_call_pc) \ - DWARF_ONE_KNOWN_DW_AT (call_return_pc, DW_AT_call_return_pc) \ - DWARF_ONE_KNOWN_DW_AT (call_tail_call, DW_AT_call_tail_call) \ - DWARF_ONE_KNOWN_DW_AT (call_target, DW_AT_call_target) \ - DWARF_ONE_KNOWN_DW_AT (call_target_clobbered, DW_AT_call_target_clobbered) \ - DWARF_ONE_KNOWN_DW_AT (call_value, DW_AT_call_value) \ - DWARF_ONE_KNOWN_DW_AT (calling_convention, DW_AT_calling_convention) \ - DWARF_ONE_KNOWN_DW_AT (common_reference, DW_AT_common_reference) \ - DWARF_ONE_KNOWN_DW_AT (comp_dir, DW_AT_comp_dir) \ - DWARF_ONE_KNOWN_DW_AT (const_expr, DW_AT_const_expr) \ - DWARF_ONE_KNOWN_DW_AT (const_value, DW_AT_const_value) \ - DWARF_ONE_KNOWN_DW_AT (containing_type, DW_AT_containing_type) \ - DWARF_ONE_KNOWN_DW_AT (count, DW_AT_count) \ - DWARF_ONE_KNOWN_DW_AT (data_bit_offset, DW_AT_data_bit_offset) \ - DWARF_ONE_KNOWN_DW_AT (data_location, DW_AT_data_location) \ - DWARF_ONE_KNOWN_DW_AT (data_member_location, DW_AT_data_member_location) \ - DWARF_ONE_KNOWN_DW_AT (decimal_scale, DW_AT_decimal_scale) \ - DWARF_ONE_KNOWN_DW_AT (decimal_sign, DW_AT_decimal_sign) \ - DWARF_ONE_KNOWN_DW_AT (decl_column, DW_AT_decl_column) \ - DWARF_ONE_KNOWN_DW_AT (decl_file, DW_AT_decl_file) \ - DWARF_ONE_KNOWN_DW_AT (decl_line, DW_AT_decl_line) \ - DWARF_ONE_KNOWN_DW_AT (declaration, DW_AT_declaration) \ - DWARF_ONE_KNOWN_DW_AT (default_value, DW_AT_default_value) \ - DWARF_ONE_KNOWN_DW_AT (defaulted, DW_AT_defaulted) \ - DWARF_ONE_KNOWN_DW_AT (deleted, DW_AT_deleted) \ - DWARF_ONE_KNOWN_DW_AT (description, DW_AT_description) \ - DWARF_ONE_KNOWN_DW_AT (digit_count, DW_AT_digit_count) \ - DWARF_ONE_KNOWN_DW_AT (discr, DW_AT_discr) \ - DWARF_ONE_KNOWN_DW_AT (discr_list, DW_AT_discr_list) \ - DWARF_ONE_KNOWN_DW_AT (discr_value, DW_AT_discr_value) \ - DWARF_ONE_KNOWN_DW_AT (dwo_name, DW_AT_dwo_name) \ - DWARF_ONE_KNOWN_DW_AT (elemental, DW_AT_elemental) \ - DWARF_ONE_KNOWN_DW_AT (encoding, DW_AT_encoding) \ - DWARF_ONE_KNOWN_DW_AT (endianity, DW_AT_endianity) \ - DWARF_ONE_KNOWN_DW_AT (entry_pc, DW_AT_entry_pc) \ - DWARF_ONE_KNOWN_DW_AT (enum_class, DW_AT_enum_class) \ - DWARF_ONE_KNOWN_DW_AT (explicit, DW_AT_explicit) \ - DWARF_ONE_KNOWN_DW_AT (export_symbols, DW_AT_export_symbols) \ - DWARF_ONE_KNOWN_DW_AT (extension, DW_AT_extension) \ - DWARF_ONE_KNOWN_DW_AT (external, DW_AT_external) \ - DWARF_ONE_KNOWN_DW_AT (frame_base, DW_AT_frame_base) \ - DWARF_ONE_KNOWN_DW_AT (friend, DW_AT_friend) \ - DWARF_ONE_KNOWN_DW_AT (high_pc, DW_AT_high_pc) \ - DWARF_ONE_KNOWN_DW_AT (identifier_case, DW_AT_identifier_case) \ - DWARF_ONE_KNOWN_DW_AT (import, DW_AT_import) \ - DWARF_ONE_KNOWN_DW_AT (inline, DW_AT_inline) \ - DWARF_ONE_KNOWN_DW_AT (is_optional, DW_AT_is_optional) \ - DWARF_ONE_KNOWN_DW_AT (language, DW_AT_language) \ - DWARF_ONE_KNOWN_DW_AT (linkage_name, DW_AT_linkage_name) \ - DWARF_ONE_KNOWN_DW_AT (location, DW_AT_location) \ - DWARF_ONE_KNOWN_DW_AT (loclists_base, DW_AT_loclists_base) \ - DWARF_ONE_KNOWN_DW_AT (low_pc, DW_AT_low_pc) \ - DWARF_ONE_KNOWN_DW_AT (lower_bound, DW_AT_lower_bound) \ - DWARF_ONE_KNOWN_DW_AT (mac_info, DW_AT_mac_info) \ - DWARF_ONE_KNOWN_DW_AT (macro_info, DW_AT_macro_info) \ - DWARF_ONE_KNOWN_DW_AT (macros, DW_AT_macros) \ - DWARF_ONE_KNOWN_DW_AT (main_subprogram, DW_AT_main_subprogram) \ - DWARF_ONE_KNOWN_DW_AT (mutable, DW_AT_mutable) \ - DWARF_ONE_KNOWN_DW_AT (name, DW_AT_name) \ - DWARF_ONE_KNOWN_DW_AT (namelist_item, DW_AT_namelist_item) \ - DWARF_ONE_KNOWN_DW_AT (noreturn, DW_AT_noreturn) \ - DWARF_ONE_KNOWN_DW_AT (object_pointer, DW_AT_object_pointer) \ - DWARF_ONE_KNOWN_DW_AT (ordering, DW_AT_ordering) \ - DWARF_ONE_KNOWN_DW_AT (picture_string, DW_AT_picture_string) \ - DWARF_ONE_KNOWN_DW_AT (priority, DW_AT_priority) \ - DWARF_ONE_KNOWN_DW_AT (producer, DW_AT_producer) \ - DWARF_ONE_KNOWN_DW_AT (prototyped, DW_AT_prototyped) \ - DWARF_ONE_KNOWN_DW_AT (pure, DW_AT_pure) \ - DWARF_ONE_KNOWN_DW_AT (ranges, DW_AT_ranges) \ - DWARF_ONE_KNOWN_DW_AT (rank, DW_AT_rank) \ - DWARF_ONE_KNOWN_DW_AT (recursive, DW_AT_recursive) \ - DWARF_ONE_KNOWN_DW_AT (reference, DW_AT_reference) \ - DWARF_ONE_KNOWN_DW_AT (return_addr, DW_AT_return_addr) \ - DWARF_ONE_KNOWN_DW_AT (rnglists_base, DW_AT_rnglists_base) \ - DWARF_ONE_KNOWN_DW_AT (rvalue_reference, DW_AT_rvalue_reference) \ - DWARF_ONE_KNOWN_DW_AT (segment, DW_AT_segment) \ - DWARF_ONE_KNOWN_DW_AT (sf_names, DW_AT_sf_names) \ - DWARF_ONE_KNOWN_DW_AT (sibling, DW_AT_sibling) \ - DWARF_ONE_KNOWN_DW_AT (signature, DW_AT_signature) \ - DWARF_ONE_KNOWN_DW_AT (small, DW_AT_small) \ - DWARF_ONE_KNOWN_DW_AT (specification, DW_AT_specification) \ - DWARF_ONE_KNOWN_DW_AT (src_coords, DW_AT_src_coords) \ - DWARF_ONE_KNOWN_DW_AT (src_info, DW_AT_src_info) \ - DWARF_ONE_KNOWN_DW_AT (start_scope, DW_AT_start_scope) \ - DWARF_ONE_KNOWN_DW_AT (static_link, DW_AT_static_link) \ - DWARF_ONE_KNOWN_DW_AT (stmt_list, DW_AT_stmt_list) \ - DWARF_ONE_KNOWN_DW_AT (str_offsets_base, DW_AT_str_offsets_base) \ - DWARF_ONE_KNOWN_DW_AT (string_length, DW_AT_string_length) \ - DWARF_ONE_KNOWN_DW_AT (string_length_bit_size, DW_AT_string_length_bit_size) \ - DWARF_ONE_KNOWN_DW_AT (string_length_byte_size, DW_AT_string_length_byte_size) \ - DWARF_ONE_KNOWN_DW_AT (threads_scaled, DW_AT_threads_scaled) \ - DWARF_ONE_KNOWN_DW_AT (trampoline, DW_AT_trampoline) \ - DWARF_ONE_KNOWN_DW_AT (type, DW_AT_type) \ - DWARF_ONE_KNOWN_DW_AT (upper_bound, DW_AT_upper_bound) \ - DWARF_ONE_KNOWN_DW_AT (use_UTF8, DW_AT_use_UTF8) \ - DWARF_ONE_KNOWN_DW_AT (use_location, DW_AT_use_location) \ - DWARF_ONE_KNOWN_DW_AT (variable_parameter, DW_AT_variable_parameter) \ - DWARF_ONE_KNOWN_DW_AT (virtuality, DW_AT_virtuality) \ - DWARF_ONE_KNOWN_DW_AT (visibility, DW_AT_visibility) \ - DWARF_ONE_KNOWN_DW_AT (vtable_elem_location, DW_AT_vtable_elem_location) \ - /* End of DW_AT_*. */ - -#define DWARF_ALL_KNOWN_DW_ATE \ - DWARF_ONE_KNOWN_DW_ATE (ASCII, DW_ATE_ASCII) \ - DWARF_ONE_KNOWN_DW_ATE (UCS, DW_ATE_UCS) \ - DWARF_ONE_KNOWN_DW_ATE (UTF, DW_ATE_UTF) \ - DWARF_ONE_KNOWN_DW_ATE (address, DW_ATE_address) \ - DWARF_ONE_KNOWN_DW_ATE (boolean, DW_ATE_boolean) \ - DWARF_ONE_KNOWN_DW_ATE (complex_float, DW_ATE_complex_float) \ - DWARF_ONE_KNOWN_DW_ATE (decimal_float, DW_ATE_decimal_float) \ - DWARF_ONE_KNOWN_DW_ATE (edited, DW_ATE_edited) \ - DWARF_ONE_KNOWN_DW_ATE (float, DW_ATE_float) \ - DWARF_ONE_KNOWN_DW_ATE (imaginary_float, DW_ATE_imaginary_float) \ - DWARF_ONE_KNOWN_DW_ATE (numeric_string, DW_ATE_numeric_string) \ - DWARF_ONE_KNOWN_DW_ATE (packed_decimal, DW_ATE_packed_decimal) \ - DWARF_ONE_KNOWN_DW_ATE (signed, DW_ATE_signed) \ - DWARF_ONE_KNOWN_DW_ATE (signed_char, DW_ATE_signed_char) \ - DWARF_ONE_KNOWN_DW_ATE (signed_fixed, DW_ATE_signed_fixed) \ - DWARF_ONE_KNOWN_DW_ATE (unsigned, DW_ATE_unsigned) \ - DWARF_ONE_KNOWN_DW_ATE (unsigned_char, DW_ATE_unsigned_char) \ - DWARF_ONE_KNOWN_DW_ATE (unsigned_fixed, DW_ATE_unsigned_fixed) \ - DWARF_ONE_KNOWN_DW_ATE (void, DW_ATE_void) \ - /* End of DW_ATE_*. */ - -#define DWARF_ALL_KNOWN_DW_CC \ - DWARF_ONE_KNOWN_DW_CC (nocall, DW_CC_nocall) \ - DWARF_ONE_KNOWN_DW_CC (normal, DW_CC_normal) \ - DWARF_ONE_KNOWN_DW_CC (pass_by_reference, DW_CC_pass_by_reference) \ - DWARF_ONE_KNOWN_DW_CC (pass_by_value, DW_CC_pass_by_value) \ - DWARF_ONE_KNOWN_DW_CC (program, DW_CC_program) \ - /* End of DW_CC_*. */ - -#define DWARF_ALL_KNOWN_DW_CFA \ - DWARF_ONE_KNOWN_DW_CFA (AARCH64_negate_ra_state, DW_CFA_AARCH64_negate_ra_state) \ - DWARF_ONE_KNOWN_DW_CFA (GNU_args_size, DW_CFA_GNU_args_size) \ - DWARF_ONE_KNOWN_DW_CFA (GNU_negative_offset_extended, DW_CFA_GNU_negative_offset_extended) \ - DWARF_ONE_KNOWN_DW_CFA (GNU_window_save, DW_CFA_GNU_window_save) \ - DWARF_ONE_KNOWN_DW_CFA (MIPS_advance_loc8, DW_CFA_MIPS_advance_loc8) \ - DWARF_ONE_KNOWN_DW_CFA (advance_loc, DW_CFA_advance_loc) \ - DWARF_ONE_KNOWN_DW_CFA (advance_loc1, DW_CFA_advance_loc1) \ - DWARF_ONE_KNOWN_DW_CFA (advance_loc2, DW_CFA_advance_loc2) \ - DWARF_ONE_KNOWN_DW_CFA (advance_loc4, DW_CFA_advance_loc4) \ - DWARF_ONE_KNOWN_DW_CFA (def_cfa, DW_CFA_def_cfa) \ - DWARF_ONE_KNOWN_DW_CFA (def_cfa_expression, DW_CFA_def_cfa_expression) \ - DWARF_ONE_KNOWN_DW_CFA (def_cfa_offset, DW_CFA_def_cfa_offset) \ - DWARF_ONE_KNOWN_DW_CFA (def_cfa_offset_sf, DW_CFA_def_cfa_offset_sf) \ - DWARF_ONE_KNOWN_DW_CFA (def_cfa_register, DW_CFA_def_cfa_register) \ - DWARF_ONE_KNOWN_DW_CFA (def_cfa_sf, DW_CFA_def_cfa_sf) \ - DWARF_ONE_KNOWN_DW_CFA (expression, DW_CFA_expression) \ - DWARF_ONE_KNOWN_DW_CFA (extended, DW_CFA_extended) \ - DWARF_ONE_KNOWN_DW_CFA (nop, DW_CFA_nop) \ - DWARF_ONE_KNOWN_DW_CFA (offset, DW_CFA_offset) \ - DWARF_ONE_KNOWN_DW_CFA (offset_extended, DW_CFA_offset_extended) \ - DWARF_ONE_KNOWN_DW_CFA (offset_extended_sf, DW_CFA_offset_extended_sf) \ - DWARF_ONE_KNOWN_DW_CFA (register, DW_CFA_register) \ - DWARF_ONE_KNOWN_DW_CFA (remember_state, DW_CFA_remember_state) \ - DWARF_ONE_KNOWN_DW_CFA (restore, DW_CFA_restore) \ - DWARF_ONE_KNOWN_DW_CFA (restore_extended, DW_CFA_restore_extended) \ - DWARF_ONE_KNOWN_DW_CFA (restore_state, DW_CFA_restore_state) \ - DWARF_ONE_KNOWN_DW_CFA (same_value, DW_CFA_same_value) \ - DWARF_ONE_KNOWN_DW_CFA (set_loc, DW_CFA_set_loc) \ - DWARF_ONE_KNOWN_DW_CFA (undefined, DW_CFA_undefined) \ - DWARF_ONE_KNOWN_DW_CFA (val_expression, DW_CFA_val_expression) \ - DWARF_ONE_KNOWN_DW_CFA (val_offset, DW_CFA_val_offset) \ - DWARF_ONE_KNOWN_DW_CFA (val_offset_sf, DW_CFA_val_offset_sf) \ - /* End of DW_CFA_*. */ - -#define DWARF_ALL_KNOWN_DW_CHILDREN \ - DWARF_ONE_KNOWN_DW_CHILDREN (no, DW_CHILDREN_no) \ - DWARF_ONE_KNOWN_DW_CHILDREN (yes, DW_CHILDREN_yes) \ - /* End of DW_CHILDREN_*. */ - -#define DWARF_ALL_KNOWN_DW_CIE_ID \ - DWARF_ONE_KNOWN_DW_CIE_ID (32, DW_CIE_ID_32) \ - DWARF_ONE_KNOWN_DW_CIE_ID (64, DW_CIE_ID_64) \ - /* End of DW_CIE_ID_*. */ - -#define DWARF_ALL_KNOWN_DW_DEFAULTED \ - DWARF_ONE_KNOWN_DW_DEFAULTED (in_class, DW_DEFAULTED_in_class) \ - DWARF_ONE_KNOWN_DW_DEFAULTED (no, DW_DEFAULTED_no) \ - DWARF_ONE_KNOWN_DW_DEFAULTED (out_of_class, DW_DEFAULTED_out_of_class) \ - /* End of DW_DEFAULTED_*. */ - -#define DWARF_ALL_KNOWN_DW_DS \ - DWARF_ONE_KNOWN_DW_DS (leading_overpunch, DW_DS_leading_overpunch) \ - DWARF_ONE_KNOWN_DW_DS (leading_separate, DW_DS_leading_separate) \ - DWARF_ONE_KNOWN_DW_DS (trailing_overpunch, DW_DS_trailing_overpunch) \ - DWARF_ONE_KNOWN_DW_DS (trailing_separate, DW_DS_trailing_separate) \ - DWARF_ONE_KNOWN_DW_DS (unsigned, DW_DS_unsigned) \ - /* End of DW_DS_*. */ - -#define DWARF_ALL_KNOWN_DW_DSC \ - DWARF_ONE_KNOWN_DW_DSC (label, DW_DSC_label) \ - DWARF_ONE_KNOWN_DW_DSC (range, DW_DSC_range) \ - /* End of DW_DSC_*. */ - -#define DWARF_ALL_KNOWN_DW_EH_PE \ - DWARF_ONE_KNOWN_DW_EH_PE (absptr, DW_EH_PE_absptr) \ - DWARF_ONE_KNOWN_DW_EH_PE (aligned, DW_EH_PE_aligned) \ - DWARF_ONE_KNOWN_DW_EH_PE (datarel, DW_EH_PE_datarel) \ - DWARF_ONE_KNOWN_DW_EH_PE (funcrel, DW_EH_PE_funcrel) \ - DWARF_ONE_KNOWN_DW_EH_PE (indirect, DW_EH_PE_indirect) \ - DWARF_ONE_KNOWN_DW_EH_PE (omit, DW_EH_PE_omit) \ - DWARF_ONE_KNOWN_DW_EH_PE (pcrel, DW_EH_PE_pcrel) \ - DWARF_ONE_KNOWN_DW_EH_PE (sdata2, DW_EH_PE_sdata2) \ - DWARF_ONE_KNOWN_DW_EH_PE (sdata4, DW_EH_PE_sdata4) \ - DWARF_ONE_KNOWN_DW_EH_PE (sdata8, DW_EH_PE_sdata8) \ - DWARF_ONE_KNOWN_DW_EH_PE (signed, DW_EH_PE_signed) \ - DWARF_ONE_KNOWN_DW_EH_PE (sleb128, DW_EH_PE_sleb128) \ - DWARF_ONE_KNOWN_DW_EH_PE (textrel, DW_EH_PE_textrel) \ - DWARF_ONE_KNOWN_DW_EH_PE (udata2, DW_EH_PE_udata2) \ - DWARF_ONE_KNOWN_DW_EH_PE (udata4, DW_EH_PE_udata4) \ - DWARF_ONE_KNOWN_DW_EH_PE (udata8, DW_EH_PE_udata8) \ - DWARF_ONE_KNOWN_DW_EH_PE (uleb128, DW_EH_PE_uleb128) \ - /* End of DW_EH_PE_*. */ - -#define DWARF_ALL_KNOWN_DW_END \ - DWARF_ONE_KNOWN_DW_END (big, DW_END_big) \ - DWARF_ONE_KNOWN_DW_END (default, DW_END_default) \ - DWARF_ONE_KNOWN_DW_END (little, DW_END_little) \ - /* End of DW_END_*. */ - -#define DWARF_ALL_KNOWN_DW_FORM \ - DWARF_ONE_KNOWN_DW_FORM (GNU_addr_index, DW_FORM_GNU_addr_index) \ - DWARF_ONE_KNOWN_DW_FORM (GNU_ref_alt, DW_FORM_GNU_ref_alt) \ - DWARF_ONE_KNOWN_DW_FORM (GNU_str_index, DW_FORM_GNU_str_index) \ - DWARF_ONE_KNOWN_DW_FORM (GNU_strp_alt, DW_FORM_GNU_strp_alt) \ - DWARF_ONE_KNOWN_DW_FORM (addr, DW_FORM_addr) \ - DWARF_ONE_KNOWN_DW_FORM (addrx, DW_FORM_addrx) \ - DWARF_ONE_KNOWN_DW_FORM (addrx1, DW_FORM_addrx1) \ - DWARF_ONE_KNOWN_DW_FORM (addrx2, DW_FORM_addrx2) \ - DWARF_ONE_KNOWN_DW_FORM (addrx3, DW_FORM_addrx3) \ - DWARF_ONE_KNOWN_DW_FORM (addrx4, DW_FORM_addrx4) \ - DWARF_ONE_KNOWN_DW_FORM (block, DW_FORM_block) \ - DWARF_ONE_KNOWN_DW_FORM (block1, DW_FORM_block1) \ - DWARF_ONE_KNOWN_DW_FORM (block2, DW_FORM_block2) \ - DWARF_ONE_KNOWN_DW_FORM (block4, DW_FORM_block4) \ - DWARF_ONE_KNOWN_DW_FORM (data1, DW_FORM_data1) \ - DWARF_ONE_KNOWN_DW_FORM (data16, DW_FORM_data16) \ - DWARF_ONE_KNOWN_DW_FORM (data2, DW_FORM_data2) \ - DWARF_ONE_KNOWN_DW_FORM (data4, DW_FORM_data4) \ - DWARF_ONE_KNOWN_DW_FORM (data8, DW_FORM_data8) \ - DWARF_ONE_KNOWN_DW_FORM (exprloc, DW_FORM_exprloc) \ - DWARF_ONE_KNOWN_DW_FORM (flag, DW_FORM_flag) \ - DWARF_ONE_KNOWN_DW_FORM (flag_present, DW_FORM_flag_present) \ - DWARF_ONE_KNOWN_DW_FORM (implicit_const, DW_FORM_implicit_const) \ - DWARF_ONE_KNOWN_DW_FORM (indirect, DW_FORM_indirect) \ - DWARF_ONE_KNOWN_DW_FORM (line_strp, DW_FORM_line_strp) \ - DWARF_ONE_KNOWN_DW_FORM (loclistx, DW_FORM_loclistx) \ - DWARF_ONE_KNOWN_DW_FORM (ref1, DW_FORM_ref1) \ - DWARF_ONE_KNOWN_DW_FORM (ref2, DW_FORM_ref2) \ - DWARF_ONE_KNOWN_DW_FORM (ref4, DW_FORM_ref4) \ - DWARF_ONE_KNOWN_DW_FORM (ref8, DW_FORM_ref8) \ - DWARF_ONE_KNOWN_DW_FORM (ref_addr, DW_FORM_ref_addr) \ - DWARF_ONE_KNOWN_DW_FORM (ref_sig8, DW_FORM_ref_sig8) \ - DWARF_ONE_KNOWN_DW_FORM (ref_sup4, DW_FORM_ref_sup4) \ - DWARF_ONE_KNOWN_DW_FORM (ref_sup8, DW_FORM_ref_sup8) \ - DWARF_ONE_KNOWN_DW_FORM (ref_udata, DW_FORM_ref_udata) \ - DWARF_ONE_KNOWN_DW_FORM (rnglistx, DW_FORM_rnglistx) \ - DWARF_ONE_KNOWN_DW_FORM (sdata, DW_FORM_sdata) \ - DWARF_ONE_KNOWN_DW_FORM (sec_offset, DW_FORM_sec_offset) \ - DWARF_ONE_KNOWN_DW_FORM (string, DW_FORM_string) \ - DWARF_ONE_KNOWN_DW_FORM (strp, DW_FORM_strp) \ - DWARF_ONE_KNOWN_DW_FORM (strp_sup, DW_FORM_strp_sup) \ - DWARF_ONE_KNOWN_DW_FORM (strx, DW_FORM_strx) \ - DWARF_ONE_KNOWN_DW_FORM (strx1, DW_FORM_strx1) \ - DWARF_ONE_KNOWN_DW_FORM (strx2, DW_FORM_strx2) \ - DWARF_ONE_KNOWN_DW_FORM (strx3, DW_FORM_strx3) \ - DWARF_ONE_KNOWN_DW_FORM (strx4, DW_FORM_strx4) \ - DWARF_ONE_KNOWN_DW_FORM (udata, DW_FORM_udata) \ - /* End of DW_FORM_*. */ - -#define DWARF_ALL_KNOWN_DW_ID \ - DWARF_ONE_KNOWN_DW_ID (case_insensitive, DW_ID_case_insensitive) \ - DWARF_ONE_KNOWN_DW_ID (case_sensitive, DW_ID_case_sensitive) \ - DWARF_ONE_KNOWN_DW_ID (down_case, DW_ID_down_case) \ - DWARF_ONE_KNOWN_DW_ID (up_case, DW_ID_up_case) \ - /* End of DW_ID_*. */ - -#define DWARF_ALL_KNOWN_DW_INL \ - DWARF_ONE_KNOWN_DW_INL (declared_inlined, DW_INL_declared_inlined) \ - DWARF_ONE_KNOWN_DW_INL (declared_not_inlined, DW_INL_declared_not_inlined) \ - DWARF_ONE_KNOWN_DW_INL (inlined, DW_INL_inlined) \ - DWARF_ONE_KNOWN_DW_INL (not_inlined, DW_INL_not_inlined) \ - /* End of DW_INL_*. */ - -#define DWARF_ALL_KNOWN_DW_LANG \ - DWARF_ONE_KNOWN_DW_LANG (Ada83, DW_LANG_Ada83) \ - DWARF_ONE_KNOWN_DW_LANG (Ada95, DW_LANG_Ada95) \ - DWARF_ONE_KNOWN_DW_LANG (BLISS, DW_LANG_BLISS) \ - DWARF_ONE_KNOWN_DW_LANG (C, DW_LANG_C) \ - DWARF_ONE_KNOWN_DW_LANG (C11, DW_LANG_C11) \ - DWARF_ONE_KNOWN_DW_LANG (C89, DW_LANG_C89) \ - DWARF_ONE_KNOWN_DW_LANG (C99, DW_LANG_C99) \ - DWARF_ONE_KNOWN_DW_LANG (C_plus_plus, DW_LANG_C_plus_plus) \ - DWARF_ONE_KNOWN_DW_LANG (C_plus_plus_03, DW_LANG_C_plus_plus_03) \ - DWARF_ONE_KNOWN_DW_LANG (C_plus_plus_11, DW_LANG_C_plus_plus_11) \ - DWARF_ONE_KNOWN_DW_LANG (C_plus_plus_14, DW_LANG_C_plus_plus_14) \ - DWARF_ONE_KNOWN_DW_LANG (Cobol74, DW_LANG_Cobol74) \ - DWARF_ONE_KNOWN_DW_LANG (Cobol85, DW_LANG_Cobol85) \ - DWARF_ONE_KNOWN_DW_LANG (D, DW_LANG_D) \ - DWARF_ONE_KNOWN_DW_LANG (Dylan, DW_LANG_Dylan) \ - DWARF_ONE_KNOWN_DW_LANG (Fortran03, DW_LANG_Fortran03) \ - DWARF_ONE_KNOWN_DW_LANG (Fortran08, DW_LANG_Fortran08) \ - DWARF_ONE_KNOWN_DW_LANG (Fortran77, DW_LANG_Fortran77) \ - DWARF_ONE_KNOWN_DW_LANG (Fortran90, DW_LANG_Fortran90) \ - DWARF_ONE_KNOWN_DW_LANG (Fortran95, DW_LANG_Fortran95) \ - DWARF_ONE_KNOWN_DW_LANG (Go, DW_LANG_Go) \ - DWARF_ONE_KNOWN_DW_LANG (Haskell, DW_LANG_Haskell) \ - DWARF_ONE_KNOWN_DW_LANG (Java, DW_LANG_Java) \ - DWARF_ONE_KNOWN_DW_LANG (Julia, DW_LANG_Julia) \ - DWARF_ONE_KNOWN_DW_LANG (Mips_Assembler, DW_LANG_Mips_Assembler) \ - DWARF_ONE_KNOWN_DW_LANG (Modula2, DW_LANG_Modula2) \ - DWARF_ONE_KNOWN_DW_LANG (Modula3, DW_LANG_Modula3) \ - DWARF_ONE_KNOWN_DW_LANG (OCaml, DW_LANG_OCaml) \ - DWARF_ONE_KNOWN_DW_LANG (ObjC, DW_LANG_ObjC) \ - DWARF_ONE_KNOWN_DW_LANG (ObjC_plus_plus, DW_LANG_ObjC_plus_plus) \ - DWARF_ONE_KNOWN_DW_LANG (OpenCL, DW_LANG_OpenCL) \ - DWARF_ONE_KNOWN_DW_LANG (PLI, DW_LANG_PLI) \ - DWARF_ONE_KNOWN_DW_LANG (Pascal83, DW_LANG_Pascal83) \ - DWARF_ONE_KNOWN_DW_LANG (Python, DW_LANG_Python) \ - DWARF_ONE_KNOWN_DW_LANG (RenderScript, DW_LANG_RenderScript) \ - DWARF_ONE_KNOWN_DW_LANG (Rust, DW_LANG_Rust) \ - DWARF_ONE_KNOWN_DW_LANG (Swift, DW_LANG_Swift) \ - DWARF_ONE_KNOWN_DW_LANG (UPC, DW_LANG_UPC) \ - /* End of DW_LANG_*. */ - -#define DWARF_ALL_KNOWN_DW_LLE \ - DWARF_ONE_KNOWN_DW_LLE (base_address, DW_LLE_base_address) \ - DWARF_ONE_KNOWN_DW_LLE (base_addressx, DW_LLE_base_addressx) \ - DWARF_ONE_KNOWN_DW_LLE (default_location, DW_LLE_default_location) \ - DWARF_ONE_KNOWN_DW_LLE (end_of_list, DW_LLE_end_of_list) \ - DWARF_ONE_KNOWN_DW_LLE (offset_pair, DW_LLE_offset_pair) \ - DWARF_ONE_KNOWN_DW_LLE (start_end, DW_LLE_start_end) \ - DWARF_ONE_KNOWN_DW_LLE (start_length, DW_LLE_start_length) \ - DWARF_ONE_KNOWN_DW_LLE (startx_endx, DW_LLE_startx_endx) \ - DWARF_ONE_KNOWN_DW_LLE (startx_length, DW_LLE_startx_length) \ - /* End of DW_LLE_*. */ - -#define DWARF_ALL_KNOWN_DW_LLE_GNU \ - DWARF_ONE_KNOWN_DW_LLE_GNU (base_address_selection_entry, DW_LLE_GNU_base_address_selection_entry) \ - DWARF_ONE_KNOWN_DW_LLE_GNU (end_of_list_entry, DW_LLE_GNU_end_of_list_entry) \ - DWARF_ONE_KNOWN_DW_LLE_GNU (start_end_entry, DW_LLE_GNU_start_end_entry) \ - DWARF_ONE_KNOWN_DW_LLE_GNU (start_length_entry, DW_LLE_GNU_start_length_entry) \ - /* End of DW_LLE_GNU_*. */ - -#define DWARF_ALL_KNOWN_DW_LNCT \ - DWARF_ONE_KNOWN_DW_LNCT (MD5, DW_LNCT_MD5) \ - DWARF_ONE_KNOWN_DW_LNCT (directory_index, DW_LNCT_directory_index) \ - DWARF_ONE_KNOWN_DW_LNCT (path, DW_LNCT_path) \ - DWARF_ONE_KNOWN_DW_LNCT (size, DW_LNCT_size) \ - DWARF_ONE_KNOWN_DW_LNCT (timestamp, DW_LNCT_timestamp) \ - /* End of DW_LNCT_*. */ - -#define DWARF_ALL_KNOWN_DW_LNE \ - DWARF_ONE_KNOWN_DW_LNE (define_file, DW_LNE_define_file) \ - DWARF_ONE_KNOWN_DW_LNE (end_sequence, DW_LNE_end_sequence) \ - DWARF_ONE_KNOWN_DW_LNE (set_address, DW_LNE_set_address) \ - DWARF_ONE_KNOWN_DW_LNE (set_discriminator, DW_LNE_set_discriminator) \ - /* End of DW_LNE_*. */ - -#define DWARF_ALL_KNOWN_DW_LNS \ - DWARF_ONE_KNOWN_DW_LNS (advance_line, DW_LNS_advance_line) \ - DWARF_ONE_KNOWN_DW_LNS (advance_pc, DW_LNS_advance_pc) \ - DWARF_ONE_KNOWN_DW_LNS (const_add_pc, DW_LNS_const_add_pc) \ - DWARF_ONE_KNOWN_DW_LNS (copy, DW_LNS_copy) \ - DWARF_ONE_KNOWN_DW_LNS (fixed_advance_pc, DW_LNS_fixed_advance_pc) \ - DWARF_ONE_KNOWN_DW_LNS (negate_stmt, DW_LNS_negate_stmt) \ - DWARF_ONE_KNOWN_DW_LNS (set_basic_block, DW_LNS_set_basic_block) \ - DWARF_ONE_KNOWN_DW_LNS (set_column, DW_LNS_set_column) \ - DWARF_ONE_KNOWN_DW_LNS (set_epilogue_begin, DW_LNS_set_epilogue_begin) \ - DWARF_ONE_KNOWN_DW_LNS (set_file, DW_LNS_set_file) \ - DWARF_ONE_KNOWN_DW_LNS (set_isa, DW_LNS_set_isa) \ - DWARF_ONE_KNOWN_DW_LNS (set_prologue_end, DW_LNS_set_prologue_end) \ - /* End of DW_LNS_*. */ - -#define DWARF_ALL_KNOWN_DW_MACINFO \ - DWARF_ONE_KNOWN_DW_MACINFO (define, DW_MACINFO_define) \ - DWARF_ONE_KNOWN_DW_MACINFO (end_file, DW_MACINFO_end_file) \ - DWARF_ONE_KNOWN_DW_MACINFO (start_file, DW_MACINFO_start_file) \ - DWARF_ONE_KNOWN_DW_MACINFO (undef, DW_MACINFO_undef) \ - DWARF_ONE_KNOWN_DW_MACINFO (vendor_ext, DW_MACINFO_vendor_ext) \ - /* End of DW_MACINFO_*. */ - -#define DWARF_ALL_KNOWN_DW_MACRO \ - DWARF_ONE_KNOWN_DW_MACRO (define, DW_MACRO_define) \ - DWARF_ONE_KNOWN_DW_MACRO (define_strp, DW_MACRO_define_strp) \ - DWARF_ONE_KNOWN_DW_MACRO (define_strx, DW_MACRO_define_strx) \ - DWARF_ONE_KNOWN_DW_MACRO (define_sup, DW_MACRO_define_sup) \ - DWARF_ONE_KNOWN_DW_MACRO (end_file, DW_MACRO_end_file) \ - DWARF_ONE_KNOWN_DW_MACRO (import, DW_MACRO_import) \ - DWARF_ONE_KNOWN_DW_MACRO (import_sup, DW_MACRO_import_sup) \ - DWARF_ONE_KNOWN_DW_MACRO (start_file, DW_MACRO_start_file) \ - DWARF_ONE_KNOWN_DW_MACRO (undef, DW_MACRO_undef) \ - DWARF_ONE_KNOWN_DW_MACRO (undef_strp, DW_MACRO_undef_strp) \ - DWARF_ONE_KNOWN_DW_MACRO (undef_strx, DW_MACRO_undef_strx) \ - DWARF_ONE_KNOWN_DW_MACRO (undef_sup, DW_MACRO_undef_sup) \ - /* End of DW_MACRO_*. */ - -#define DWARF_ALL_KNOWN_DW_OP \ - DWARF_ONE_KNOWN_DW_OP (GNU_addr_index, DW_OP_GNU_addr_index) \ - DWARF_ONE_KNOWN_DW_OP (GNU_const_index, DW_OP_GNU_const_index) \ - DWARF_ONE_KNOWN_DW_OP (GNU_const_type, DW_OP_GNU_const_type) \ - DWARF_ONE_KNOWN_DW_OP (GNU_convert, DW_OP_GNU_convert) \ - DWARF_ONE_KNOWN_DW_OP (GNU_deref_type, DW_OP_GNU_deref_type) \ - DWARF_ONE_KNOWN_DW_OP (GNU_encoded_addr, DW_OP_GNU_encoded_addr) \ - DWARF_ONE_KNOWN_DW_OP (GNU_entry_value, DW_OP_GNU_entry_value) \ - DWARF_ONE_KNOWN_DW_OP (GNU_implicit_pointer, DW_OP_GNU_implicit_pointer) \ - DWARF_ONE_KNOWN_DW_OP (GNU_parameter_ref, DW_OP_GNU_parameter_ref) \ - DWARF_ONE_KNOWN_DW_OP (GNU_push_tls_address, DW_OP_GNU_push_tls_address) \ - DWARF_ONE_KNOWN_DW_OP (GNU_regval_type, DW_OP_GNU_regval_type) \ - DWARF_ONE_KNOWN_DW_OP (GNU_reinterpret, DW_OP_GNU_reinterpret) \ - DWARF_ONE_KNOWN_DW_OP (GNU_uninit, DW_OP_GNU_uninit) \ - DWARF_ONE_KNOWN_DW_OP (GNU_variable_value, DW_OP_GNU_variable_value) \ - DWARF_ONE_KNOWN_DW_OP (abs, DW_OP_abs) \ - DWARF_ONE_KNOWN_DW_OP (addr, DW_OP_addr) \ - DWARF_ONE_KNOWN_DW_OP (addrx, DW_OP_addrx) \ - DWARF_ONE_KNOWN_DW_OP (and, DW_OP_and) \ - DWARF_ONE_KNOWN_DW_OP (bit_piece, DW_OP_bit_piece) \ - DWARF_ONE_KNOWN_DW_OP (bra, DW_OP_bra) \ - DWARF_ONE_KNOWN_DW_OP (breg0, DW_OP_breg0) \ - DWARF_ONE_KNOWN_DW_OP (breg1, DW_OP_breg1) \ - DWARF_ONE_KNOWN_DW_OP (breg10, DW_OP_breg10) \ - DWARF_ONE_KNOWN_DW_OP (breg11, DW_OP_breg11) \ - DWARF_ONE_KNOWN_DW_OP (breg12, DW_OP_breg12) \ - DWARF_ONE_KNOWN_DW_OP (breg13, DW_OP_breg13) \ - DWARF_ONE_KNOWN_DW_OP (breg14, DW_OP_breg14) \ - DWARF_ONE_KNOWN_DW_OP (breg15, DW_OP_breg15) \ - DWARF_ONE_KNOWN_DW_OP (breg16, DW_OP_breg16) \ - DWARF_ONE_KNOWN_DW_OP (breg17, DW_OP_breg17) \ - DWARF_ONE_KNOWN_DW_OP (breg18, DW_OP_breg18) \ - DWARF_ONE_KNOWN_DW_OP (breg19, DW_OP_breg19) \ - DWARF_ONE_KNOWN_DW_OP (breg2, DW_OP_breg2) \ - DWARF_ONE_KNOWN_DW_OP (breg20, DW_OP_breg20) \ - DWARF_ONE_KNOWN_DW_OP (breg21, DW_OP_breg21) \ - DWARF_ONE_KNOWN_DW_OP (breg22, DW_OP_breg22) \ - DWARF_ONE_KNOWN_DW_OP (breg23, DW_OP_breg23) \ - DWARF_ONE_KNOWN_DW_OP (breg24, DW_OP_breg24) \ - DWARF_ONE_KNOWN_DW_OP (breg25, DW_OP_breg25) \ - DWARF_ONE_KNOWN_DW_OP (breg26, DW_OP_breg26) \ - DWARF_ONE_KNOWN_DW_OP (breg27, DW_OP_breg27) \ - DWARF_ONE_KNOWN_DW_OP (breg28, DW_OP_breg28) \ - DWARF_ONE_KNOWN_DW_OP (breg29, DW_OP_breg29) \ - DWARF_ONE_KNOWN_DW_OP (breg3, DW_OP_breg3) \ - DWARF_ONE_KNOWN_DW_OP (breg30, DW_OP_breg30) \ - DWARF_ONE_KNOWN_DW_OP (breg31, DW_OP_breg31) \ - DWARF_ONE_KNOWN_DW_OP (breg4, DW_OP_breg4) \ - DWARF_ONE_KNOWN_DW_OP (breg5, DW_OP_breg5) \ - DWARF_ONE_KNOWN_DW_OP (breg6, DW_OP_breg6) \ - DWARF_ONE_KNOWN_DW_OP (breg7, DW_OP_breg7) \ - DWARF_ONE_KNOWN_DW_OP (breg8, DW_OP_breg8) \ - DWARF_ONE_KNOWN_DW_OP (breg9, DW_OP_breg9) \ - DWARF_ONE_KNOWN_DW_OP (bregx, DW_OP_bregx) \ - DWARF_ONE_KNOWN_DW_OP (call2, DW_OP_call2) \ - DWARF_ONE_KNOWN_DW_OP (call4, DW_OP_call4) \ - DWARF_ONE_KNOWN_DW_OP (call_frame_cfa, DW_OP_call_frame_cfa) \ - DWARF_ONE_KNOWN_DW_OP (call_ref, DW_OP_call_ref) \ - DWARF_ONE_KNOWN_DW_OP (const1s, DW_OP_const1s) \ - DWARF_ONE_KNOWN_DW_OP (const1u, DW_OP_const1u) \ - DWARF_ONE_KNOWN_DW_OP (const2s, DW_OP_const2s) \ - DWARF_ONE_KNOWN_DW_OP (const2u, DW_OP_const2u) \ - DWARF_ONE_KNOWN_DW_OP (const4s, DW_OP_const4s) \ - DWARF_ONE_KNOWN_DW_OP (const4u, DW_OP_const4u) \ - DWARF_ONE_KNOWN_DW_OP (const8s, DW_OP_const8s) \ - DWARF_ONE_KNOWN_DW_OP (const8u, DW_OP_const8u) \ - DWARF_ONE_KNOWN_DW_OP (const_type, DW_OP_const_type) \ - DWARF_ONE_KNOWN_DW_OP (consts, DW_OP_consts) \ - DWARF_ONE_KNOWN_DW_OP (constu, DW_OP_constu) \ - DWARF_ONE_KNOWN_DW_OP (constx, DW_OP_constx) \ - DWARF_ONE_KNOWN_DW_OP (convert, DW_OP_convert) \ - DWARF_ONE_KNOWN_DW_OP (deref, DW_OP_deref) \ - DWARF_ONE_KNOWN_DW_OP (deref_size, DW_OP_deref_size) \ - DWARF_ONE_KNOWN_DW_OP (deref_type, DW_OP_deref_type) \ - DWARF_ONE_KNOWN_DW_OP (div, DW_OP_div) \ - DWARF_ONE_KNOWN_DW_OP (drop, DW_OP_drop) \ - DWARF_ONE_KNOWN_DW_OP (dup, DW_OP_dup) \ - DWARF_ONE_KNOWN_DW_OP (entry_value, DW_OP_entry_value) \ - DWARF_ONE_KNOWN_DW_OP (eq, DW_OP_eq) \ - DWARF_ONE_KNOWN_DW_OP (fbreg, DW_OP_fbreg) \ - DWARF_ONE_KNOWN_DW_OP (form_tls_address, DW_OP_form_tls_address) \ - DWARF_ONE_KNOWN_DW_OP (ge, DW_OP_ge) \ - DWARF_ONE_KNOWN_DW_OP (gt, DW_OP_gt) \ - DWARF_ONE_KNOWN_DW_OP (implicit_pointer, DW_OP_implicit_pointer) \ - DWARF_ONE_KNOWN_DW_OP (implicit_value, DW_OP_implicit_value) \ - DWARF_ONE_KNOWN_DW_OP (le, DW_OP_le) \ - DWARF_ONE_KNOWN_DW_OP (lit0, DW_OP_lit0) \ - DWARF_ONE_KNOWN_DW_OP (lit1, DW_OP_lit1) \ - DWARF_ONE_KNOWN_DW_OP (lit10, DW_OP_lit10) \ - DWARF_ONE_KNOWN_DW_OP (lit11, DW_OP_lit11) \ - DWARF_ONE_KNOWN_DW_OP (lit12, DW_OP_lit12) \ - DWARF_ONE_KNOWN_DW_OP (lit13, DW_OP_lit13) \ - DWARF_ONE_KNOWN_DW_OP (lit14, DW_OP_lit14) \ - DWARF_ONE_KNOWN_DW_OP (lit15, DW_OP_lit15) \ - DWARF_ONE_KNOWN_DW_OP (lit16, DW_OP_lit16) \ - DWARF_ONE_KNOWN_DW_OP (lit17, DW_OP_lit17) \ - DWARF_ONE_KNOWN_DW_OP (lit18, DW_OP_lit18) \ - DWARF_ONE_KNOWN_DW_OP (lit19, DW_OP_lit19) \ - DWARF_ONE_KNOWN_DW_OP (lit2, DW_OP_lit2) \ - DWARF_ONE_KNOWN_DW_OP (lit20, DW_OP_lit20) \ - DWARF_ONE_KNOWN_DW_OP (lit21, DW_OP_lit21) \ - DWARF_ONE_KNOWN_DW_OP (lit22, DW_OP_lit22) \ - DWARF_ONE_KNOWN_DW_OP (lit23, DW_OP_lit23) \ - DWARF_ONE_KNOWN_DW_OP (lit24, DW_OP_lit24) \ - DWARF_ONE_KNOWN_DW_OP (lit25, DW_OP_lit25) \ - DWARF_ONE_KNOWN_DW_OP (lit26, DW_OP_lit26) \ - DWARF_ONE_KNOWN_DW_OP (lit27, DW_OP_lit27) \ - DWARF_ONE_KNOWN_DW_OP (lit28, DW_OP_lit28) \ - DWARF_ONE_KNOWN_DW_OP (lit29, DW_OP_lit29) \ - DWARF_ONE_KNOWN_DW_OP (lit3, DW_OP_lit3) \ - DWARF_ONE_KNOWN_DW_OP (lit30, DW_OP_lit30) \ - DWARF_ONE_KNOWN_DW_OP (lit31, DW_OP_lit31) \ - DWARF_ONE_KNOWN_DW_OP (lit4, DW_OP_lit4) \ - DWARF_ONE_KNOWN_DW_OP (lit5, DW_OP_lit5) \ - DWARF_ONE_KNOWN_DW_OP (lit6, DW_OP_lit6) \ - DWARF_ONE_KNOWN_DW_OP (lit7, DW_OP_lit7) \ - DWARF_ONE_KNOWN_DW_OP (lit8, DW_OP_lit8) \ - DWARF_ONE_KNOWN_DW_OP (lit9, DW_OP_lit9) \ - DWARF_ONE_KNOWN_DW_OP (lt, DW_OP_lt) \ - DWARF_ONE_KNOWN_DW_OP (minus, DW_OP_minus) \ - DWARF_ONE_KNOWN_DW_OP (mod, DW_OP_mod) \ - DWARF_ONE_KNOWN_DW_OP (mul, DW_OP_mul) \ - DWARF_ONE_KNOWN_DW_OP (ne, DW_OP_ne) \ - DWARF_ONE_KNOWN_DW_OP (neg, DW_OP_neg) \ - DWARF_ONE_KNOWN_DW_OP (nop, DW_OP_nop) \ - DWARF_ONE_KNOWN_DW_OP (not, DW_OP_not) \ - DWARF_ONE_KNOWN_DW_OP (or, DW_OP_or) \ - DWARF_ONE_KNOWN_DW_OP (over, DW_OP_over) \ - DWARF_ONE_KNOWN_DW_OP (pick, DW_OP_pick) \ - DWARF_ONE_KNOWN_DW_OP (piece, DW_OP_piece) \ - DWARF_ONE_KNOWN_DW_OP (plus, DW_OP_plus) \ - DWARF_ONE_KNOWN_DW_OP (plus_uconst, DW_OP_plus_uconst) \ - DWARF_ONE_KNOWN_DW_OP (push_object_address, DW_OP_push_object_address) \ - DWARF_ONE_KNOWN_DW_OP (reg0, DW_OP_reg0) \ - DWARF_ONE_KNOWN_DW_OP (reg1, DW_OP_reg1) \ - DWARF_ONE_KNOWN_DW_OP (reg10, DW_OP_reg10) \ - DWARF_ONE_KNOWN_DW_OP (reg11, DW_OP_reg11) \ - DWARF_ONE_KNOWN_DW_OP (reg12, DW_OP_reg12) \ - DWARF_ONE_KNOWN_DW_OP (reg13, DW_OP_reg13) \ - DWARF_ONE_KNOWN_DW_OP (reg14, DW_OP_reg14) \ - DWARF_ONE_KNOWN_DW_OP (reg15, DW_OP_reg15) \ - DWARF_ONE_KNOWN_DW_OP (reg16, DW_OP_reg16) \ - DWARF_ONE_KNOWN_DW_OP (reg17, DW_OP_reg17) \ - DWARF_ONE_KNOWN_DW_OP (reg18, DW_OP_reg18) \ - DWARF_ONE_KNOWN_DW_OP (reg19, DW_OP_reg19) \ - DWARF_ONE_KNOWN_DW_OP (reg2, DW_OP_reg2) \ - DWARF_ONE_KNOWN_DW_OP (reg20, DW_OP_reg20) \ - DWARF_ONE_KNOWN_DW_OP (reg21, DW_OP_reg21) \ - DWARF_ONE_KNOWN_DW_OP (reg22, DW_OP_reg22) \ - DWARF_ONE_KNOWN_DW_OP (reg23, DW_OP_reg23) \ - DWARF_ONE_KNOWN_DW_OP (reg24, DW_OP_reg24) \ - DWARF_ONE_KNOWN_DW_OP (reg25, DW_OP_reg25) \ - DWARF_ONE_KNOWN_DW_OP (reg26, DW_OP_reg26) \ - DWARF_ONE_KNOWN_DW_OP (reg27, DW_OP_reg27) \ - DWARF_ONE_KNOWN_DW_OP (reg28, DW_OP_reg28) \ - DWARF_ONE_KNOWN_DW_OP (reg29, DW_OP_reg29) \ - DWARF_ONE_KNOWN_DW_OP (reg3, DW_OP_reg3) \ - DWARF_ONE_KNOWN_DW_OP (reg30, DW_OP_reg30) \ - DWARF_ONE_KNOWN_DW_OP (reg31, DW_OP_reg31) \ - DWARF_ONE_KNOWN_DW_OP (reg4, DW_OP_reg4) \ - DWARF_ONE_KNOWN_DW_OP (reg5, DW_OP_reg5) \ - DWARF_ONE_KNOWN_DW_OP (reg6, DW_OP_reg6) \ - DWARF_ONE_KNOWN_DW_OP (reg7, DW_OP_reg7) \ - DWARF_ONE_KNOWN_DW_OP (reg8, DW_OP_reg8) \ - DWARF_ONE_KNOWN_DW_OP (reg9, DW_OP_reg9) \ - DWARF_ONE_KNOWN_DW_OP (regval_type, DW_OP_regval_type) \ - DWARF_ONE_KNOWN_DW_OP (regx, DW_OP_regx) \ - DWARF_ONE_KNOWN_DW_OP (reinterpret, DW_OP_reinterpret) \ - DWARF_ONE_KNOWN_DW_OP (rot, DW_OP_rot) \ - DWARF_ONE_KNOWN_DW_OP (shl, DW_OP_shl) \ - DWARF_ONE_KNOWN_DW_OP (shr, DW_OP_shr) \ - DWARF_ONE_KNOWN_DW_OP (shra, DW_OP_shra) \ - DWARF_ONE_KNOWN_DW_OP (skip, DW_OP_skip) \ - DWARF_ONE_KNOWN_DW_OP (stack_value, DW_OP_stack_value) \ - DWARF_ONE_KNOWN_DW_OP (swap, DW_OP_swap) \ - DWARF_ONE_KNOWN_DW_OP (xderef, DW_OP_xderef) \ - DWARF_ONE_KNOWN_DW_OP (xderef_size, DW_OP_xderef_size) \ - DWARF_ONE_KNOWN_DW_OP (xderef_type, DW_OP_xderef_type) \ - DWARF_ONE_KNOWN_DW_OP (xor, DW_OP_xor) \ - /* End of DW_OP_*. */ - -#define DWARF_ALL_KNOWN_DW_ORD \ - DWARF_ONE_KNOWN_DW_ORD (col_major, DW_ORD_col_major) \ - DWARF_ONE_KNOWN_DW_ORD (row_major, DW_ORD_row_major) \ - /* End of DW_ORD_*. */ - -#define DWARF_ALL_KNOWN_DW_RLE \ - DWARF_ONE_KNOWN_DW_RLE (base_address, DW_RLE_base_address) \ - DWARF_ONE_KNOWN_DW_RLE (base_addressx, DW_RLE_base_addressx) \ - DWARF_ONE_KNOWN_DW_RLE (end_of_list, DW_RLE_end_of_list) \ - DWARF_ONE_KNOWN_DW_RLE (offset_pair, DW_RLE_offset_pair) \ - DWARF_ONE_KNOWN_DW_RLE (start_end, DW_RLE_start_end) \ - DWARF_ONE_KNOWN_DW_RLE (start_length, DW_RLE_start_length) \ - DWARF_ONE_KNOWN_DW_RLE (startx_endx, DW_RLE_startx_endx) \ - DWARF_ONE_KNOWN_DW_RLE (startx_length, DW_RLE_startx_length) \ - /* End of DW_RLE_*. */ - -#define DWARF_ALL_KNOWN_DW_TAG \ - DWARF_ONE_KNOWN_DW_TAG (GNU_BINCL, DW_TAG_GNU_BINCL) \ - DWARF_ONE_KNOWN_DW_TAG (GNU_EINCL, DW_TAG_GNU_EINCL) \ - DWARF_ONE_KNOWN_DW_TAG (GNU_call_site, DW_TAG_GNU_call_site) \ - DWARF_ONE_KNOWN_DW_TAG (GNU_call_site_parameter, DW_TAG_GNU_call_site_parameter) \ - DWARF_ONE_KNOWN_DW_TAG (GNU_formal_parameter_pack, DW_TAG_GNU_formal_parameter_pack) \ - DWARF_ONE_KNOWN_DW_TAG (GNU_template_parameter_pack, DW_TAG_GNU_template_parameter_pack) \ - DWARF_ONE_KNOWN_DW_TAG (GNU_template_template_param, DW_TAG_GNU_template_template_param) \ - DWARF_ONE_KNOWN_DW_TAG (MIPS_loop, DW_TAG_MIPS_loop) \ - DWARF_ONE_KNOWN_DW_TAG (access_declaration, DW_TAG_access_declaration) \ - DWARF_ONE_KNOWN_DW_TAG (array_type, DW_TAG_array_type) \ - DWARF_ONE_KNOWN_DW_TAG (atomic_type, DW_TAG_atomic_type) \ - DWARF_ONE_KNOWN_DW_TAG (base_type, DW_TAG_base_type) \ - DWARF_ONE_KNOWN_DW_TAG (call_site, DW_TAG_call_site) \ - DWARF_ONE_KNOWN_DW_TAG (call_site_parameter, DW_TAG_call_site_parameter) \ - DWARF_ONE_KNOWN_DW_TAG (catch_block, DW_TAG_catch_block) \ - DWARF_ONE_KNOWN_DW_TAG (class_template, DW_TAG_class_template) \ - DWARF_ONE_KNOWN_DW_TAG (class_type, DW_TAG_class_type) \ - DWARF_ONE_KNOWN_DW_TAG (coarray_type, DW_TAG_coarray_type) \ - DWARF_ONE_KNOWN_DW_TAG (common_block, DW_TAG_common_block) \ - DWARF_ONE_KNOWN_DW_TAG (common_inclusion, DW_TAG_common_inclusion) \ - DWARF_ONE_KNOWN_DW_TAG (compile_unit, DW_TAG_compile_unit) \ - DWARF_ONE_KNOWN_DW_TAG (condition, DW_TAG_condition) \ - DWARF_ONE_KNOWN_DW_TAG (const_type, DW_TAG_const_type) \ - DWARF_ONE_KNOWN_DW_TAG (constant, DW_TAG_constant) \ - DWARF_ONE_KNOWN_DW_TAG (dwarf_procedure, DW_TAG_dwarf_procedure) \ - DWARF_ONE_KNOWN_DW_TAG (dynamic_type, DW_TAG_dynamic_type) \ - DWARF_ONE_KNOWN_DW_TAG (entry_point, DW_TAG_entry_point) \ - DWARF_ONE_KNOWN_DW_TAG (enumeration_type, DW_TAG_enumeration_type) \ - DWARF_ONE_KNOWN_DW_TAG (enumerator, DW_TAG_enumerator) \ - DWARF_ONE_KNOWN_DW_TAG (file_type, DW_TAG_file_type) \ - DWARF_ONE_KNOWN_DW_TAG (formal_parameter, DW_TAG_formal_parameter) \ - DWARF_ONE_KNOWN_DW_TAG (format_label, DW_TAG_format_label) \ - DWARF_ONE_KNOWN_DW_TAG (friend, DW_TAG_friend) \ - DWARF_ONE_KNOWN_DW_TAG (function_template, DW_TAG_function_template) \ - DWARF_ONE_KNOWN_DW_TAG (generic_subrange, DW_TAG_generic_subrange) \ - DWARF_ONE_KNOWN_DW_TAG (immutable_type, DW_TAG_immutable_type) \ - DWARF_ONE_KNOWN_DW_TAG (imported_declaration, DW_TAG_imported_declaration) \ - DWARF_ONE_KNOWN_DW_TAG (imported_module, DW_TAG_imported_module) \ - DWARF_ONE_KNOWN_DW_TAG (imported_unit, DW_TAG_imported_unit) \ - DWARF_ONE_KNOWN_DW_TAG (inheritance, DW_TAG_inheritance) \ - DWARF_ONE_KNOWN_DW_TAG (inlined_subroutine, DW_TAG_inlined_subroutine) \ - DWARF_ONE_KNOWN_DW_TAG (interface_type, DW_TAG_interface_type) \ - DWARF_ONE_KNOWN_DW_TAG (label, DW_TAG_label) \ - DWARF_ONE_KNOWN_DW_TAG (lexical_block, DW_TAG_lexical_block) \ - DWARF_ONE_KNOWN_DW_TAG (member, DW_TAG_member) \ - DWARF_ONE_KNOWN_DW_TAG (module, DW_TAG_module) \ - DWARF_ONE_KNOWN_DW_TAG (namelist, DW_TAG_namelist) \ - DWARF_ONE_KNOWN_DW_TAG (namelist_item, DW_TAG_namelist_item) \ - DWARF_ONE_KNOWN_DW_TAG (namespace, DW_TAG_namespace) \ - DWARF_ONE_KNOWN_DW_TAG (packed_type, DW_TAG_packed_type) \ - DWARF_ONE_KNOWN_DW_TAG (partial_unit, DW_TAG_partial_unit) \ - DWARF_ONE_KNOWN_DW_TAG (pointer_type, DW_TAG_pointer_type) \ - DWARF_ONE_KNOWN_DW_TAG (ptr_to_member_type, DW_TAG_ptr_to_member_type) \ - DWARF_ONE_KNOWN_DW_TAG (reference_type, DW_TAG_reference_type) \ - DWARF_ONE_KNOWN_DW_TAG (restrict_type, DW_TAG_restrict_type) \ - DWARF_ONE_KNOWN_DW_TAG (rvalue_reference_type, DW_TAG_rvalue_reference_type) \ - DWARF_ONE_KNOWN_DW_TAG (set_type, DW_TAG_set_type) \ - DWARF_ONE_KNOWN_DW_TAG (shared_type, DW_TAG_shared_type) \ - DWARF_ONE_KNOWN_DW_TAG (skeleton_unit, DW_TAG_skeleton_unit) \ - DWARF_ONE_KNOWN_DW_TAG (string_type, DW_TAG_string_type) \ - DWARF_ONE_KNOWN_DW_TAG (structure_type, DW_TAG_structure_type) \ - DWARF_ONE_KNOWN_DW_TAG (subprogram, DW_TAG_subprogram) \ - DWARF_ONE_KNOWN_DW_TAG (subrange_type, DW_TAG_subrange_type) \ - DWARF_ONE_KNOWN_DW_TAG (subroutine_type, DW_TAG_subroutine_type) \ - DWARF_ONE_KNOWN_DW_TAG (template_alias, DW_TAG_template_alias) \ - DWARF_ONE_KNOWN_DW_TAG (template_type_parameter, DW_TAG_template_type_parameter) \ - DWARF_ONE_KNOWN_DW_TAG (template_value_parameter, DW_TAG_template_value_parameter) \ - DWARF_ONE_KNOWN_DW_TAG (thrown_type, DW_TAG_thrown_type) \ - DWARF_ONE_KNOWN_DW_TAG (try_block, DW_TAG_try_block) \ - DWARF_ONE_KNOWN_DW_TAG (type_unit, DW_TAG_type_unit) \ - DWARF_ONE_KNOWN_DW_TAG (typedef, DW_TAG_typedef) \ - DWARF_ONE_KNOWN_DW_TAG (union_type, DW_TAG_union_type) \ - DWARF_ONE_KNOWN_DW_TAG (unspecified_parameters, DW_TAG_unspecified_parameters) \ - DWARF_ONE_KNOWN_DW_TAG (unspecified_type, DW_TAG_unspecified_type) \ - DWARF_ONE_KNOWN_DW_TAG (variable, DW_TAG_variable) \ - DWARF_ONE_KNOWN_DW_TAG (variant, DW_TAG_variant) \ - DWARF_ONE_KNOWN_DW_TAG (variant_part, DW_TAG_variant_part) \ - DWARF_ONE_KNOWN_DW_TAG (volatile_type, DW_TAG_volatile_type) \ - DWARF_ONE_KNOWN_DW_TAG (with_stmt, DW_TAG_with_stmt) \ - /* End of DW_TAG_*. */ - -#define DWARF_ALL_KNOWN_DW_UT \ - DWARF_ONE_KNOWN_DW_UT (compile, DW_UT_compile) \ - DWARF_ONE_KNOWN_DW_UT (partial, DW_UT_partial) \ - DWARF_ONE_KNOWN_DW_UT (skeleton, DW_UT_skeleton) \ - DWARF_ONE_KNOWN_DW_UT (split_compile, DW_UT_split_compile) \ - DWARF_ONE_KNOWN_DW_UT (split_type, DW_UT_split_type) \ - DWARF_ONE_KNOWN_DW_UT (type, DW_UT_type) \ - /* End of DW_UT_*. */ - -#define DWARF_ALL_KNOWN_DW_VIRTUALITY \ - DWARF_ONE_KNOWN_DW_VIRTUALITY (none, DW_VIRTUALITY_none) \ - DWARF_ONE_KNOWN_DW_VIRTUALITY (pure_virtual, DW_VIRTUALITY_pure_virtual) \ - DWARF_ONE_KNOWN_DW_VIRTUALITY (virtual, DW_VIRTUALITY_virtual) \ - /* End of DW_VIRTUALITY_*. */ - -#define DWARF_ALL_KNOWN_DW_VIS \ - DWARF_ONE_KNOWN_DW_VIS (exported, DW_VIS_exported) \ - DWARF_ONE_KNOWN_DW_VIS (local, DW_VIS_local) \ - DWARF_ONE_KNOWN_DW_VIS (qualified, DW_VIS_qualified) \ - /* End of DW_VIS_*. */ diff --git a/libdrgn/io.c b/libdrgn/io.c index b3a0a6ed6..532eff47a 100644 --- a/libdrgn/io.c +++ b/libdrgn/io.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include diff --git a/libdrgn/io.h b/libdrgn/io.h index b72457733..9c00b5e93 100644 --- a/libdrgn/io.h +++ b/libdrgn/io.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file diff --git a/libdrgn/kdump.c b/libdrgn/kdump.c index cb0844cc0..5e822b7ef 100644 --- a/libdrgn/kdump.c +++ b/libdrgn/kdump.c @@ -1,5 +1,5 @@ // Copyright 2019 - Serapheim Dimitropoulos -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include @@ -36,6 +36,10 @@ static struct drgn_error *drgn_platform_from_kdump(kdump_ctx_t *ctx, else if (strcmp(str, KDUMP_ARCH_PPC64) == 0) arch = &arch_info_ppc64; /* libkdumpfile doesn't support RISC-V */ + else if (strcmp(str, KDUMP_ARCH_S390X) == 0) + arch = &arch_info_s390x; + else if (strcmp(str, KDUMP_ARCH_S390) == 0) + arch = &arch_info_s390; else arch = &arch_info_unknown; @@ -163,6 +167,9 @@ struct drgn_error *drgn_program_set_kdump(struct drgn_program *prog) err_platform: prog->has_platform = had_platform; err: + // Reset anything we parsed from vmcoreinfo + free(prog->vmcoreinfo.raw); + memset(&prog->vmcoreinfo, 0, sizeof(prog->vmcoreinfo)); kdump_free(ctx); return err; } diff --git a/libdrgn/language.c b/libdrgn/language.c index 9bbf98779..b701c6440 100644 --- a/libdrgn/language.c +++ b/libdrgn/language.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include diff --git a/libdrgn/language.h b/libdrgn/language.h index 935545a0e..00f9ee315 100644 --- a/libdrgn/language.h +++ b/libdrgn/language.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file diff --git a/libdrgn/language_c.c b/libdrgn/language_c.c index a698cc4b4..31331b902 100644 --- a/libdrgn/language_c.c +++ b/libdrgn/language_c.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include @@ -536,14 +536,13 @@ static struct drgn_error * c_format_type_name(struct drgn_qualified_type qualified_type, char **ret) { struct drgn_error *err; - struct string_builder sb = {}; - + struct string_builder sb = STRING_BUILDER_INIT; err = c_format_type_name_impl(qualified_type, &sb); if (err) { free(sb.str); return err; } - if (!string_builder_finalize(&sb, ret)) + if (!(*ret = string_builder_null_terminate(&sb))) return &drgn_enomem; return NULL; } @@ -552,8 +551,7 @@ static struct drgn_error * c_format_type(struct drgn_qualified_type qualified_type, char **ret) { struct drgn_error *err; - struct string_builder sb = {}; - + struct string_builder sb = STRING_BUILDER_INIT; if (drgn_type_is_complete(qualified_type.type)) err = c_define_type(qualified_type, 0, &sb); else @@ -562,7 +560,7 @@ c_format_type(struct drgn_qualified_type qualified_type, char **ret) free(sb.str); return err; } - if (!string_builder_finalize(&sb, ret)) + if (!(*ret = string_builder_null_terminate(&sb))) return &drgn_enomem; return NULL; } @@ -692,30 +690,70 @@ c_format_int_object(const struct drgn_object *obj, return NULL; } + union drgn_value value_mem; + const union drgn_value *value; + err = drgn_object_read_value(obj, &value_mem, &value); + if (err) + return err; switch (obj->encoding) { - case DRGN_OBJECT_ENCODING_SIGNED: { - int64_t svalue; - - err = drgn_object_read_signed(obj, &svalue); - if (err) - return err; - if (!string_builder_appendf(sb, "%" PRId64, svalue)) - return &drgn_enomem; - return NULL; - } - case DRGN_OBJECT_ENCODING_UNSIGNED: { - uint64_t uvalue; - - err = drgn_object_read_unsigned(obj, &uvalue); - if (err) - return err; - if (!string_builder_appendf(sb, "%" PRIu64, uvalue)) - return &drgn_enomem; - return NULL; + case DRGN_OBJECT_ENCODING_SIGNED: + if (!string_builder_appendf(sb, "%" PRId64, value->svalue)) { + err = &drgn_enomem; + goto out; + } + break; + case DRGN_OBJECT_ENCODING_UNSIGNED: + if (!string_builder_appendf(sb, "%" PRIu64, value->uvalue)) { + err = &drgn_enomem; + goto out; + } + break; + case DRGN_OBJECT_ENCODING_SIGNED_BIG: + case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: { + if (!string_builder_append(sb, "0x")) { + err = &drgn_enomem; + goto out; + } + const uint8_t *buf = (uint8_t *)value->bufp; + size_t bytes = drgn_object_size(obj); + if (obj->little_endian) { + size_t i = bytes - 1; + while (i > 0 && buf[i] == 0) + i--; + if (!string_builder_appendf(sb, "%" PRIx8, buf[i])) { + err = &drgn_enomem; + goto out; + } + while (i-- > 0) { + if (!string_builder_appendf(sb, "%02" PRIx8, buf[i])) { + err = &drgn_enomem; + goto out; + } + } + } else { + size_t i = 0; + while (i < bytes - 1 && buf[i] == 0) + i++; + if (!string_builder_appendf(sb, "%" PRIx8, buf[i])) { + err = &drgn_enomem; + goto out; + } + while (++i < bytes) { + if (!string_builder_appendf(sb, "%02" PRIx8, buf[i])) { + err = &drgn_enomem; + goto out; + } + } + } + break; } default: UNREACHABLE(); } + err = NULL; +out: + drgn_object_deinit_value(obj, value); + return err; } static struct drgn_error * @@ -978,7 +1016,7 @@ struct compound_initializer_state { uint64_t bit_offset; }; -DEFINE_VECTOR(compound_initializer_stack, struct compound_initializer_state) +DEFINE_VECTOR(compound_initializer_stack, struct compound_initializer_state); struct compound_initializer_iter { struct initializer_iter iter; @@ -997,13 +1035,13 @@ compound_initializer_iter_next(struct initializer_iter *iter_, container_of(iter_, struct compound_initializer_iter, iter); for (;;) { - if (!iter->stack.size) + if (compound_initializer_stack_empty(&iter->stack)) return &drgn_stop; struct compound_initializer_state *top = - &iter->stack.data[iter->stack.size - 1]; + compound_initializer_stack_last(&iter->stack); if (top->member == top->end) { - iter->stack.size--; + compound_initializer_stack_pop(&iter->stack); continue; } @@ -1063,11 +1101,9 @@ static void compound_initializer_iter_reset(struct initializer_iter *iter_) { struct compound_initializer_iter *iter = container_of(iter_, struct compound_initializer_iter, iter); - struct drgn_type *underlying_type = - drgn_underlying_type(iter->obj->type); - - iter->stack.size = 1; - iter->stack.data[0].member = drgn_type_members(underlying_type); + compound_initializer_stack_resize(&iter->stack, 1); + compound_initializer_stack_first(&iter->stack)->member = + drgn_type_members(drgn_underlying_type(iter->obj->type)); } static struct drgn_error * @@ -1077,7 +1113,7 @@ compound_initializer_append_designation(struct initializer_iter *iter_, struct compound_initializer_iter *iter = container_of(iter_, struct compound_initializer_iter, iter); struct compound_initializer_state *top = - &iter->stack.data[iter->stack.size - 1]; + compound_initializer_stack_last(&iter->stack); const char *name = top->member[-1].name; if (name && !string_builder_appendf(sb, ".%s = ", name)) @@ -1174,7 +1210,7 @@ c_format_compound_object(const struct drgn_object *obj, } while (new->member < new->end); drgn_object_deinit(&member); if (err) - return err; + goto out; } err = c_format_initializer(drgn_object_program(obj), &iter.iter, indent, @@ -1604,15 +1640,14 @@ static struct drgn_error *c_format_object(const struct drgn_object *obj, char **ret) { struct drgn_error *err; - struct string_builder sb = {}; - + struct string_builder sb = STRING_BUILDER_INIT; err = c_format_object_impl(obj, 0, columns, max(columns, (size_t)1), flags, &sb); if (err) { free(sb.str); return err; } - if (!string_builder_finalize(&sb, ret)) + if (!(*ret = string_builder_null_terminate(&sb))) return &drgn_enomem; return NULL; } @@ -1653,6 +1688,8 @@ enum { C_TOKEN_DOT, C_TOKEN_NUMBER, C_TOKEN_IDENTIFIER, + C_TOKEN_TEMPLATE_ARGUMENTS, + C_TOKEN_COLON, }; #include "c_keywords.inc" @@ -1699,6 +1736,43 @@ struct drgn_error *drgn_c_family_lexer_func(struct drgn_lexer *lexer, token->kind = C_TOKEN_DOT; p++; break; + case ':': + token->kind = C_TOKEN_COLON; + p++; + break; + case '<': + // This is a hack for cpp_append_to_identifier(). We don't want + // to deal with actually parsing template arguments, and we + // don't care about "<" otherwise, so this scans a token from + // the "<" to its matching ">". + if (cpp) { + token->kind = C_TOKEN_TEMPLATE_ARGUMENTS; + p++; + size_t less_thans = 1; + bool in_single_quotes = false; + do { + switch (*p++) { + case '<': + if (!in_single_quotes) + less_thans++; + break; + case '>': + if (!in_single_quotes) + less_thans--; + break; + case '\'': + // Handling the edge-case of an escaped single-quote + if (!(in_single_quotes && *(p - 2) == '\\')) + in_single_quotes = !in_single_quotes; + break; + case '\0': + return drgn_error_create(DRGN_ERROR_SYNTAX, + "invalid template arguments"); + } + } while (less_thans > 0); + break; + } + fallthrough; default: if (isalpha(*p) || *p == '_') { do { @@ -2063,6 +2137,45 @@ enum drgn_primitive_type c_parse_specifier_list(const char *s) return primitive; } + +// The DWARF index currently includes template arguments in indexed names. So, +// to be able to find a type with template arguments, we have to look it up with +// the template arguments included. This looks for a C_TOKEN_TEMPLATE_ARGUMENTS +// token after the identifier and returns the length from the beginning of the +// identifier to the end of the template arguments. +// +// Note that this requires that the user formats the template arguments exactly +// as they appear in DWARF (which can vary between compilers). In the future, it +// might be better to properly parse and either normalize the template arguments +// or look them up as an AST somehow. +static struct drgn_error *cpp_append_to_identifier( + struct drgn_lexer *lexer, const char *identifier, size_t *len_ret) +{ + struct drgn_error *err; + + // Only for C++. + if (!((struct drgn_c_family_lexer *)lexer)->cpp) + return NULL; + + struct drgn_token token; + + do { + err = drgn_lexer_pop(lexer, &token); + } while (!err && (token.kind == C_TOKEN_IDENTIFIER || + token.kind == C_TOKEN_COLON)); + + if (err) + return err; + if (token.kind != C_TOKEN_TEMPLATE_ARGUMENTS) { + err = drgn_lexer_push(lexer, &token); + if (err) + return err; + } + + *len_ret = token.value + token.len - identifier; + return NULL; +} + static struct drgn_error * c_parse_specifier_qualifier_list(struct drgn_program *prog, struct drgn_lexer *lexer, const char *filename, @@ -2110,10 +2223,15 @@ c_parse_specifier_qualifier_list(struct drgn_program *prog, keyword_spelling[token.kind], specifier_spelling[prev_specifier]); } - } else if (token.kind == C_TOKEN_IDENTIFIER && - specifier == SPECIFIER_NONE && !identifier) { + } else if ((token.kind == C_TOKEN_IDENTIFIER || + token.kind == C_TOKEN_COLON) && + specifier == SPECIFIER_NONE && !identifier) { identifier = token.value; identifier_len = token.len; + err = cpp_append_to_identifier(lexer, identifier, + &identifier_len); + if (err) + return err; } else if (token.kind == C_TOKEN_STRUCT || token.kind == C_TOKEN_UNION || token.kind == C_TOKEN_CLASS || @@ -2133,7 +2251,8 @@ c_parse_specifier_qualifier_list(struct drgn_program *prog, err = drgn_lexer_pop(lexer, &token); if (err) return err; - if (token.kind != C_TOKEN_IDENTIFIER) { + if (!(token.kind == C_TOKEN_IDENTIFIER || + token.kind == C_TOKEN_COLON)) { return drgn_error_format(DRGN_ERROR_SYNTAX, "expected identifier after '%s'", keyword_spelling[tag_token]); @@ -2141,6 +2260,10 @@ c_parse_specifier_qualifier_list(struct drgn_program *prog, } identifier = token.value; identifier_len = token.len; + err = cpp_append_to_identifier(lexer, identifier, + &identifier_len); + if (err) + return err; } else { err = drgn_lexer_push(lexer, &token); if (err) @@ -2150,18 +2273,19 @@ c_parse_specifier_qualifier_list(struct drgn_program *prog, } if (specifier == SPECIFIER_NONE) { - enum drgn_type_kind kind; - + uint64_t kinds; if (tag_token == C_TOKEN_STRUCT) { - kind = DRGN_TYPE_STRUCT; + kinds = 1 << DRGN_TYPE_STRUCT; } else if (tag_token == C_TOKEN_UNION) { - kind = DRGN_TYPE_UNION; + kinds = 1 << DRGN_TYPE_UNION; } else if (tag_token == C_TOKEN_CLASS) { - kind = DRGN_TYPE_CLASS; + kinds = 1 << DRGN_TYPE_CLASS; } else if (tag_token == C_TOKEN_ENUM) { - kind = DRGN_TYPE_ENUM; + kinds = 1 << DRGN_TYPE_ENUM; } else if (identifier) { - if (strstartswith(identifier, "size_t")) { + if (identifier_len == sizeof("size_t") - 1 && + memcmp(identifier, "size_t", + sizeof("size_t") - 1) == 0) { err = drgn_program_find_primitive_type(prog, DRGN_C_TYPE_SIZE_T, &ret->type); @@ -2169,7 +2293,9 @@ c_parse_specifier_qualifier_list(struct drgn_program *prog, return err; ret->qualifiers = 0; goto out; - } else if (strstartswith(identifier, "ptrdiff_t")) { + } else if (identifier_len == sizeof("ptrdiff_t") - 1 && + memcmp(identifier, "ptrdiff_t", + sizeof("ptrdiff_t") - 1) == 0) { err = drgn_program_find_primitive_type(prog, DRGN_C_TYPE_PTRDIFF_T, &ret->type); @@ -2177,15 +2303,21 @@ c_parse_specifier_qualifier_list(struct drgn_program *prog, return err; ret->qualifiers = 0; goto out; + } else if (((struct drgn_c_family_lexer *)lexer)->cpp) { + kinds = ((1 << DRGN_TYPE_STRUCT) + | (1 << DRGN_TYPE_UNION) + | (1 << DRGN_TYPE_CLASS) + | (1 << DRGN_TYPE_ENUM) + | (1 << DRGN_TYPE_TYPEDEF)); } else { - kind = DRGN_TYPE_TYPEDEF; + kinds = 1 << DRGN_TYPE_TYPEDEF; } } else { return drgn_error_create(DRGN_ERROR_SYNTAX, "expected type specifier"); } - err = drgn_program_find_type_impl(prog, kind, identifier, + err = drgn_program_find_type_impl(prog, kinds, identifier, identifier_len, filename, ret); if (err) diff --git a/libdrgn/lazy_object.c b/libdrgn/lazy_object.c index 1bf0eddb1..ff715296f 100644 --- a/libdrgn/lazy_object.c +++ b/libdrgn/lazy_object.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include diff --git a/libdrgn/lazy_object.h b/libdrgn/lazy_object.h index a44a93001..26ca52e67 100644 --- a/libdrgn/lazy_object.h +++ b/libdrgn/lazy_object.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file diff --git a/libdrgn/lexer.c b/libdrgn/lexer.c index 089e1be9b..347cb88cd 100644 --- a/libdrgn/lexer.c +++ b/libdrgn/lexer.c @@ -1,10 +1,10 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include "drgn.h" #include "lexer.h" -DEFINE_VECTOR_FUNCTIONS(drgn_token_vector) +DEFINE_VECTOR_FUNCTIONS(drgn_token_vector); void drgn_lexer_init(struct drgn_lexer *lexer, drgn_lexer_func func, const char *str) @@ -22,11 +22,11 @@ void drgn_lexer_deinit(struct drgn_lexer *lexer) struct drgn_error *drgn_lexer_pop(struct drgn_lexer *lexer, struct drgn_token *token) { - if (lexer->stack.size) { + if (drgn_token_vector_empty(&lexer->stack)) { + return lexer->func(lexer, token); + } else { *token = *drgn_token_vector_pop(&lexer->stack); return NULL; - } else { - return lexer->func(lexer, token); } } diff --git a/libdrgn/lexer.h b/libdrgn/lexer.h index c2b0a246d..f1068cde7 100644 --- a/libdrgn/lexer.h +++ b/libdrgn/lexer.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -57,7 +57,7 @@ struct drgn_token { size_t len; }; -DEFINE_VECTOR_TYPE(drgn_token_vector, struct drgn_token) +DEFINE_VECTOR_TYPE(drgn_token_vector, struct drgn_token); /** * Lexer instance. diff --git a/libdrgn/linux_kernel.c b/libdrgn/linux_kernel.c index 3c071fa9c..1b3080e1b 100644 --- a/libdrgn/linux_kernel.c +++ b/libdrgn/linux_kernel.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include @@ -18,6 +18,7 @@ #include #include "binary_buffer.h" +#include "cleanup.h" #include "debug_info.h" #include "drgn.h" #include "error.h" @@ -25,6 +26,7 @@ #include "helpers.h" #include "io.h" #include "linux_kernel.h" +#include "platform.h" #include "program.h" #include "type.h" #include "util.h" @@ -106,7 +108,6 @@ struct drgn_error *read_vmcoreinfo_fallback(struct drgn_program *prog) FILE *file; uint64_t address; size_t size; - char *buf; Elf64_Nhdr *nhdr; file = fopen("/sys/kernel/vmcoreinfo", "r"); @@ -121,13 +122,13 @@ struct drgn_error *read_vmcoreinfo_fallback(struct drgn_program *prog) } fclose(file); - buf = malloc(size); + _cleanup_free_ char *buf = malloc(size); if (!buf) return &drgn_enomem; err = drgn_program_read_memory(prog, buf, address, size, true); if (err) - goto out; + return err; /* * The first 12 bytes are the Elf{32,64}_Nhdr (it's the same in both @@ -140,13 +141,10 @@ struct drgn_error *read_vmcoreinfo_fallback(struct drgn_program *prog) nhdr->n_descsz > size - 24) { err = drgn_error_create(DRGN_ERROR_OTHER, "VMCOREINFO is invalid"); - goto out; + return err; } - err = drgn_program_parse_vmcoreinfo(prog, buf + 24, nhdr->n_descsz); -out: - free(buf); - return err; + return drgn_program_parse_vmcoreinfo(prog, buf + 24, nhdr->n_descsz); } static struct drgn_error *linux_kernel_get_page_shift(struct drgn_program *prog, @@ -213,6 +211,63 @@ linux_kernel_get_uts_release(struct drgn_program *prog, struct drgn_object *ret) 0, 0); } +// jiffies is defined as an alias of jiffies_64 via the Linux kernel linker +// script, so it is not included in debug info. +static struct drgn_error *linux_kernel_get_jiffies(struct drgn_program *prog, + struct drgn_object *ret) +{ + struct drgn_error *err; + struct drgn_object jiffies_64; + drgn_object_init(&jiffies_64, prog); + err = drgn_program_find_object(prog, "jiffies_64", NULL, + DRGN_FIND_OBJECT_VARIABLE, &jiffies_64); + if (err) { + if (err->code == DRGN_ERROR_LOOKUP) { + drgn_error_destroy(err); + err = &drgn_not_found; + } + goto out; + } + if (jiffies_64.kind != DRGN_OBJECT_REFERENCE) { + err = &drgn_not_found; + goto out; + } + uint64_t address = jiffies_64.address; + struct drgn_qualified_type qualified_type; + err = drgn_program_find_primitive_type(prog, DRGN_C_TYPE_UNSIGNED_LONG, + &qualified_type.type); + if (err) + return err; + qualified_type.qualifiers = DRGN_QUALIFIER_VOLATILE; + if (drgn_type_size(qualified_type.type) == 4 && + !drgn_type_little_endian(qualified_type.type)) + address += 4; + err = drgn_object_set_reference(ret, qualified_type, address, 0, 0); +out: + drgn_object_deinit(&jiffies_64); + return err; +} + +static struct drgn_error * +linux_kernel_get_vmcoreinfo(struct drgn_program *prog, struct drgn_object *ret) +{ + struct drgn_error *err; + struct drgn_qualified_type qualified_type; + err = drgn_program_find_primitive_type(prog, + DRGN_C_TYPE_CHAR, + &qualified_type.type); + if (err) + return err; + qualified_type.qualifiers = DRGN_QUALIFIER_CONST; + err = drgn_array_type_create(prog, qualified_type, prog->vmcoreinfo.raw_size, + &drgn_language_c, &qualified_type.type); + if (err) + return err; + qualified_type.qualifiers = 0; + return drgn_object_set_from_buffer(ret, qualified_type, prog->vmcoreinfo.raw, + prog->vmcoreinfo.raw_size, 0, 0); +} + // The vmemmap address can vary depending on architecture, kernel version, // configuration options, and KASLR. However, we can get it generically from the // section_mem_map of any valid mem_section. @@ -320,7 +375,9 @@ static struct drgn_error *linux_kernel_get_vmemmap(struct drgn_program *prog, { struct drgn_error *err; if (prog->vmemmap.kind == DRGN_OBJECT_ABSENT) { - uint64_t address; + // Silence -Wmaybe-uninitialized false positive last seen with + // GCC 13 by initializing to zero. + uint64_t address = 0; err = linux_kernel_get_vmemmap_address(prog, &address); if (err) return err; @@ -458,12 +515,28 @@ kernel_module_iterator_next(struct kernel_module_iterator *it) return err; // Set tmp1 to the module base address and tmp2 to the size. - err = drgn_object_member(&it->tmp1, &it->mod, "core_layout"); + err = drgn_object_member(&it->tmp1, &it->mod, "mem"); if (!err) { - // Since Linux kernel commit 7523e4dc5057 ("module: use a - // structure to encapsulate layout.") (in v4.5), the base and - // size are in the `struct module_layout core_layout` member of - // `struct module`. + union drgn_value mod_mem_type; + + // Since Linux kernel commit ac3b43283923 ("module: replace + // module_layout with module_memory") (in v6.4), the base and + // size are in the `struct module_memory mem[MOD_TEXT]` member + // of `struct module`. + err = drgn_program_find_object(drgn_object_program(&it->mod), + "MOD_TEXT", NULL, + DRGN_FIND_OBJECT_CONSTANT, + &it->tmp2); + if (err) + return err; + err = drgn_object_read_integer(&it->tmp2, &mod_mem_type); + if (err) + return err; + + err = drgn_object_subscript(&it->tmp1, &it->tmp1, + mod_mem_type.uvalue); + if (err) + return err; err = drgn_object_member(&it->tmp2, &it->tmp1, "size"); if (err) return err; @@ -471,15 +544,36 @@ kernel_module_iterator_next(struct kernel_module_iterator *it) if (err) return err; } else if (err->code == DRGN_ERROR_LOOKUP) { - // Before that, they are directly in the `struct module`. + // Since Linux kernel commit 7523e4dc5057 ("module: use a + // structure to encapsulate layout.") (in v4.5), the base and + // size are in the `struct module_layout core_layout` member of + // `struct module`. drgn_error_destroy(err); - err = drgn_object_member(&it->tmp2, &it->mod, "core_size"); - if (err) - return err; - err = drgn_object_member(&it->tmp1, &it->mod, "module_core"); - if (err) + err = drgn_object_member(&it->tmp1, &it->mod, "core_layout"); + if (!err) { + err = drgn_object_member(&it->tmp2, &it->tmp1, "size"); + if (err) + return err; + err = drgn_object_member(&it->tmp1, &it->tmp1, "base"); + if (err) + return err; + } else if (err->code == DRGN_ERROR_LOOKUP) { + // Before that, they are directly in the `struct + // module`. + drgn_error_destroy(err); + + err = drgn_object_member(&it->tmp2, &it->mod, + "core_size"); + if (err) + return err; + err = drgn_object_member(&it->tmp1, &it->mod, + "module_core"); + if (err) + return err; + } else { return err; + } } else { return err; } @@ -1175,7 +1269,7 @@ static struct drgn_error *identify_kernel_elf(Elf *elf, } DEFINE_HASH_MAP(elf_scn_name_map, const char *, Elf_Scn *, - c_string_key_hash_pair, c_string_key_eq) + c_string_key_hash_pair, c_string_key_eq); static struct drgn_error * cache_kernel_module_sections(struct kernel_module_iterator *kmod_it, Elf *elf) @@ -1274,7 +1368,7 @@ kernel_module_table_key(struct kernel_module_file * const *entry) } DEFINE_HASH_TABLE(kernel_module_table, struct kernel_module_file *, - kernel_module_table_key, nstring_hash_pair, nstring_eq) + kernel_module_table_key, nstring_hash_pair, nstring_eq); static struct drgn_error * report_loaded_kernel_module(struct drgn_debug_info_load_state *load, diff --git a/libdrgn/linux_kernel.h b/libdrgn/linux_kernel.h index 8c3d93acf..d848a45ec 100644 --- a/libdrgn/linux_kernel.h +++ b/libdrgn/linux_kernel.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #ifndef DRGN_LINUX_KERNEL_H #define DRGN_LINUX_KERNEL_H @@ -32,6 +32,9 @@ linux_kernel_report_debug_info(struct drgn_debug_info_load_state *load); #define KDUMP_SIGNATURE "KDUMP " #define KDUMP_SIG_LEN (sizeof(KDUMP_SIGNATURE) - 1) +#define FLATTENED_SIGNATURE "makedumpfile" +#define FLATTENED_SIG_LEN (sizeof(FLATTENED_SIGNATURE) - 1) + #ifdef WITH_LIBKDUMPFILE struct drgn_error *drgn_program_cache_kdump_notes(struct drgn_program *prog); struct drgn_error *drgn_program_set_kdump(struct drgn_program *prog); diff --git a/libdrgn/linux_kernel_helpers.c b/libdrgn/linux_kernel_helpers.c index ee338a553..72bf5e62e 100644 --- a/libdrgn/linux_kernel_helpers.c +++ b/libdrgn/linux_kernel_helpers.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include @@ -40,7 +40,7 @@ begin_virtual_address_translation(struct drgn_program *prog, uint64_t pgtable, goto err; } if (!prog->platform.arch->linux_kernel_pgtable_iterator_next) { - err = drgn_error_format(DRGN_ERROR_INVALID_ARGUMENT, + err = drgn_error_format(DRGN_ERROR_NOT_IMPLEMENTED, "virtual address translation is not implemented for %s architecture", prog->platform.arch->name); goto err; @@ -190,6 +190,35 @@ struct drgn_error *linux_helper_read_vm(struct drgn_program *prog, return err; } +struct drgn_error *linux_helper_follow_phys(struct drgn_program *prog, + uint64_t pgtable, + uint64_t virt_addr, uint64_t *ret) +{ + struct drgn_error *err; + + err = begin_virtual_address_translation(prog, pgtable, virt_addr); + if (err) + return err; + + struct pgtable_iterator *it = prog->pgtable_it; + pgtable_iterator_next_fn *next = + prog->platform.arch->linux_kernel_pgtable_iterator_next; + uint64_t start_virt_addr, start_phys_addr; + err = next(prog, it, &start_virt_addr, &start_phys_addr); + if (err) + goto out; + if (start_phys_addr == UINT64_MAX) { + err = drgn_error_create_fault("address is not mapped", + virt_addr); + goto out; + } + *ret = start_phys_addr + (virt_addr - start_virt_addr); + err = NULL; +out: + end_virtual_address_translation(prog); + return err; +} + struct drgn_error *linux_helper_per_cpu_ptr(struct drgn_object *res, const struct drgn_object *ptr, uint64_t cpu) @@ -228,7 +257,8 @@ struct drgn_error *linux_helper_per_cpu_ptr(struct drgn_object *res, return err; } -struct drgn_error *linux_helper_idle_task(struct drgn_object *res, uint64_t cpu) +static struct drgn_error *cpu_rq_member(struct drgn_object *res, uint64_t cpu, + const char *member_name) { struct drgn_error *err; struct drgn_program *prog = drgn_object_program(res); @@ -245,58 +275,141 @@ struct drgn_error *linux_helper_idle_task(struct drgn_object *res, uint64_t cpu) err = linux_helper_per_cpu_ptr(&tmp, &tmp, cpu); if (err) goto out; - err = drgn_object_member_dereference(res, &tmp, "idle"); + err = drgn_object_member_dereference(&tmp, &tmp, member_name); + if (err) + goto out; + err = drgn_object_read(res, &tmp); +out: + drgn_object_deinit(&tmp); + return err; +} + +struct drgn_error *linux_helper_cpu_curr(struct drgn_object *res, uint64_t cpu) +{ + return cpu_rq_member(res, cpu, "curr"); +} + +struct drgn_error *linux_helper_idle_task(struct drgn_object *res, uint64_t cpu) +{ + return cpu_rq_member(res, cpu, "idle"); +} + +struct drgn_error *linux_helper_task_cpu(const struct drgn_object *task, + uint64_t *ret) +{ + struct drgn_error *err; + struct drgn_object tmp; + drgn_object_init(&tmp, drgn_object_program(task)); + + // If CONFIG_THREAD_INFO_IN_TASK=y and since Linux kernel commit + // bcf9033e5449 ("sched: move CPU field back into thread_info if + // THREAD_INFO_IN_TASK=y") (in v5.16), the CPU is task->thread_info.cpu. + // + // If CONFIG_THREAD_INFO_IN_TASK=y but before that commit, the cpu is + // task->cpu. + // + // If CONFIG_THREAD_INFO_IN_TASK=n or before Linux kernel commit + // c65eacbe290b ("sched/core: Allow putting thread_info into + // task_struct") (in v4.9), the CPU is + // ((struct thread_info *)task->stack)->cpu. + // + // If none of those exist, then the kernel must be !SMP. + err = drgn_object_member_dereference(&tmp, task, "thread_info"); + if (!err) { + err = drgn_object_member(&tmp, &tmp, "cpu"); + if (err && err->code == DRGN_ERROR_LOOKUP) { + drgn_error_destroy(err); + err = drgn_object_member_dereference(&tmp, task, "cpu"); + } + } else if (err->code == DRGN_ERROR_LOOKUP) { + // CONFIG_THREAD_INFO_IN_TASK=n + drgn_error_destroy(err); + err = drgn_object_member_dereference(&tmp, task, "stack"); + if (err) + goto out; + struct drgn_qualified_type thread_info_type; + err = drgn_program_find_type(drgn_object_program(task), + "struct thread_info *", NULL, + &thread_info_type); + if (err) + goto out; + err = drgn_object_cast(&tmp, thread_info_type, &tmp); + if (err) + goto out; + err = drgn_object_member_dereference(&tmp, &tmp, "cpu"); + } + if (!err) { + union drgn_value value; + err = drgn_object_read_integer(&tmp, &value); + if (!err) + *ret = value.uvalue; + } else if (err->code == DRGN_ERROR_LOOKUP) { + // CONFIG_SMP=n + drgn_error_destroy(err); + *ret = 0; + err = NULL; + } out: drgn_object_deinit(&tmp); return err; } struct drgn_error * -linux_helper_radix_tree_lookup(struct drgn_object *res, - const struct drgn_object *root, uint64_t index) +linux_helper_xa_load(struct drgn_object *res, + const struct drgn_object *xa, uint64_t index) { struct drgn_error *err; - static const uint64_t RADIX_TREE_ENTRY_MASK = 3; - uint64_t RADIX_TREE_INTERNAL_NODE; - uint64_t RADIX_TREE_MAP_MASK; - struct drgn_object node, tmp; + + struct drgn_object entry, node, tmp; struct drgn_qualified_type node_type; + uint64_t internal_flag, node_min; + drgn_object_init(&entry, drgn_object_program(res)); drgn_object_init(&node, drgn_object_program(res)); drgn_object_init(&tmp, drgn_object_program(res)); - /* node = root->xa_head */ - err = drgn_object_member_dereference(&node, root, "xa_head"); + // See xa_for_each() in drgn/helpers/linux/xarray.py for a description + // of the cases we have to handle. + // entry = xa->xa_head + err = drgn_object_member_dereference(&entry, xa, "xa_head"); if (!err) { + err = drgn_object_read(&entry, &entry); + if (err) + goto out; + // node_type = struct xa_node * err = drgn_program_find_type(drgn_object_program(res), "struct xa_node *", NULL, &node_type); if (err) goto out; - RADIX_TREE_INTERNAL_NODE = 2; + internal_flag = 2; + node_min = 4097; } else if (err->code == DRGN_ERROR_LOOKUP) { drgn_error_destroy(err); - /* node = (void *)root.rnode */ - err = drgn_object_member_dereference(&node, root, "rnode"); + // entry = (void *)xa->rnode + err = drgn_object_member_dereference(&entry, xa, "rnode"); if (err) goto out; + // node_type = typeof(xa->rnode) + node_type = drgn_object_qualified_type(&entry); + struct drgn_qualified_type voidp_type; err = drgn_program_find_type(drgn_object_program(res), "void *", - NULL, &node_type); + NULL, &voidp_type); if (err) goto out; - err = drgn_object_cast(&node, node_type, &node); + err = drgn_object_cast(&entry, voidp_type, &entry); if (err) goto out; - err = drgn_program_find_type(drgn_object_program(res), - "struct radix_tree_node *", NULL, - &node_type); - if (err) - goto out; - RADIX_TREE_INTERNAL_NODE = 1; + internal_flag = 1; + node_min = 0; } else { goto out; } + // xa_is_node() or radix_tree_is_internal_node() +#define is_node(entry_value) \ + (((entry_value) & 3) == internal_flag && (entry_value) >= node_min) + struct drgn_type_member *member; uint64_t member_bit_offset; err = drgn_type_find_member(drgn_type_type(node_type.type).type, @@ -309,54 +422,158 @@ linux_helper_radix_tree_lookup(struct drgn_object *res, goto out; if (drgn_type_kind(member_type.type) != DRGN_TYPE_ARRAY) { err = drgn_error_create(DRGN_ERROR_TYPE, - "struct radix_tree_node slots member is not an array"); + "struct xa_node slots member is not an array"); goto out; } - RADIX_TREE_MAP_MASK = drgn_type_length(member_type.type) - 1; - - for (;;) { - uint64_t value; - union drgn_value shift; - uint64_t offset; - - err = drgn_object_read(&node, &node); + uint64_t XA_CHUNK_MASK = drgn_type_length(member_type.type) - 1; + uint64_t sizeof_slots; + if (node_min == 0) { // !xarray + err = drgn_type_sizeof(member_type.type, &sizeof_slots); if (err) goto out; - err = drgn_object_read_unsigned(&node, &value); - if (err) - goto out; - if ((value & RADIX_TREE_ENTRY_MASK) != RADIX_TREE_INTERNAL_NODE) - break; + } + + uint64_t entry_value; + err = drgn_object_read_unsigned(&entry, &entry_value); + if (err) + goto out; + if (is_node(entry_value)) { + // node = xa_to_node(entry) + // or + // node = entry_to_node(entry) err = drgn_object_set_unsigned(&node, node_type, - value & ~RADIX_TREE_INTERNAL_NODE, - 0); + entry_value - internal_flag, 0); if (err) goto out; + // node_shift = node->shift err = drgn_object_member_dereference(&tmp, &node, "shift"); if (err) goto out; - err = drgn_object_read_integer(&tmp, &shift); + union drgn_value node_shift; + err = drgn_object_read_integer(&tmp, &node_shift); if (err) goto out; - if (shift.uvalue >= 64) + + uint64_t offset; + if (node_shift.uvalue >= 64) // Avoid undefined behavior. offset = 0; else - offset = (index >> shift.uvalue) & RADIX_TREE_MAP_MASK; - err = drgn_object_member_dereference(&tmp, &node, "slots"); - if (err) - goto out; - err = drgn_object_subscript(&node, &tmp, offset); - if (err) - goto out; + offset = index >> node_shift.uvalue; + if (offset > XA_CHUNK_MASK) + goto null; + + for (;;) { + // entry = node->slots[offset] + err = drgn_object_member_dereference(&tmp, &node, + "slots"); + if (err) + goto out; + err = drgn_object_subscript(&entry, &tmp, offset); + if (err) + goto out; + err = drgn_object_read(&entry, &entry); + if (err) + goto out; + err = drgn_object_read_unsigned(&entry, &entry_value); + if (err) + goto out; + + if ((entry_value & 3) == internal_flag) { + if (node_min != 0 && // xarray + entry_value < 256) { // xa_is_sibling() + // entry = node->slots[xa_to_sibling(entry)] + err = drgn_object_subscript(&entry, + &tmp, + entry_value >> 2); + if (err) + goto out; + err = drgn_object_read(&entry, &entry); + if (err) + goto out; + err = drgn_object_read_unsigned(&entry, + &entry_value); + if (err) + goto out; + } else if (node_min == 0 && // !xarray + tmp.address <= entry_value && + entry_value < tmp.address + sizeof_slots) { // is_sibling_entry() + // entry = *(void **)entry_to_node(entry) + struct drgn_qualified_type voidpp_type; + err = drgn_program_find_type(drgn_object_program(res), + "void **", + NULL, + &voidpp_type); + if (err) + goto out; + err = drgn_object_set_unsigned(&entry, + voidpp_type, + entry_value - 1, + 0); + if (err) + goto out; + err = drgn_object_dereference(&entry, + &entry); + if (err) + goto out; + err = drgn_object_read(&entry, &entry); + if (err) + goto out; + err = drgn_object_read_unsigned(&entry, + &entry_value); + if (err) + goto out; + } + } + + if (node_shift.uvalue == 0 || !is_node(entry_value)) + break; + + // node = xa_to_node(entry) + // or + // node = entry_to_node(entry) + err = drgn_object_set_unsigned(&node, node_type, + entry_value - internal_flag, + 0); + if (err) + goto out; + // node_shift = node->shift + err = drgn_object_member_dereference(&tmp, &node, + "shift"); + if (err) + goto out; + err = drgn_object_read_integer(&tmp, &node_shift); + if (err) + goto out; + + if (node_shift.uvalue >= 64) // Avoid undefined behavior. + offset = 0; + else + offset = (index >> node_shift.uvalue) & XA_CHUNK_MASK; + } + } else if (index) { + goto null; } - err = drgn_object_copy(res, &node); + err = drgn_object_copy(res, &entry); out: drgn_object_deinit(&tmp); drgn_object_deinit(&node); + drgn_object_deinit(&entry); return err; + +null: + err = drgn_object_set_unsigned(res, drgn_object_qualified_type(&entry), + 0, 0); + goto out; + +#undef is_node } +// Note that this only works since Linux kernel commit 0a835c4f090a +// ("Reimplement IDR and IDA using the radix tree") (in v4.11). We only need +// this since Linux kernel commit 95846ecf9dac ("pid: replace pid bitmap +// implementation with IDR API") (in v4.15) (see find_pid_in_pid_hash()), so +// that's okay. struct drgn_error *linux_helper_idr_find(struct drgn_object *res, const struct drgn_object *idr, uint64_t id) @@ -389,7 +606,7 @@ struct drgn_error *linux_helper_idr_find(struct drgn_object *res, err = drgn_object_address_of(&tmp, &tmp); if (err) goto out; - err = linux_helper_radix_tree_lookup(res, &tmp, id); + err = linux_helper_xa_load(res, &tmp, id); out: drgn_object_deinit(&tmp); return err; diff --git a/libdrgn/linux_kernel_object_find.inc.strswitch b/libdrgn/linux_kernel_object_find.inc.strswitch index 65526a3c9..78f48342f 100644 --- a/libdrgn/linux_kernel_object_find.inc.strswitch +++ b/libdrgn/linux_kernel_object_find.inc.strswitch @@ -1,21 +1,42 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// SPDX-License-Identifier: LGPL-2.1-or-later + struct drgn_error *linux_kernel_object_find(const char *name, size_t name_len, const char *filename, enum drgn_find_object_flags flags, void *arg, struct drgn_object *ret) { struct drgn_program *prog = arg; - if (!filename && (flags & DRGN_FIND_OBJECT_CONSTANT)) { + if (!filename) { @memswitch (name, name_len)@ @case "PAGE_SHIFT"@ - return linux_kernel_get_page_shift(prog, ret); + if (flags & DRGN_FIND_OBJECT_CONSTANT) + return linux_kernel_get_page_shift(prog, ret); + break; @case "PAGE_SIZE"@ - return linux_kernel_get_page_size(prog, ret); + if (flags & DRGN_FIND_OBJECT_CONSTANT) + return linux_kernel_get_page_size(prog, ret); + break; @case "PAGE_MASK"@ - return linux_kernel_get_page_mask(prog, ret); + if (flags & DRGN_FIND_OBJECT_CONSTANT) + return linux_kernel_get_page_mask(prog, ret); + break; @case "UTS_RELEASE"@ - return linux_kernel_get_uts_release(prog, ret); + if (flags & DRGN_FIND_OBJECT_CONSTANT) + return linux_kernel_get_uts_release(prog, ret); + break; + @case "VMCOREINFO"@ + if (flags & DRGN_FIND_OBJECT_CONSTANT) + return linux_kernel_get_vmcoreinfo(prog, ret); + break; + @case "jiffies"@ + if (flags & DRGN_FIND_OBJECT_VARIABLE) + return linux_kernel_get_jiffies(prog, ret); + break; @case "vmemmap"@ - return linux_kernel_get_vmemmap(prog, ret); + if (flags & DRGN_FIND_OBJECT_CONSTANT) + return linux_kernel_get_vmemmap(prog, ret); + break; @endswitch@ } return &drgn_not_found; diff --git a/libdrgn/log.c b/libdrgn/log.c new file mode 100644 index 000000000..30d39553b --- /dev/null +++ b/libdrgn/log.c @@ -0,0 +1,84 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include +#include +#include + +#include "log.h" +#include "program.h" +#include "util.h" + +LIBDRGN_PUBLIC void drgn_program_set_log_level(struct drgn_program *prog, + int level) +{ + prog->log_level = level; +} + +LIBDRGN_PUBLIC int drgn_program_get_log_level(struct drgn_program *prog) +{ + return prog->log_level; +} + +static void drgn_file_log_fn(struct drgn_program *prog, void *arg, + enum drgn_log_level level, const char *format, + va_list ap, struct drgn_error *err) +{ + FILE *file = arg; + flockfile(file); + + static const char * const prefix[] = { + [DRGN_LOG_DEBUG] = "debug: ", + [DRGN_LOG_INFO] = "info: ", + [DRGN_LOG_WARNING] = "warning: ", + [DRGN_LOG_ERROR] = "error: ", + [DRGN_LOG_CRITICAL] = "critical: ", + }; + fputs(prefix[level], file); + vfprintf(file, format, ap); + if (err) + drgn_error_fwrite(file, err); + else + putc('\n', file); + + funlockfile(file); +} + +LIBDRGN_PUBLIC void drgn_program_set_log_file(struct drgn_program *prog, + FILE *file) +{ + drgn_program_set_log_callback(prog, drgn_file_log_fn, file); +} + +LIBDRGN_PUBLIC void drgn_program_set_log_callback(struct drgn_program *prog, + drgn_log_fn *callback, + void *callback_arg) +{ + prog->log_fn = callback; + prog->log_arg = callback_arg; +} + +LIBDRGN_PUBLIC void drgn_program_get_log_callback(struct drgn_program *prog, + drgn_log_fn **callback_ret, + void **callback_arg_ret) +{ + *callback_ret = prog->log_fn; + *callback_arg_ret = prog->log_arg; +} + +bool drgn_log_is_enabled(struct drgn_program *prog, enum drgn_log_level level) +{ + return level >= prog->log_level; +} + +void drgn_error_log(enum drgn_log_level level, struct drgn_program *prog, + struct drgn_error *err, const char *format, ...) +{ + if (!drgn_log_is_enabled(prog, level)) + return; + + va_list ap; + va_start(ap, format); + prog->log_fn(prog, prog->log_arg, level, format, ap, err); + va_end(ap); +} diff --git a/libdrgn/log.h b/libdrgn/log.h new file mode 100644 index 000000000..74085afc8 --- /dev/null +++ b/libdrgn/log.h @@ -0,0 +1,89 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// SPDX-License-Identifier: LGPL-2.1-or-later + +/** + * @file + * + * Logging. + * + * See @ref LoggingInternals. + */ + +#ifndef DRGN_LOG_H +#define DRGN_LOG_H + +#include "drgn.h" + +/** + * @ingroup Internals + * + * @defgroup LoggingInternals Logging + * + * Logging functions. + * + * @{ + */ + +/** + * Return whether the given log level is enabled. + * + * This can be used to avoid expensive computations that are only needed for + * logging. + */ +bool drgn_log_is_enabled(struct drgn_program *prog, enum drgn_log_level level); + +/** + * @{ + * + * @name Logging Functions + */ + +#ifdef DOXYGEN +/** Log a printf-style message at the given level. */ +void drgn_log(enum drgn_log_level level, struct drgn_program *prog, + const char *format, ...); +#else +#define drgn_log(level, prog, ...) drgn_error_log(level, prog, NULL, __VA_ARGS__) +#endif +/** Log a critical message. */ +#define drgn_log_critical(...) drgn_log(DRGN_LOG_CRITICAL, __VA_ARGS__) +/** Log an error message. */ +#define drgn_log_error(...) drgn_log(DRGN_LOG_ERROR, __VA_ARGS__) +/** Log a warning message. */ +#define drgn_log_warning(...) drgn_log(DRGN_LOG_WARNING, __VA_ARGS__) +/** Log an informational message. */ +#define drgn_log_info(...) drgn_log(DRGN_LOG_INFO, __VA_ARGS__) +/** Log a debug message. */ +#define drgn_log_debug(...) drgn_log(DRGN_LOG_DEBUG, __VA_ARGS__) + +/** + * @} + * + * @{ + * + * @name Error Logging Functions + */ + +/** + * Log a printf-style message followed by a @ref drgn_error at the given level. + */ +__attribute__((__format__(__printf__, 4, 5))) +void drgn_error_log(enum drgn_log_level level, struct drgn_program *prog, + struct drgn_error *err, const char *format, ...); +/** Log a critical message followed by a @ref drgn_error. */ +#define drgn_error_log_critical(...) drgn_error_log(DRGN_LOG_CRIT, __VA_ARGS__) +/** Log an error message followed by a @ref drgn_error. */ +#define drgn_error_log_error(...) drgn_error_log(DRGN_LOG_ERR, __VA_ARGS__) +/** Log a warning message followed by a @ref drgn_error. */ +#define drgn_error_log_warning(...) drgn_error_log(DRGN_LOG_WARNING, __VA_ARGS__) +/** Log an informational message followed by a @ref drgn_error. */ +#define drgn_error_log_info(...) drgn_error_log(DRGN_LOG_INFO, __VA_ARGS__) +/** Log a debug message followed by a @ref drgn_error. */ +#define drgn_error_log_debug(...) drgn_error_log(DRGN_LOG_DEBUG, __VA_ARGS__) + +/** + * @} + * @} + */ + +#endif /* DRGN_LOG_H */ diff --git a/libdrgn/m4/.gitignore b/libdrgn/m4/.gitignore index afd9093a7..39d545d42 100644 --- a/libdrgn/m4/.gitignore +++ b/libdrgn/m4/.gitignore @@ -4,3 +4,4 @@ !/ax_append_flag.m4 !/ax_check_compile_flag.m4 !/ax_require_defined.m4 +!/my_c_auto.m4 diff --git a/libdrgn/m4/my_c_auto.m4 b/libdrgn/m4/my_c_auto.m4 new file mode 100644 index 000000000..d38c72436 --- /dev/null +++ b/libdrgn/m4/my_c_auto.m4 @@ -0,0 +1,21 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +# Check whether C23 auto is supported by the current compiler and compilation +# flags. If not, but the GNU __auto_type extension is supported, define auto to +# __auto_type. Otherwise, fail. +AC_DEFUN([MY_C_AUTO], +[AC_CACHE_CHECK([for auto], [my_cv_c_auto], + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([[auto x = 1;]])], + [my_cv_c_auto=yes], [my_cv_c_auto=no])]) +if test "x$my_cv_c_auto" != xyes; then + AC_CACHE_CHECK([for __auto_type], [my_cv_c___auto_type], + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([[__auto_type x = 1;]])], + [my_cv_c___auto_type=yes], [my_cv_c___auto_type=no])]) + if test "x$my_cv_c___auto_type" == xyes; then + AC_DEFINE([auto], [__auto_type]) + else + AC_MSG_ERROR([no auto or __auto_type]) + fi +fi +]) diff --git a/libdrgn/memory_reader.c b/libdrgn/memory_reader.c index 810432cd3..fb6fbb6b6 100644 --- a/libdrgn/memory_reader.c +++ b/libdrgn/memory_reader.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include @@ -36,7 +36,7 @@ drgn_memory_segment_to_key(const struct drgn_memory_segment *entry) DEFINE_BINARY_SEARCH_TREE_FUNCTIONS(drgn_memory_segment_tree, node, drgn_memory_segment_to_key, - binary_search_tree_scalar_cmp, splay) + binary_search_tree_scalar_cmp, splay); void drgn_memory_reader_init(struct drgn_memory_reader *reader) { diff --git a/libdrgn/memory_reader.h b/libdrgn/memory_reader.h index 4044cfdb9..87e04035d 100644 --- a/libdrgn/memory_reader.h +++ b/libdrgn/memory_reader.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -32,7 +32,7 @@ */ DEFINE_BINARY_SEARCH_TREE_TYPE(drgn_memory_segment_tree, - struct drgn_memory_segment) + struct drgn_memory_segment); /** * Memory reader. diff --git a/libdrgn/minmax.h b/libdrgn/minmax.h index 0c64ce703..1ae8adf7f 100644 --- a/libdrgn/minmax.h +++ b/libdrgn/minmax.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -39,13 +39,13 @@ /** @endcond */ /** - * Get the minimum of two integer constant expressions with compatible types, - * resulting in an integer constant expression. + * Get the minimum of two integer constant expressions, resulting in an integer + * constant expression. */ #define min_iconst(x, y) cmp_iconst_impl(x, y, <) /** - * Get the maximum of two integer constant expressions with compatible types, - * resulting in an integer constant expression. + * Get the maximum of two integer constant expressions, resulting in an integer + * constant expression. */ #define max_iconst(x, y) cmp_iconst_impl(x, y, >) /** @cond */ @@ -54,14 +54,9 @@ * Enforce that the arguments are integer constant expressions. The \ * size of a non-VLA array must be an integer constant expression, and \ * a compound literal cannot be a VLA. Evaluates to non-zero to fall \ - * through to the next check. \ + * through to the comparison. \ */ \ (sizeof((char [(x) * 0 + (y) * 0 + 1]){0}) && \ - /* \ - * Generate a warning if x and y do not have compatible types. \ - * Evaluates to non-zero to fall through to the comparison. \ - */ \ - sizeof((typeof(x) *)1 == (typeof(y) *)1) && \ (x) op (y) ? (x) : (y)) /** @endcond */ diff --git a/libdrgn/nstring.h b/libdrgn/nstring.h index 5fc803d57..a44d1245c 100644 --- a/libdrgn/nstring.h +++ b/libdrgn/nstring.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file diff --git a/libdrgn/object.c b/libdrgn/object.c index 3a51dec7f..1df55be11 100644 --- a/libdrgn/object.c +++ b/libdrgn/object.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include @@ -7,6 +7,7 @@ #include #include +#include "cleanup.h" #include "drgn.h" #include "error.h" #include "language.h" @@ -32,8 +33,10 @@ LIBDRGN_PUBLIC void drgn_object_init(struct drgn_object *obj, static void drgn_value_deinit(const struct drgn_object *obj, const union drgn_value *value) { - if (obj->encoding == DRGN_OBJECT_ENCODING_BUFFER && - !drgn_object_is_inline(obj)) + if ((obj->encoding == DRGN_OBJECT_ENCODING_BUFFER + && !drgn_object_is_inline(obj)) + || obj->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG + || obj->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG) free(value->bufp); } @@ -74,8 +77,8 @@ drgn_object_type_impl(struct drgn_type *type, struct drgn_type *underlying_type, ret->underlying_type = underlying_type; ret->qualifiers = qualifiers; - ret->encoding = drgn_type_object_encoding(type); - if (drgn_object_encoding_is_complete(ret->encoding)) { + if (drgn_type_is_complete(underlying_type) + && drgn_type_kind(underlying_type) != DRGN_TYPE_FUNCTION) { err = drgn_type_bit_size(type, &ret->bit_size); if (err) return err; @@ -83,9 +86,17 @@ drgn_object_type_impl(struct drgn_type *type, struct drgn_type *underlying_type, ret->bit_size = 0; } - ret->is_bit_field = bit_field_size != 0; - if (ret->encoding == DRGN_OBJECT_ENCODING_SIGNED || - ret->encoding == DRGN_OBJECT_ENCODING_UNSIGNED) { + struct drgn_type *compatible_type = underlying_type; + SWITCH_ENUM(drgn_type_kind(compatible_type), + case DRGN_TYPE_ENUM: + if (!drgn_type_is_complete(compatible_type)) { + ret->encoding = DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER; + break; + } + compatible_type = drgn_type_type(compatible_type).type; + fallthrough; + case DRGN_TYPE_INT: + case DRGN_TYPE_BOOL: { if (bit_field_size != 0) { if (bit_field_size > ret->bit_size) { return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, @@ -104,28 +115,57 @@ drgn_object_type_impl(struct drgn_type *type, struct drgn_type *underlying_type, "unsupported integer bit size (%" PRIu64 ")", ret->bit_size); } - } else { - if (bit_field_size != 0) { - return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, - "bit field must be integer"); - } - if (ret->encoding == DRGN_OBJECT_ENCODING_FLOAT && - (ret->bit_size < 1 || ret->bit_size > 256)) { + bool is_signed = + drgn_type_kind(compatible_type) == DRGN_TYPE_INT + && drgn_type_is_signed(compatible_type); + if (ret->bit_size <= 64 && is_signed) + ret->encoding = DRGN_OBJECT_ENCODING_SIGNED; + else if (ret->bit_size <= 64) + ret->encoding = DRGN_OBJECT_ENCODING_UNSIGNED; + else if (is_signed) + ret->encoding = DRGN_OBJECT_ENCODING_SIGNED_BIG; + else + ret->encoding = DRGN_OBJECT_ENCODING_UNSIGNED_BIG; + break; + } + case DRGN_TYPE_POINTER: + ret->encoding = DRGN_OBJECT_ENCODING_UNSIGNED; + break; + case DRGN_TYPE_FLOAT: + if (ret->bit_size < 1 || ret->bit_size > 256) { return drgn_error_format(DRGN_ERROR_INVALID_ARGUMENT, "unsupported floating-point bit size (%" PRIu64 ")", ret->bit_size); } - } + ret->encoding = DRGN_OBJECT_ENCODING_FLOAT; + break; + case DRGN_TYPE_STRUCT: + case DRGN_TYPE_UNION: + case DRGN_TYPE_CLASS: + case DRGN_TYPE_ARRAY: + if (drgn_type_is_complete(compatible_type)) + ret->encoding = DRGN_OBJECT_ENCODING_BUFFER; + else + ret->encoding = DRGN_OBJECT_ENCODING_INCOMPLETE_BUFFER; + break; + case DRGN_TYPE_VOID: + case DRGN_TYPE_FUNCTION: + ret->encoding = DRGN_OBJECT_ENCODING_NONE; + break; + // This is already the underlying type, so it can't be a typedef. + case DRGN_TYPE_TYPEDEF: + ) - if (drgn_type_has_little_endian(underlying_type)) { - ret->little_endian = drgn_type_little_endian(underlying_type); - } else if (drgn_type_kind(underlying_type) == DRGN_TYPE_ENUM && - drgn_type_is_complete(underlying_type)) { - ret->little_endian = - drgn_type_little_endian(drgn_type_type(underlying_type).type); - } else { - ret->little_endian = false; + if (bit_field_size != 0 + && drgn_type_kind(compatible_type) != DRGN_TYPE_INT + && drgn_type_kind(compatible_type) != DRGN_TYPE_BOOL) { + return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, + "bit field must be integer"); } + ret->is_bit_field = bit_field_size != 0; + + ret->little_endian = (drgn_type_has_little_endian(compatible_type) + && drgn_type_little_endian(compatible_type)); return NULL; } @@ -143,23 +183,37 @@ static struct drgn_error * drgn_object_type_operand(const struct drgn_operand_type *op_type, struct drgn_object_type *ret) { - return drgn_object_type_impl(op_type->type, op_type->underlying_type, - op_type->qualifiers, - op_type->bit_field_size, ret); + struct drgn_error *err; + err = drgn_object_type_impl(op_type->type, op_type->underlying_type, + op_type->qualifiers, + op_type->bit_field_size, ret); + if (err) + return err; + if (ret->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG + || ret->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG) { + return drgn_error_create(DRGN_ERROR_NOT_IMPLEMENTED, + "operations on integer values larger than 64 bits are not yet supported"); + } + return NULL; } -static struct drgn_error drgn_integer_too_big = { - .code = DRGN_ERROR_NOT_IMPLEMENTED, - .message = "integer values larger than 64 bits are not yet supported", -}; - struct drgn_error * drgn_object_set_signed_internal(struct drgn_object *res, const struct drgn_object_type *type, int64_t svalue) { - if (type->bit_size > 64) - return &drgn_integer_too_big; + if (type->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG) { + uint64_t size = drgn_value_size(type->bit_size); + void *buf = malloc64(size); + if (!buf) + return &drgn_enomem; + copy_lsbytes_fill(buf, size, type->little_endian, &svalue, + sizeof(svalue), HOST_LITTLE_ENDIAN, + svalue < 0 ? -1 : 0); + drgn_object_reinit(res, type, DRGN_OBJECT_VALUE); + res->value.bufp = buf; + return NULL; + } drgn_object_reinit(res, type, DRGN_OBJECT_VALUE); res->value.svalue = truncate_signed(svalue, type->bit_size); return NULL; @@ -175,7 +229,8 @@ drgn_object_set_signed(struct drgn_object *res, err = drgn_object_type(qualified_type, bit_field_size, &type); if (err) return err; - if (type.encoding != DRGN_OBJECT_ENCODING_SIGNED) { + if (type.encoding != DRGN_OBJECT_ENCODING_SIGNED + && type.encoding != DRGN_OBJECT_ENCODING_SIGNED_BIG) { return drgn_error_create(DRGN_ERROR_TYPE, "not a signed integer type"); } @@ -187,8 +242,17 @@ drgn_object_set_unsigned_internal(struct drgn_object *res, const struct drgn_object_type *type, uint64_t uvalue) { - if (type->bit_size > 64) - return &drgn_integer_too_big; + if (type->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG) { + uint64_t size = drgn_value_size(type->bit_size); + void *buf = malloc64(size); + if (!buf) + return &drgn_enomem; + copy_lsbytes(buf, size, type->little_endian, &uvalue, + sizeof(uvalue), HOST_LITTLE_ENDIAN); + drgn_object_reinit(res, type, DRGN_OBJECT_VALUE); + res->value.bufp = buf; + return NULL; + } drgn_object_reinit(res, type, DRGN_OBJECT_VALUE); res->value.uvalue = truncate_unsigned(uvalue, type->bit_size); return NULL; @@ -204,7 +268,8 @@ drgn_object_set_unsigned(struct drgn_object *res, err = drgn_object_type(qualified_type, bit_field_size, &type); if (err) return err; - if (type.encoding != DRGN_OBJECT_ENCODING_UNSIGNED) { + if (type.encoding != DRGN_OBJECT_ENCODING_UNSIGNED + && type.encoding != DRGN_OBJECT_ENCODING_UNSIGNED_BIG) { return drgn_error_create(DRGN_ERROR_TYPE, "not an unsigned integer type"); } @@ -216,7 +281,7 @@ static struct drgn_error drgn_float_size_unsupported = { .message = "float values which are not 32 or 64 bits are not yet supported", }; -static struct drgn_error * +struct drgn_error * drgn_object_set_float_internal(struct drgn_object *res, const struct drgn_object_type *type, double fvalue) @@ -292,13 +357,16 @@ drgn_object_set_from_buffer_internal(struct drgn_object *res, * copy to a temporary value before freeing or modifying the old value. */ union drgn_value value; - if (type->encoding == DRGN_OBJECT_ENCODING_BUFFER) { - if (bit_offset != 0) { + if (type->encoding == DRGN_OBJECT_ENCODING_BUFFER + || type->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG + || type->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG) { + if (type->encoding == DRGN_OBJECT_ENCODING_BUFFER + && bit_offset != 0) { return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, "non-scalar must be byte-aligned"); } uint64_t size = drgn_value_size(type->bit_size); - char *dst; + void *dst; if (size <= sizeof(res->value.ibuf)) { dst = value.ibuf; } else { @@ -307,13 +375,27 @@ drgn_object_set_from_buffer_internal(struct drgn_object *res, return &drgn_enomem; value.bufp = dst; } - memcpy(dst, p, size); + int dst_bit_offset = 0; + if (type->encoding != DRGN_OBJECT_ENCODING_BUFFER + && !type->little_endian) + dst_bit_offset = -type->bit_size % 8; + ((uint8_t *)dst)[0] = 0; + ((uint8_t *)dst)[size - 1] = 0; + copy_bits(dst, dst_bit_offset, p, bit_offset, type->bit_size, + type->little_endian); + if (type->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG + && type->bit_size % 8 != 0) { + int8_t *p; + if (type->little_endian) + p = (int8_t *)dst + size - 1; + else + p = (int8_t *)dst; + *p = truncate_signed8(*p, type->bit_size % 8); + } } else if (drgn_object_encoding_is_complete(type->encoding)) { - if (type->encoding == DRGN_OBJECT_ENCODING_FLOAT) { - if (type->bit_size != 32 && type->bit_size != 64) - return &drgn_float_size_unsupported; - } else if (type->bit_size > 64) - return &drgn_integer_too_big; + if (type->encoding == DRGN_OBJECT_ENCODING_FLOAT + && type->bit_size != 32 && type->bit_size != 64) + return &drgn_float_size_unsupported; drgn_value_deserialize(&value, p, bit_offset, type->encoding, type->bit_size, type->little_endian); } else { @@ -360,13 +442,21 @@ drgn_object_set_reference_internal(struct drgn_object *res, address += bit_offset / 8; address &= address_mask; bit_offset %= 8; - if (type->encoding != DRGN_OBJECT_ENCODING_SIGNED && - type->encoding != DRGN_OBJECT_ENCODING_UNSIGNED && - type->encoding != DRGN_OBJECT_ENCODING_FLOAT && - type->encoding != DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER && - bit_offset != 0) { - return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, - "non-scalar must be byte-aligned"); + if (bit_offset != 0) { + SWITCH_ENUM(type->encoding, + case DRGN_OBJECT_ENCODING_SIGNED: + case DRGN_OBJECT_ENCODING_UNSIGNED: + case DRGN_OBJECT_ENCODING_SIGNED_BIG: + case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: + case DRGN_OBJECT_ENCODING_FLOAT: + case DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER: + break; + case DRGN_OBJECT_ENCODING_NONE: + case DRGN_OBJECT_ENCODING_BUFFER: + case DRGN_OBJECT_ENCODING_INCOMPLETE_BUFFER: + return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, + "non-scalar must be byte-aligned"); + ) } if (type->bit_size > UINT64_MAX - bit_offset) { return drgn_error_format(DRGN_ERROR_OVERFLOW, @@ -421,7 +511,9 @@ drgn_object_copy(struct drgn_object *res, const struct drgn_object *obj) SWITCH_ENUM(obj->kind, case DRGN_OBJECT_VALUE: - if (obj->encoding == DRGN_OBJECT_ENCODING_BUFFER) { + if (obj->encoding == DRGN_OBJECT_ENCODING_BUFFER + || obj->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG + || obj->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG) { size_t size = drgn_object_size(obj); char *dst; const char *src; @@ -552,34 +644,87 @@ drgn_object_read_reference(const struct drgn_object *obj, obj->type); } - if (obj->encoding == DRGN_OBJECT_ENCODING_BUFFER) { - assert(obj->bit_offset == 0); + if (obj->encoding == DRGN_OBJECT_ENCODING_BUFFER + || obj->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG + || obj->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG) { + int dst_bit_offset = 0; + if (obj->encoding != DRGN_OBJECT_ENCODING_BUFFER + && !obj->little_endian) + dst_bit_offset = -obj->bit_size % 8; + uint64_t size = drgn_object_size(obj); - char *dst; - if (size <= sizeof(value->ibuf)) { - dst = value->ibuf; + void *dst; + if (obj->bit_offset == 0 && dst_bit_offset == 0) { + if (size <= sizeof(value->ibuf)) { + dst = value->ibuf; + } else { + dst = malloc64(size); + if (!dst) + return &drgn_enomem; + } + err = drgn_program_read_memory(drgn_object_program(obj), + dst, obj->address, size, + false); + if (err) { + if (dst != value->ibuf) + free(dst); + return err; + } + if (obj->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG + && obj->bit_size % 8 != 0) { + // dst_bit_offset == 0 && obj->bit_size % 8 != 0 + // implies obj->little_endian. + uint8_t *p = (uint8_t *)dst + size - 1; + *p = truncate_unsigned8(*p, obj->bit_size % 8); + } } else { - dst = malloc64(size); - if (!dst) + // bit_offset + bit_size is guaranteed not to overflow + // because bit_offset can only be non-zero for + // SIGNED_BIG and UNSIGNED_BIG, which we limit to a + // reasonable bit_size. + uint64_t read_size = + drgn_value_size(obj->bit_offset + obj->bit_size); + + // We could probably read directly into dst and move the + // bits in place if we really wanted to, but this is + // easier. + _cleanup_free_ void *tmp = malloc64(read_size); + if (!tmp) return &drgn_enomem; + err = drgn_program_read_memory(drgn_object_program(obj), + tmp, obj->address, + read_size, false); + if (err) + return err; + if (size <= sizeof(value->ibuf)) { + dst = value->ibuf; + } else { + dst = malloc64(size); + if (!dst) + return &drgn_enomem; + } + ((uint8_t *)dst)[0] = 0; + ((uint8_t *)dst)[size - 1] = 0; + copy_bits(dst, dst_bit_offset, tmp, obj->bit_offset, + obj->bit_size, obj->little_endian); } - err = drgn_program_read_memory(drgn_object_program(obj), dst, - obj->address, size, false); - if (err) { - if (dst != value->ibuf) - free(dst); - return err; + if (obj->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG + && obj->bit_size % 8 != 0) { + int8_t *p; + if (obj->little_endian) + p = (int8_t *)dst + size - 1; + else + p = (int8_t *)dst; + *p = truncate_signed8(*p, obj->bit_size % 8); } if (size > sizeof(value->ibuf)) value->bufp = dst; return NULL; } else { uint64_t bit_size = obj->bit_size; - if (obj->encoding == DRGN_OBJECT_ENCODING_FLOAT) { - if (bit_size != 32 && bit_size != 64) - return &drgn_float_size_unsupported; - } else if (bit_size > 64) - return &drgn_integer_too_big; + if (obj->encoding == DRGN_OBJECT_ENCODING_FLOAT + && bit_size != 32 && bit_size != 64) + return &drgn_float_size_unsupported; uint8_t bit_offset = obj->bit_offset; uint64_t read_size = drgn_value_size(bit_offset + bit_size); char buf[9]; @@ -653,9 +798,22 @@ drgn_object_read_bytes(const struct drgn_object *obj, void *buf) SWITCH_ENUM(obj->kind, case DRGN_OBJECT_VALUE: - if (obj->encoding == DRGN_OBJECT_ENCODING_BUFFER) { - memcpy(buf, drgn_object_buffer(obj), - drgn_object_size(obj)); + if (obj->encoding == DRGN_OBJECT_ENCODING_BUFFER + || obj->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG + || obj->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG) { + if (obj->bit_size % 8 == 0) { + memcpy(buf, drgn_object_buffer(obj), + drgn_object_size(obj)); + } else { + int bit_offset = 0; + if (obj->encoding != DRGN_OBJECT_ENCODING_BUFFER + && !obj->little_endian) + bit_offset = -obj->bit_size % 8; + ((uint8_t *)buf)[drgn_object_size(obj) - 1] = 0; + copy_bits(buf, 0, drgn_object_buffer(obj), + bit_offset, obj->bit_size, + obj->little_endian); + } } else { union { uint64_t uvalue; @@ -683,26 +841,45 @@ drgn_object_read_bytes(const struct drgn_object *obj, void *buf) } return NULL; case DRGN_OBJECT_REFERENCE: { - uint8_t bit_offset = obj->bit_offset; - uint64_t bit_size = obj->bit_size; - uint64_t read_size = drgn_value_size(bit_offset + bit_size); - if (bit_offset == 0) { - return drgn_program_read_memory(drgn_object_program(obj), - buf, obj->address, - read_size, false); + uint64_t size = drgn_object_size(obj); + if (obj->bit_offset == 0) { + err = drgn_program_read_memory(drgn_object_program(obj), + buf, obj->address, size, + false); + if (err) + return err; + if (obj->bit_size % 8 != 0) { + uint8_t *p = (uint8_t *)buf + size - 1; + if (obj->little_endian) + *p = truncate_unsigned8(*p, obj->bit_size % 8); + else + *p &= 0xff00U >> (obj->bit_size % 8); + } } else { - char tmp[9]; - assert(read_size <= sizeof(tmp)); + uint64_t read_size = + drgn_value_size(obj->bit_offset + obj->bit_size); + + void *tmp; + char tmp_small[9]; + _cleanup_free_ void *tmp_large = NULL; + if (read_size > sizeof(tmp_small)) { + tmp_large = malloc64(read_size); + if (!tmp_large) + return &drgn_enomem; + tmp = tmp_large; + } else { + tmp = tmp_small; + } err = drgn_program_read_memory(drgn_object_program(obj), tmp, obj->address, read_size, false); if (err) return err; - ((uint8_t *)buf)[drgn_value_size(bit_size) - 1] = 0; - copy_bits(buf, 0, tmp, bit_offset, obj->bit_size, + ((uint8_t *)buf)[size - 1] = 0; + copy_bits(buf, 0, tmp, obj->bit_offset, obj->bit_size, obj->little_endian); - return NULL; } + return NULL; } case DRGN_OBJECT_ABSENT: return &drgn_error_object_absent; @@ -760,10 +937,17 @@ drgn_object_value_float(const struct drgn_object *obj, double *ret) return err; } +static struct drgn_error drgn_integer_too_big = { + .code = DRGN_ERROR_OVERFLOW, + .message = "integer type is too big", +}; + LIBDRGN_PUBLIC struct drgn_error * drgn_object_read_signed(const struct drgn_object *obj, int64_t *ret) { - if (obj->encoding != DRGN_OBJECT_ENCODING_SIGNED) { + if (obj->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG) { + return &drgn_integer_too_big; + } else if (obj->encoding != DRGN_OBJECT_ENCODING_SIGNED) { return drgn_error_create(DRGN_ERROR_TYPE, "not a signed integer"); } @@ -773,7 +957,9 @@ drgn_object_read_signed(const struct drgn_object *obj, int64_t *ret) LIBDRGN_PUBLIC struct drgn_error * drgn_object_read_unsigned(const struct drgn_object *obj, uint64_t *ret) { - if (obj->encoding != DRGN_OBJECT_ENCODING_UNSIGNED) { + if (obj->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG) { + return &drgn_integer_too_big; + } else if (obj->encoding != DRGN_OBJECT_ENCODING_UNSIGNED) { return drgn_error_create(DRGN_ERROR_TYPE, "not an unsigned integer"); } @@ -787,11 +973,12 @@ drgn_object_read_integer(const struct drgn_object *obj, union drgn_value *ret) union drgn_value value_mem; const union drgn_value *value; - if (obj->encoding != DRGN_OBJECT_ENCODING_SIGNED && - obj->encoding != DRGN_OBJECT_ENCODING_UNSIGNED) { - return drgn_error_create(DRGN_ERROR_TYPE, - "not an integer"); - } + if (obj->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG + || obj->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG) + return &drgn_integer_too_big; + else if (obj->encoding != DRGN_OBJECT_ENCODING_SIGNED + && obj->encoding != DRGN_OBJECT_ENCODING_UNSIGNED) + return drgn_error_create(DRGN_ERROR_TYPE, "not an integer"); err = drgn_object_read_value(obj, &value_mem, &value); if (err) return err; @@ -906,6 +1093,9 @@ drgn_object_convert_signed(const struct drgn_object *obj, uint64_t bit_size, case DRGN_OBJECT_ENCODING_FLOAT: *ret = truncate_signed(value->fvalue, bit_size); break; + case DRGN_OBJECT_ENCODING_SIGNED_BIG: + case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: + return &drgn_integer_too_big; default: err = drgn_error_create(DRGN_ERROR_TYPE, "object cannot be converted to integer"); @@ -934,6 +1124,9 @@ drgn_object_convert_unsigned(const struct drgn_object *obj, uint64_t bit_size, case DRGN_OBJECT_ENCODING_FLOAT: *ret = truncate_unsigned(value->fvalue, bit_size); break; + case DRGN_OBJECT_ENCODING_SIGNED_BIG: + case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: + return &drgn_integer_too_big; default: err = drgn_error_create(DRGN_ERROR_TYPE, "object cannot be converted to integer"); @@ -963,6 +1156,9 @@ drgn_object_convert_float(const struct drgn_object *obj, double *fvalue) case DRGN_OBJECT_ENCODING_FLOAT: *fvalue = value->fvalue; break; + case DRGN_OBJECT_ENCODING_SIGNED_BIG: + case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: + return &drgn_integer_too_big; default: err = drgn_error_create(DRGN_ERROR_TYPE, "object cannot be converted to floating-point"); @@ -1071,6 +1267,23 @@ drgn_object_is_zero_impl(const struct drgn_object *obj, bool *ret) *ret = false; return NULL; } + case DRGN_OBJECT_ENCODING_SIGNED_BIG: + case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: { + union drgn_value value_mem; + const union drgn_value *value; + err = drgn_object_read_value(obj, &value_mem, &value); + if (err) + return err; + size_t size = drgn_object_size(obj); + for (size_t i = 0; i < size; i++) { + if (value->bufp[i] != 0) { + *ret = false; + break; + } + } + drgn_object_deinit_value(obj, value); + return NULL; + } case DRGN_OBJECT_ENCODING_FLOAT: { double fvalue; @@ -1098,7 +1311,7 @@ drgn_object_is_zero_impl(const struct drgn_object *obj, bool *ret) default: break; } - /* fallthrough */ + fallthrough; } default: return drgn_error_create(DRGN_ERROR_TYPE, @@ -1543,6 +1756,9 @@ static struct drgn_error *pointer_operand(const struct drgn_object *ptr, switch (ptr->encoding) { case DRGN_OBJECT_ENCODING_UNSIGNED: return drgn_object_value_unsigned(ptr, ret); + case DRGN_OBJECT_ENCODING_SIGNED_BIG: + case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: + return &drgn_integer_too_big; case DRGN_OBJECT_ENCODING_BUFFER: case DRGN_OBJECT_ENCODING_NONE: case DRGN_OBJECT_ENCODING_INCOMPLETE_BUFFER: @@ -1851,6 +2067,9 @@ drgn_op_add_to_pointer(struct drgn_object *res, if (err) return err; break; + case DRGN_OBJECT_ENCODING_SIGNED_BIG: + case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: + return &drgn_integer_too_big; default: return drgn_error_create(DRGN_ERROR_TYPE, "invalid addend type for pointer arithmetic"); diff --git a/libdrgn/object.h b/libdrgn/object.h index db2e2bcfa..8fbac1d85 100644 --- a/libdrgn/object.h +++ b/libdrgn/object.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -153,6 +153,15 @@ drgn_object_set_unsigned_internal(struct drgn_object *res, const struct drgn_object_type *type, uint64_t uvalue); +/** + * Like @ref drgn_object_set_float() but @ref drgn_object_type() was already + * called and the type is already known to be a floating-point type. + */ +struct drgn_error * +drgn_object_set_float_internal(struct drgn_object *res, + const struct drgn_object_type *type, + double fvalue); + /** * Like @ref drgn_object_set_from_buffer() but @ref drgn_object_type() was * already called and the bounds of the buffer have already been checked. diff --git a/libdrgn/object_index.c b/libdrgn/object_index.c index 74142a15d..0f3b791b2 100644 --- a/libdrgn/object_index.c +++ b/libdrgn/object_index.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include diff --git a/libdrgn/object_index.h b/libdrgn/object_index.h index f551118e4..752bd5b60 100644 --- a/libdrgn/object_index.h +++ b/libdrgn/object_index.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file diff --git a/libdrgn/openmp.c b/libdrgn/openmp.c new file mode 100644 index 000000000..6c317fdb7 --- /dev/null +++ b/libdrgn/openmp.c @@ -0,0 +1,90 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// SPDX-License-Identifier: LGPL-2.1-or-later + +#ifdef _OPENMP + +#include +#include +#include +#include + +#include "cleanup.h" +#include "hash_table.h" +#include "openmp.h" +#include "util.h" + +DEFINE_HASH_SET(int_set, int, int_key_hash_pair, scalar_key_eq); + +int drgn_num_threads; + +struct cpu_list_state { + int current; + int end; +}; + +static int cpu_list_next(FILE *file, struct cpu_list_state *state) +{ + if (state->current >= state->end) { + if (fscanf(file, "%d", &state->current) < 1) + return -1; + if (fscanf(file, "-%d", &state->end) >= 1) + state->end++; + else + state->end = state->current + 1; + fgetc(file); + } + return state->current++; +} + +__attribute__((__constructor__)) +static void drgn_init_num_threads(void) +{ + // If the number of threads was set explicitly or anything below fails, + // use the current OpenMP setting. + drgn_num_threads = omp_get_max_threads(); + if (getenv("OMP_NUM_THREADS")) + return; + + // Simultaneous multithreading rarely helps, and often slows down, our + // parallel indexing. Limit the number of threads to the number of + // online CPU cores, ignoring hardware threads. + + #define SIBLINGS_FORMAT "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list" + char siblings_path[sizeof(SIBLINGS_FORMAT) + - sizeof("%d") + + max_decimal_length(int) + + 1]; + + int num_threads = 0; + _cleanup_(int_set_deinit) struct int_set cpus_seen = HASH_TABLE_INIT; + + _cleanup_fclose_ FILE *online = fopen("/sys/devices/system/cpu/online", "r"); + if (!online) + return; + struct cpu_list_state online_state = {}; + int cpu; + while ((cpu = cpu_list_next(online, &online_state)) >= 0) { + if (int_set_search(&cpus_seen, &cpu).entry) + continue; + + num_threads++; + + snprintf(siblings_path, sizeof(siblings_path), SIBLINGS_FORMAT, + cpu); + _cleanup_fclose_ FILE *siblings = fopen(siblings_path, "r"); + if (!siblings) + continue; + struct cpu_list_state siblings_state = {}; + int sibling_cpu; + while ((sibling_cpu = cpu_list_next(siblings, &siblings_state)) + >= 0) { + if (int_set_insert(&cpus_seen, &sibling_cpu, NULL) < 0) + return; + } + } + + if (num_threads > 0 && num_threads < drgn_num_threads) + drgn_num_threads = num_threads; +} + +#endif /* _OPENMP */ diff --git a/libdrgn/openmp.h b/libdrgn/openmp.h new file mode 100644 index 000000000..d0ce3efc1 --- /dev/null +++ b/libdrgn/openmp.h @@ -0,0 +1,20 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// SPDX-License-Identifier: LGPL-2.1-or-later + +#ifndef DRGN_OPENMP_H +#define DRGN_OPENMP_H + +#ifdef _OPENMP +#include // IWYU pragma: export + +extern int drgn_num_threads; +#else +static inline int omp_get_thread_num(void) +{ + return 0; +} + +#define drgn_num_threads 1 +#endif + +#endif /* DRGN_OPENMP_H */ diff --git a/libdrgn/orc.h b/libdrgn/orc.h index c3b42c86d..05cabb1b1 100644 --- a/libdrgn/orc.h +++ b/libdrgn/orc.h @@ -1,15 +1,29 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file * * ORC unwinder definitions. * - * As of Linux v5.12, ORC is only defined for x86-64. This file assumes that the + * As of Linux v6.4, ORC is only defined for x86-64. This file assumes that the * overall format would be the same for other architectures other than * architecture-specific register numbers, but this may require reorganization * if that isn't the case. + * + * There are multiple versions of the ORC format: + * + * - Version 3 since since Linux kernel commit fb799447ae29 ("x86,objtool: Split + * UNWIND_HINT_EMPTY in two") (in v6.4). + * - Version 2 between that and Linux kernel commit ffb1b4a41016 + * ("x86/unwind/orc: Add 'signal' field to ORC metadata") (in v6.3). + * - Version 1 before that, introduced in Linux kernel commit ee9f8fce9964 + * ("x86/unwind: Add the ORC unwinder") (in v4.14). + * + * (The version numbers are our own invention and aren't used upstream.) + * + * So far, the format changes only affect the interpretation of @ref + * drgn_orc_entry::flags. The getters assume the latest version. */ #ifndef DRGN_ORC_H @@ -21,18 +35,29 @@ struct drgn_orc_entry { int16_t sp_offset; int16_t bp_offset; - /* - * This is represented by 4 bit fields in the Linux kernel, but this is - * easier to deal with. + /** + * Bit layout by version: + * + * |Version| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| + * |-------|---|---|---|---|---|---|---|---|---|---|---|---| + * | 3| sp_reg |||| bp_reg |||| type ||| S | + * | 2| sp_reg |||| bp_reg |||| type || S | E | + * | 1| sp_reg |||| bp_reg |||| type || E | | + * + * S = signal + * E = end */ uint16_t flags; }; -/* These correspond to UNWIND_HINT_* in the Linux kernel. */ enum { - DRGN_ORC_TYPE_CALL = 0, - DRGN_ORC_TYPE_REGS = 1, - DRGN_ORC_TYPE_REGS_PARTIAL = 2, + // In versions 1 and 2, UNDEFINED and END_OF_STACK didn't exist, and + // CALL = 0, REGS = 1, and REGS_PARTIAL = 2. + DRGN_ORC_TYPE_UNDEFINED = 0, + DRGN_ORC_TYPE_END_OF_STACK = 1, + DRGN_ORC_TYPE_CALL = 2, + DRGN_ORC_TYPE_REGS = 3, + DRGN_ORC_TYPE_REGS_PARTIAL = 4, }; static inline int drgn_orc_sp_reg(const struct drgn_orc_entry *orc) @@ -47,17 +72,12 @@ static inline int drgn_orc_bp_reg(const struct drgn_orc_entry *orc) static inline int drgn_orc_type(const struct drgn_orc_entry *orc) { - return (orc->flags >> 8) & 0x3; -} - -static inline bool drgn_orc_is_end(const struct drgn_orc_entry *orc) -{ - return orc->flags & 0x400; + return (orc->flags >> 8) & 0x7; } -static inline bool drgn_orc_flags_is_terminator(uint16_t flags) +static inline bool drgn_orc_signal(const struct drgn_orc_entry *orc) { - return (flags & 0x40f) == 0; + return orc->flags & 0x800; } #endif /* DRGN_ORC_H */ diff --git a/libdrgn/orc_info.c b/libdrgn/orc_info.c index 6fe9bc486..09221ca1b 100644 --- a/libdrgn/orc_info.c +++ b/libdrgn/orc_info.c @@ -1,43 +1,63 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include +#include #include #include #include "debug_info.h" // IWYU pragma: associated +#include "elf_file.h" #include "error.h" #include "orc.h" +#include "platform.h" +#include "program.h" #include "util.h" -void drgn_orc_module_info_deinit(struct drgn_debug_info_module *module) +void drgn_module_orc_info_deinit(struct drgn_module *module) { free(module->orc.entries); free(module->orc.pc_offsets); } -/* - * Get the program counter of an ORC entry directly from the .orc_unwind_ip - * section. - */ -static inline uint64_t drgn_raw_orc_pc(struct drgn_debug_info_module *module, - size_t i) +// Getters for "raw" ORC information, i.e., before it is aligned, byte swapped, +// and normalized to the latest version. +static inline uint64_t drgn_raw_orc_pc(struct drgn_module *module, + unsigned int i) { int32_t offset; - memcpy(&offset, - (int32_t *)module->scn_data[DRGN_SCN_ORC_UNWIND_IP]->d_buf + i, - sizeof(offset)); - if (drgn_platform_bswap(&module->platform)) + memcpy(&offset, &module->orc.pc_offsets[i], sizeof(offset)); + if (drgn_elf_file_bswap(module->debug_file)) offset = bswap_32(offset); return module->orc.pc_base + UINT64_C(4) * i + offset; } -static int compare_orc_entries(const void *a, const void *b, void *arg) +static bool +drgn_raw_orc_entry_is_terminator(struct drgn_module *module, unsigned int i) +{ + uint16_t flags; + memcpy(&flags, &module->orc.entries[i].flags, sizeof(flags)); + if (drgn_elf_file_bswap(module->debug_file)) + flags = bswap_16(flags); + if (module->orc.version >= 3) { + // orc->type == ORC_TYPE_UNDEFINED + return (flags & 0x700) == 0; + } else if (module->orc.version == 2) { + // orc->sp_reg == ORC_REG_UNDEFINED && !orc->end + return (flags & 0x80f) == 0; + } else { + // orc->sp_reg == ORC_REG_UNDEFINED && !orc->end + return (flags & 0x40f) == 0; + } +} + +static _Thread_local struct drgn_module *compare_orc_entries_module; +static int compare_orc_entries(const void *a, const void *b) { - struct drgn_debug_info_module *module = arg; - size_t index_a = *(size_t *)a; - size_t index_b = *(size_t *)b; + struct drgn_module *module = compare_orc_entries_module; + unsigned int index_a = *(unsigned int *)a; + unsigned int index_b = *(unsigned int *)b; uint64_t pc_a = drgn_raw_orc_pc(module, index_a); uint64_t pc_b = drgn_raw_orc_pc(module, index_b); @@ -50,25 +70,16 @@ static int compare_orc_entries(const void *a, const void *b, void *arg) * If two entries have the same PC, then one is probably a "terminator" * at the end of a compilation unit. Prefer the real entry. */ - const struct drgn_orc_entry *entries = - module->scn_data[DRGN_SCN_ORC_UNWIND]->d_buf; - uint16_t flags_a, flags_b; - memcpy(&flags_a, &entries[index_a].flags, sizeof(flags_a)); - memcpy(&flags_b, &entries[index_b].flags, sizeof(flags_b)); - if (drgn_platform_bswap(&module->platform)) { - flags_a = bswap_16(flags_a); - flags_b = bswap_16(flags_b); - } - return (drgn_orc_flags_is_terminator(flags_b) - - drgn_orc_flags_is_terminator(flags_a)); + return (drgn_raw_orc_entry_is_terminator(module, index_b) + - drgn_raw_orc_entry_is_terminator(module, index_a)); } -static size_t keep_orc_entry(struct drgn_debug_info_module *module, - size_t *indices, size_t num_entries, size_t i) +static unsigned int keep_orc_entry(struct drgn_module *module, + unsigned int *indices, + unsigned int num_entries, unsigned int i) { - const struct drgn_orc_entry *entries = - module->scn_data[DRGN_SCN_ORC_UNWIND]->d_buf; + const struct drgn_orc_entry *entries = module->orc.entries; if (num_entries > 0 && memcmp(&entries[indices[num_entries - 1]], &entries[indices[i]], sizeof(entries[0])) == 0) { @@ -87,17 +98,22 @@ static size_t keep_orc_entry(struct drgn_debug_info_module *module, * The vast majority of ORC entries are redundant with DWARF CFI, and it's a * waste to store and binary search those entries. This removes ORC entries that * are entirely shadowed by DWARF FDEs. + * + * Note that we don't bother checking EH CFI because currently ORC is only used + * for the Linux kernel on x86-64, which explicitly disables EH data. */ -static size_t remove_fdes_from_orc(struct drgn_debug_info_module *module, - size_t *indices, size_t num_entries) +static unsigned int remove_fdes_from_orc(struct drgn_module *module, + unsigned int *indices, + unsigned int num_entries) { - if (module->dwarf.num_fdes == 0) + if (module->dwarf.debug_frame.num_fdes == 0) return num_entries; - struct drgn_dwarf_fde *fde = module->dwarf.fdes; - struct drgn_dwarf_fde *last_fde = fde + module->dwarf.num_fdes - 1; + struct drgn_dwarf_fde *fde = module->dwarf.debug_frame.fdes; + struct drgn_dwarf_fde *last_fde = + fde + module->dwarf.debug_frame.num_fdes - 1; - size_t new_num_entries = 0; + unsigned int new_num_entries = 0; /* Keep any entries that start before the first DWARF FDE. */ uint64_t start_pc; @@ -110,7 +126,7 @@ static size_t remove_fdes_from_orc(struct drgn_debug_info_module *module, return num_entries; } - for (size_t i = new_num_entries; i < num_entries - 1; i++) { + for (unsigned int i = new_num_entries; i < num_entries - 1; i++) { uint64_t end_pc = drgn_raw_orc_pc(module, i + 1); /* @@ -164,59 +180,175 @@ static size_t remove_fdes_from_orc(struct drgn_debug_info_module *module, num_entries - 1); } -static struct drgn_error * -drgn_debug_info_parse_orc(struct drgn_debug_info_module *module) +static int orc_version_from_header(Elf_Data *orc_header) +{ + if (orc_header->d_size != 20) + return -1; + + // Known version identifiers in .orc_header. These can be generated in + // the kernel source tree with: + // sh ./scripts/orc_hash.sh < arch/x86/include/asm/orc_types.h | sed -e 's/^#define ORC_HASH //' -e 's/,/, /g' + + // Linux kernel commit fb799447ae29 ("x86,objtool: Split + // UNWIND_HINT_EMPTY in two") (in v6.4) + static const uint8_t orc_hash_6_4[20] = { + 0xfe, 0x5d, 0x32, 0xbf, 0x58, 0x1b, 0xd6, 0x3b, 0x2c, 0xa9, + 0xa5, 0xc6, 0x5b, 0xa5, 0xa6, 0x25, 0xea, 0xb3, 0xfe, 0x24, + }; + // Linux kernel commit ffb1b4a41016 ("x86/unwind/orc: Add 'signal' field + // to ORC metadata") (in v6.3) + static const uint8_t orc_hash_6_3[20] = { + 0xdb, 0x84, 0xae, 0xd4, 0x10, 0x3b, 0x31, 0xdd, 0x51, 0xe0, + 0x17, 0xf8, 0xf7, 0x97, 0x83, 0xca, 0x98, 0x5c, 0x2c, 0x51, + }; + + if (memcmp(orc_header->d_buf, orc_hash_6_4, 20) == 0) + return 3; + else if (memcmp(orc_header->d_buf, orc_hash_6_3, 20) == 0) + return 2; + return -1; +} + +static int orc_version_from_osrelease(struct drgn_program *prog) +{ + char *p = (char *)prog->vmcoreinfo.osrelease; + long major = strtol(p, &p, 10); + long minor = 0; + if (*p == '.') + minor = strtol(p + 1, NULL, 10); + if (major > 6 || (major == 6 && minor >= 4)) + return 3; + else if (major == 6 && minor == 3) + return 2; + else + return 1; +} + +static struct drgn_error *drgn_read_orc_sections(struct drgn_module *module) { struct drgn_error *err; + Elf *elf = module->debug_file->elf; - if (!module->platform.arch->orc_to_cfi || - !module->scns[DRGN_SCN_ORC_UNWIND_IP] || - !module->scns[DRGN_SCN_ORC_UNWIND]) + size_t shstrndx; + if (elf_getshdrstrndx(elf, &shstrndx)) + return drgn_error_libelf(); + + Elf_Scn *orc_unwind_ip_scn = NULL; + Elf_Scn *orc_unwind_scn = NULL; + Elf_Scn *orc_header_scn = NULL; + + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn(elf, scn))) { + GElf_Shdr shdr_mem, *shdr = gelf_getshdr(scn, &shdr_mem); + if (!shdr) + return drgn_error_libelf(); + + if (shdr->sh_type != SHT_PROGBITS) + continue; + + const char *scnname = elf_strptr(elf, shstrndx, shdr->sh_name); + if (!scnname) + return drgn_error_libelf(); + + if (!orc_unwind_ip_scn + && strcmp(scnname, ".orc_unwind_ip") == 0) { + orc_unwind_ip_scn = scn; + module->orc.pc_base = shdr->sh_addr; + } else if (!orc_unwind_scn + && strcmp(scnname, ".orc_unwind") == 0) { + orc_unwind_scn = scn; + } else if (!orc_header_scn + && strcmp(scnname, ".orc_header") == 0) { + orc_header_scn = scn; + } + } + + if (!orc_unwind_ip_scn || !orc_unwind_scn) { + module->orc.num_entries = 0; return NULL; + } - GElf_Shdr shdr_mem, *shdr; - shdr = gelf_getshdr(module->scns[DRGN_SCN_ORC_UNWIND_IP], &shdr_mem); - if (!shdr) - return drgn_error_libelf(); - module->orc.pc_base = shdr->sh_addr; + // Since Linux kernel b9f174c811e3 ("x86/unwind/orc: Add ELF section + // with ORC version identifier") (in v6.4), which was also backported to + // Linux 6.3.10, vmlinux and kernel modules have a .orc_header ELF + // section containing a 20-byte hash identifying the ORC version. + // + // Because there are 6.3 and 6.4 kernels without .orc_header, we have to + // fall back to checking the kernel version. + if (orc_header_scn) { + Elf_Data *orc_header; + err = read_elf_section(orc_header_scn, &orc_header); + if (err) + return err; + module->orc.version = orc_version_from_header(orc_header); + if (module->orc.version < 0) { + return drgn_error_create(DRGN_ERROR_OTHER, + "unrecognized .orc_header"); + } + } else { + module->orc.version = orc_version_from_osrelease(module->prog); + } - err = drgn_debug_info_module_cache_section(module, - DRGN_SCN_ORC_UNWIND_IP); + Elf_Data *orc_unwind_ip, *orc_unwind; + err = read_elf_section(orc_unwind_ip_scn, &orc_unwind_ip); if (err) return err; - err = drgn_debug_info_module_cache_section(module, DRGN_SCN_ORC_UNWIND); + err = read_elf_section(orc_unwind_scn, &orc_unwind); if (err) return err; - Elf_Data *orc_unwind_ip = module->scn_data[DRGN_SCN_ORC_UNWIND_IP]; - Elf_Data *orc_unwind = module->scn_data[DRGN_SCN_ORC_UNWIND]; size_t num_entries = orc_unwind_ip->d_size / sizeof(int32_t); + if (num_entries > UINT_MAX) { + return drgn_error_create(DRGN_ERROR_OTHER, + ".orc_unwind_ip is too large"); + } + module->orc.num_entries = num_entries; + if (orc_unwind_ip->d_size % sizeof(int32_t) != 0 || orc_unwind->d_size % sizeof(struct drgn_orc_entry) != 0 || - orc_unwind->d_size / sizeof(struct drgn_orc_entry) != num_entries) { + orc_unwind->d_size / sizeof(struct drgn_orc_entry) + != module->orc.num_entries) { return drgn_error_create(DRGN_ERROR_OTHER, ".orc_unwind_ip and/or .orc_unwind has invalid size"); } - if (!num_entries) + + module->orc.pc_offsets = orc_unwind_ip->d_buf; + module->orc.entries = orc_unwind->d_buf; + + return NULL; +} + +static struct drgn_error *drgn_debug_info_parse_orc(struct drgn_module *module) +{ + struct drgn_error *err; + + if (!module->debug_file->platform.arch->orc_to_cfi) return NULL; - size_t *indices = malloc_array(num_entries, sizeof(indices[0])); - if (!indices) - return &drgn_enomem; - for (size_t i = 0; i < num_entries; i++) + err = drgn_read_orc_sections(module); + if (err || !module->orc.num_entries) + goto out_clear; + + unsigned int num_entries = module->orc.num_entries; + unsigned int *indices = malloc_array(num_entries, sizeof(indices[0])); + if (!indices) { + err = &drgn_enomem; + goto out_clear; + } + for (unsigned int i = 0; i < num_entries; i++) indices[i] = i; + compare_orc_entries_module = module; /* * Sort the ORC entries for binary search. Since Linux kernel commit * f14bf6a350df ("x86/unwind/orc: Remove boot-time ORC unwind tables * sorting") (in v5.6), this is already sorted for vmlinux, so only sort * it if necessary. */ - for (size_t i = 1; i < num_entries; i++) { - if (compare_orc_entries(&indices[i - 1], &indices[i], - module) > 0) { - qsort_r(indices, num_entries, sizeof(indices[0]), - compare_orc_entries, module); + for (unsigned int i = 1; i < num_entries; i++) { + if (compare_orc_entries(&indices[i - 1], &indices[i]) > 0) { + qsort(indices, num_entries, sizeof(indices[0]), + compare_orc_entries); break; } } @@ -235,23 +367,57 @@ drgn_debug_info_parse_orc(struct drgn_debug_info_module *module) err = &drgn_enomem; goto out; } - const int32_t *orig_offsets = orc_unwind_ip->d_buf; - const struct drgn_orc_entry *orig_entries = orc_unwind->d_buf; - bool bswap = drgn_platform_bswap(&module->platform); - for (size_t i = 0; i < num_entries; i++) { - size_t index = indices[i]; + const int32_t *orig_offsets = module->orc.pc_offsets; + const struct drgn_orc_entry *orig_entries = module->orc.entries; + const bool bswap = drgn_elf_file_bswap(module->debug_file); + const int version = module->orc.version; + for (unsigned int i = 0; i < num_entries; i++) { + unsigned int index = indices[i]; int32_t offset; memcpy(&offset, &orig_offsets[index], sizeof(offset)); - struct drgn_orc_entry entry; - memcpy(&entry, &orig_entries[index], sizeof(entry)); + memcpy(&entries[i], &orig_entries[index], sizeof(entries[i])); if (bswap) { offset = bswap_32(offset); - entry.sp_offset = bswap_16(entry.sp_offset); - entry.bp_offset = bswap_16(entry.bp_offset); - entry.flags = bswap_16(entry.flags); + entries[i].sp_offset = bswap_16(entries[i].sp_offset); + entries[i].bp_offset = bswap_16(entries[i].bp_offset); + entries[i].flags = bswap_16(entries[i].flags); + } + // "Upgrade" the format to version 3. See struct + // drgn_orc_type::flags. + if (version == 2) { + // There are no UNDEFINED or END_OF_STACK types in + // versions 1 and 2. Instead, sp_reg == + // ORC_REG_UNDEFINED && !end is equivalent to UNDEFINED, + // and sp_reg == ORC_REG_UNDEFINED && end is equivalent + // to END_OF_STACK. + int type; + if ((entries[i].flags & 0x80f) == 0) + type = DRGN_ORC_TYPE_UNDEFINED << 8; + else if ((entries[i].flags & 0x80f) == 0x800) + type = DRGN_ORC_TYPE_END_OF_STACK << 8; + else + type = (entries[i].flags & 0x300) + 0x200; + int signal = (entries[i].flags & 0x400) << 1; + entries[i].flags = ((entries[i].flags & 0xff) + | type + | signal); + } else if (version == 1) { + int type; + if ((entries[i].flags & 0x40f) == 0) + type = DRGN_ORC_TYPE_UNDEFINED << 8; + else if ((entries[i].flags & 0x40f) == 0x400) + type = DRGN_ORC_TYPE_END_OF_STACK << 8; + else + type = (entries[i].flags & 0x300) + 0x200; + // There is no signal flag in version 1. Instead, + // ORC_TYPE_REGS and ORC_TYPE_REGS_PARTIAL imply the + // signal flag, and ORC_TYPE_CALL does not. + int signal = (entries[i].flags & 0x300) > 0 ? 0x800 : 0; + entries[i].flags = ((entries[i].flags & 0xff) + | type + | signal); } pc_offsets[i] = UINT64_C(4) * index + offset - UINT64_C(4) * i; - entries[i] = entry; } module->orc.pc_offsets = pc_offsets; @@ -261,21 +427,23 @@ drgn_debug_info_parse_orc(struct drgn_debug_info_module *module) err = NULL; out: free(indices); + if (err) { +out_clear: + module->orc.pc_offsets = NULL; + module->orc.entries = NULL; + } return err; } -static inline uint64_t drgn_orc_pc(struct drgn_debug_info_module *module, - size_t i) +static inline uint64_t drgn_orc_pc(struct drgn_module *module, unsigned int i) { return module->orc.pc_base + UINT64_C(4) * i + module->orc.pc_offsets[i]; } struct drgn_error * -drgn_debug_info_find_orc_cfi(struct drgn_debug_info_module *module, - uint64_t unbiased_pc, - struct drgn_cfi_row **row_ret, - bool *interrupted_ret, - drgn_register_number *ret_addr_regno_ret) +drgn_module_find_orc_cfi(struct drgn_module *module, uint64_t pc, + struct drgn_cfi_row **row_ret, bool *interrupted_ret, + drgn_register_number *ret_addr_regno_ret) { struct drgn_error *err; @@ -286,6 +454,7 @@ drgn_debug_info_find_orc_cfi(struct drgn_debug_info_module *module, module->parsed_orc = true; } + uint64_t unbiased_pc = pc - module->debug_file_bias; /* * We don't know the maximum program counter covered by the ORC data, * but the last entry seems to always be a terminator, so it doesn't @@ -293,9 +462,9 @@ drgn_debug_info_find_orc_cfi(struct drgn_debug_info_module *module, */ if (!module->orc.num_entries || unbiased_pc < drgn_orc_pc(module, 0)) return &drgn_not_found; - size_t lo = 0, hi = module->orc.num_entries, found = 0; + unsigned int lo = 0, hi = module->orc.num_entries, found = 0; while (lo < hi) { - size_t mid = lo + (hi - lo) / 2; + unsigned int mid = lo + (hi - lo) / 2; if (drgn_orc_pc(module, mid) <= unbiased_pc) { found = mid; lo = mid + 1; @@ -303,7 +472,8 @@ drgn_debug_info_find_orc_cfi(struct drgn_debug_info_module *module, hi = mid; } } - return module->platform.arch->orc_to_cfi(&module->orc.entries[found], - row_ret, interrupted_ret, - ret_addr_regno_ret); + return module->debug_file->platform.arch->orc_to_cfi(&module->orc.entries[found], + row_ret, + interrupted_ret, + ret_addr_regno_ret); } diff --git a/libdrgn/orc_info.h b/libdrgn/orc_info.h index 4aee69eef..47499fcfc 100644 --- a/libdrgn/orc_info.h +++ b/libdrgn/orc_info.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -18,7 +18,7 @@ #include "cfi.h" -struct drgn_debug_info_module; +struct drgn_module; /** * @ingroup DebugInfo @@ -26,15 +26,15 @@ struct drgn_debug_info_module; * @{ */ -/** ORC unwinder data for a @ref drgn_debug_info_module. */ -struct drgn_orc_module_info { +/** ORC unwinder data for a @ref drgn_module. */ +struct drgn_module_orc_info { /** * Base for calculating program counter corresponding to an ORC unwinder * entry. * * This is the address of the `.orc_unwind_ip` ELF section. * - * @sa drgn_orc_module_info::entries + * @sa drgn_module_orc_info::entries */ uint64_t pc_base; /** @@ -44,14 +44,15 @@ struct drgn_orc_module_info { * This is the contents of the `.orc_unwind_ip` ELF section, byte * swapped to the host's byte order if necessary. * - * @sa drgn_orc_module_info::entries + * @sa drgn_module_orc_info::entries */ int32_t *pc_offsets; /** * ORC unwinder entries. * * This is the contents of the `.orc_unwind` ELF section, byte swapped - * to the host's byte order if necessary. + * to the host's byte order and normalized to the latest version of the + * format if necessary. * * Entry `i` specifies how to unwind the stack if * `orc_pc(i) <= PC < orc_pc(i + 1)`, where @@ -59,17 +60,17 @@ struct drgn_orc_module_info { */ struct drgn_orc_entry *entries; /** Number of ORC unwinder entries. */ - size_t num_entries; + unsigned int num_entries; + /** Version of the ORC format. See @ref orc.h. */ + int version; }; -void drgn_orc_module_info_deinit(struct drgn_debug_info_module *module); +void drgn_module_orc_info_deinit(struct drgn_module *module); struct drgn_error * -drgn_debug_info_find_orc_cfi(struct drgn_debug_info_module *module, - uint64_t unbiased_pc, - struct drgn_cfi_row **row_ret, - bool *interrupted_ret, - drgn_register_number *ret_addr_regno_ret); +drgn_module_find_orc_cfi(struct drgn_module *module, uint64_t pc, + struct drgn_cfi_row **row_ret, bool *interrupted_ret, + drgn_register_number *ret_addr_regno_ret); /** @} */ diff --git a/libdrgn/path.c b/libdrgn/path.c index 14bab3258..21b5fe632 100644 --- a/libdrgn/path.c +++ b/libdrgn/path.c @@ -1,11 +1,11 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later -#include #include #include #include "drgn.h" +#include "dwarf_constants.h" #include "path.h" #include "util.h" diff --git a/libdrgn/path.h b/libdrgn/path.h index e03cd2fd9..5707c832c 100644 --- a/libdrgn/path.h +++ b/libdrgn/path.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file diff --git a/libdrgn/platform.c b/libdrgn/platform.c index 645ba0078..f4018d4ae 100644 --- a/libdrgn/platform.c +++ b/libdrgn/platform.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include @@ -38,6 +38,11 @@ LIBDRGN_PUBLIC const struct drgn_platform drgn_host_platform = { #else #error "unknown __riscv_xlen" #endif +#elif __s390x__ + .arch = &arch_info_s390x, +// __s390__ is also defined for s390x, so the order is important. +#elif __s390__ + .arch = &arch_info_s390, #else .arch = &arch_info_unknown, #endif @@ -77,6 +82,12 @@ drgn_platform_create(enum drgn_architecture arch, case DRGN_ARCH_RISCV32: arch_info = &arch_info_riscv32; break; + case DRGN_ARCH_S390X: + arch_info = &arch_info_s390x; + break; + case DRGN_ARCH_S390: + arch_info = &arch_info_s390; + break; default: return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, "invalid architecture"); @@ -162,6 +173,12 @@ void drgn_platform_from_elf(GElf_Ehdr *ehdr, struct drgn_platform *ret) else arch = &arch_info_riscv32; break; + case EM_S390: + if (ehdr->e_ident[EI_CLASS] == ELFCLASS64) + arch = &arch_info_s390x; + else + arch = &arch_info_s390; + break; default: arch = &arch_info_unknown; break; diff --git a/libdrgn/platform.h b/libdrgn/platform.h index bb0d784ea..fdeb0cd01 100644 --- a/libdrgn/platform.h +++ b/libdrgn/platform.h @@ -1,5 +1,13 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later + +/** + * @file + * + * Platform internals + * + * See @ref PlatformInternals. + */ #ifndef DRGN_PLATFORM_H #define DRGN_PLATFORM_H @@ -14,12 +22,31 @@ struct drgn_orc_entry; struct drgn_register_state; +/** + * @ingroup Internals + * + * @defgroup PlatformInternals Platforms + * + * Platform internals. + * + * drgn's external representation of a platform is @ref drgn_platform. + * Internally, architecture-specific handling is mainly in @ref + * drgn_architecture_info. See @ref drgn_architecture_info for instructions on + * adding support for a new architecture. + * + * @{ + */ + struct drgn_register { + /** Human-readable names of this register. */ const char * const *names; + /** Number of names in @ref names. */ size_t num_names; + /** Internal register number. */ drgn_register_number regno; }; +/** Offset and size of a register in @ref drgn_register_state::buf. */ struct drgn_register_layout { uint32_t offset; uint32_t size; @@ -29,7 +56,7 @@ struct drgn_register_layout { // We enforce that it stays up to date with a static_assert() in arch_aarch64.c. #define DRGN_AARCH64_RA_SIGN_STATE_REGNO 0 -/* ELF section to apply relocations to. */ +/** ELF section to apply relocations to. */ struct drgn_relocating_section { char *buf; size_t buf_size; @@ -39,7 +66,8 @@ struct drgn_relocating_section { extern struct drgn_error drgn_invalid_relocation_offset; -/* +#ifdef DOXYGEN +/** * Apply an ELF relocation as: * * - `*dst = addend + *r_addend` if `r_addend` is not `NULL` (for `ElfN_Rela`) @@ -49,7 +77,14 @@ extern struct drgn_error drgn_invalid_relocation_offset; * * This checks bounds and handles unaligned destinations and byte swapping. It * does not check for overflow. + * + * This is defined for N of 8, 16, 32, and 64. */ +struct drgn_error * +drgn_reloc_addN(const struct drgn_relocating_section *relocating, + uint64_t r_offset, const int64_t *r_addend, uintN_t addend); +#endif + struct drgn_error * drgn_reloc_add64(const struct drgn_relocating_section *relocating, uint64_t r_offset, const int64_t *r_addend, uint64_t addend); @@ -63,13 +98,14 @@ struct drgn_error * drgn_reloc_add8(const struct drgn_relocating_section *relocating, uint64_t r_offset, const int64_t *r_addend, uint8_t addend); +/** Create an error for an unknown ELF relocation type. */ #define DRGN_UNKNOWN_RELOCATION_TYPE(r_type) \ drgn_error_format(DRGN_ERROR_OTHER, \ "unknown relocation type %" PRIu32 " in %s; " \ "please report this to %s", \ (r_type), __func__, PACKAGE_BUGREPORT) -/* +/** * Apply an ELF relocation. If @p r_addend is `NULL`, then this is an `ElfN_Rel` * relocation. Otherwise, this is an `ElfN_Rela` relocation. */ @@ -78,23 +114,23 @@ apply_elf_reloc_fn(const struct drgn_relocating_section *relocating, uint64_t r_offset, uint32_t r_type, const int64_t *r_addend, uint64_t sym_value); -/* Page table iterator. */ +/** Page table iterator. */ struct pgtable_iterator { - /* Address of the top-level page table to iterate. */ + /** Address of the top-level page table to iterate. */ uint64_t pgtable; - /* Current virtual address to translate. */ + /** Current virtual address to translate. */ uint64_t virt_addr; }; -/* +/** * Translate the current virtual address from a page table iterator. * * Abstractly, a virtual address lies in a range of addresses in the address * space. A range may be a mapped page, a page table gap, or a range of invalid * addresses (e.g., non-canonical addresses on x86-64). This finds the range - * containing the current virtual address, returns the first virtual address of - * that range and the physical address it maps to (if any), and updates the - * current virtual address to the end of the range. + * containing the current virtual address (`it->virt_addr`), returns the first + * virtual address of that range and the physical address it maps to (if any), + * and updates `it->virt_addr` to the end of the range. * * This does not merge contiguous ranges. For example, if two adjacent mapped * pages have adjacent physical addresses, this returns each page separately. @@ -114,66 +150,284 @@ typedef struct drgn_error * struct pgtable_iterator *it, uint64_t *virt_addr_ret, uint64_t *phys_addr_ret); +/** + * Architecture-specific information and callbacks. + * + * To add the bare minimum support for recognizing a new architecture: + * + * - Add a `DRGN_ARCH_FOO` enumerator to @ref drgn_architecture. + * - Add the constant to `class Architecture` in `_drgn.pyi`. + * - Create a new `libdrgn/arch_foo.c` file and add it to `libdrgn/Makefile.am`. + * - Define `struct drgn_architecture_info arch_info_foo` in + * `libdrgn/arch_foo.c` with the following members: + * - @ref name + * - @ref arch + * - @ref default_flags + * - @ref register_by_name + * - Add an `extern` declaration of `arch_info_foo` to `libdrgn/platform.h`. + * - Handle the architecture in @ref drgn_platform_from_kdump(), @ref + * drgn_host_platform, @ref drgn_platform_create(), and @ref + * drgn_platform_from_elf(). + * + * To support Linux kernel loadable modules: + * + * - Define @ref apply_elf_reloc. + * + * To support stack unwinding: + * + * - Create a new `libdrgn/arch_foo_defs.py` file. See + * `libdrgn/build-aux/gen_arch_inc_strswitch.py`. + * - Add `#include "arch_foo_defs.inc"` to `libdrgn/arch_foo.c`. + * - Add `DRGN_ARCHITECTURE_REGISTERS` to `arch_info_foo` (and remove @ref + * register_by_name). + * - Define the following @ref drgn_architecture_info members: + * - @ref default_dwarf_cfi_row (use @ref DRGN_CFI_ROW) + * - @ref fallback_unwind + * - @ref pt_regs_get_initial_registers + * - @ref prstatus_get_initial_registers + * - @ref linux_kernel_get_initial_registers + * - @ref demangle_cfi_registers (only if needed) + * + * To support virtual address translation: + * + * - Define the @ref drgn_architecture_info page table iterator members: + * - @ref linux_kernel_pgtable_iterator_create + * - @ref linux_kernel_pgtable_iterator_destroy + * - @ref linux_kernel_pgtable_iterator_init + * - @ref linux_kernel_pgtable_iterator_next + * + * This is an example of how the page table iterator members may be used + * (ignoring error handling): + * + * ``` + * // Create the iterator. + * struct pgtable_iterator *it; + * arch->linux_kernel_pgtable_iterator_create(prog, &it); + * + * // Initialize the iterator to translate virtual address 0x80000000 using + * // the page table "pgtable". + * it->pgtable = pgtable; + * it->virt_addr = 0x80000000; + * arch->linux_kernel_pgtable_iterator_init(prog, it); + * // Iterate up to virtual address 0x90000000. + * while (it->virt_addr < 0x90000000) { + * uint64_t virt_addr, phys_addr; + * arch->linux_kernel_pgtable_iterator_next(prog, it, &virt_addr, + * &phys_addr); + * if (phys_addr == UINT64_MAX) { + * printf("Virtual address range 0x%" PRIx64 "-0x%" PRIx64 + * " is not mapped\n", + * virt_addr, it->virt_addr); + * } else { + * printf("Virtual address range 0x%" PRIx64 "-0x%" PRIx64 + * " maps to physical address 0x%" PRIx64 "\n", + * virt_addr, phys_addr); + * } + * } + * + * // Reuse the iterator to translate a different address using a different page + * // table. + * it->pgtable = another_pgtable; + * it->virt_addr = 0x11110000; + * uint64_t virt_addr, phys_addr; + * arch->linux_kernel_pgtable_iterator_next(prog, it, &virt_addr, &phys_addr); + * if (phys_addr != UINT64_MAX) { + * printf("Virtual address 0x11110000 maps to physical address 0x%" PRIx64 "\n", + * phys_addr + (0x11110000 - virt_addr)); + * } + * + * // Free the iterator now that we're done with it. + * arch->linux_kernel_pgtable_iterator_destroy(prog, &it); + * ``` + */ struct drgn_architecture_info { + /** Human-readable name of this architecture. */ const char *name; + /** Architecture identifier. */ enum drgn_architecture arch; + /** + * Flags to set for the platform if we're not getting them from + * elsewhere (like from an ELF file). + */ enum drgn_platform_flags default_flags; - /* API-visible registers. */ + /** + * Registers visible to the public API. + * + * This is set by `DRGN_ARCHITECTURE_REGISTERS`. + */ const struct drgn_register *registers; - /* Number of API-visible registers. */ + /** + * Number of registers in @ref registers. + * + * This is set by `DRGN_ARCHITECTURE_REGISTERS`. + */ size_t num_registers; - /* + /** + * Internal register number of stack pointer. + * + * This is set by `DRGN_ARCHITECTURE_REGISTERS`. + */ + drgn_register_number stack_pointer_regno; + /** * Return the API-visible register with the given name, or @c NULL if it * is not recognized. + * + * This is set by `DRGN_ARCHITECTURE_REGISTERS`. It cannot be `NULL`. + * Set it to @ref drgn_register_by_name_unknown if not using + * `DRGN_ARCHITECTURE_REGISTERS`. */ const struct drgn_register *(*register_by_name)(const char *name); - /* Internal register layouts indexed by internal register number. */ + /** + * Internal register layouts indexed by internal register number. + * + * This is set by `DRGN_ARCHITECTURE_REGISTERS`. + */ const struct drgn_register_layout *register_layout; - /* + /** * Return the internal register number for the given DWARF register * number, or @ref DRGN_REGISTER_NUMBER_UNKNOWN if it is not recognized. + * + * This is set by `DRGN_ARCHITECTURE_REGISTERS`. */ drgn_register_number (*dwarf_regno_to_internal)(uint64_t); - /* CFI row containing default rules for DWARF CFI. */ + /** CFI row containing default rules for DWARF CFI. */ const struct drgn_cfi_row *default_dwarf_cfi_row; + /** + * Translate an ORC entry to a @ref drgn_cfi_row. + * + * This should be `NULL` if the architecture doesn't use ORC. + */ struct drgn_error *(*orc_to_cfi)(const struct drgn_orc_entry *, struct drgn_cfi_row **, bool *, drgn_register_number *); - /* - * Try to unwind a stack frame if CFI wasn't found. Returns &drgn_stop - * if we couldn't. + /** + * Replace mangled registers unwound by CFI with their actual values. + * + * This should be `NULL` if not needed. + */ + void (*demangle_cfi_registers)(struct drgn_program *, + struct drgn_register_state *); + /** + * Try to unwind a stack frame if CFI wasn't found. Returns &@ref + * drgn_stop if we couldn't. + * + * This typically uses something like frame pointers. If this has to + * read memory, translate @ref DRGN_ERROR_FAULT errors to &@ref + * drgn_stop. */ struct drgn_error *(*fallback_unwind)(struct drgn_program *, struct drgn_register_state *, struct drgn_register_state **); - void (*demangle_return_address)(struct drgn_program *, - struct drgn_register_state *, - drgn_register_number); - /* Given pt_regs as a value buffer object. */ - struct drgn_error *(*pt_regs_get_initial_registers)(const struct drgn_object *, - struct drgn_register_state **); - struct drgn_error *(*prstatus_get_initial_registers)(struct drgn_program *, - const void *, - size_t, - struct drgn_register_state **); - struct drgn_error *(*linux_kernel_get_initial_registers)(const struct drgn_object *, - struct drgn_register_state **); + /** + * Create a @ref drgn_register_state from a Linux `struct pt_regs`. + * + * This should check that the object is sufficiently large with @ref + * drgn_object_size(), call @ref drgn_register_state_create() with + * `interrupted = true`, and initialize it from the contents of @ref + * drgn_object_buffer(). + * + * @param[in] obj `struct pt_regs` as a value buffer object. + * @param[out] ret Returned registers. + */ + struct drgn_error *(*pt_regs_get_initial_registers)(const struct drgn_object *obj, + struct drgn_register_state **ret); + /** + * Create a @ref drgn_register_state from the contents of an ELF + * `NT_PRSTATUS` note. + * + * This should check that @p size is sufficiently large, call @ref + * drgn_register_state_create() with `interrupted = true`, and + * initialize it from @p prstatus. + * + * Refer to `struct elf_prstatus_common` in the Linux kernel source for + * the format, and in particular, the `elf_gregset_t pr_reg` member. + * `elf_gregset_t` has an architecture-specific layout; on many + * architectures, it is identical to or a prefix of `struct pt_regs`. + * `pr_reg` is typically at offset 112 on 64-bit platforms and 72 on + * 32-bit platforms. + * + * @param[in] prstatus Buffer of `NT_PRSTATUS` contents. + * @param[in] size Size of @p prstatus in bytes. + * @param[out] ret Returned registers. + */ + struct drgn_error *(*prstatus_get_initial_registers)(struct drgn_program *prog, + const void *prstatus, + size_t size, + struct drgn_register_state **ret); + /** + * Create a @ref drgn_register_state from the `struct task_struct` of a + * scheduled-out Linux kernel thread. + * + * This should should call @ref drgn_register_state_create() with + * `interrupted = false` and initialize it from the saved thread + * context. + * + * The context can usually be found in `struct task_struct::thread` + * and/or the thread stack. Refer to this architecture's implementation + * of `switch_to()` in the Linux kernel. + * + * @param[in] task_obj `struct task_struct` object. + * @param[out] ret Returned registers. + */ + struct drgn_error *(*linux_kernel_get_initial_registers)(const struct drgn_object *task_obj, + struct drgn_register_state **ret); + /** + * Apply an ELF relocation. + * + * This should use the pre-defined @ref drgn_reloc_addN() functions + * whenever possible. Note that this is only used to relocate debugging + * information sections, so typically only simple absolute and + * PC-relative relocations need to be implemented. + */ apply_elf_reloc_fn *apply_elf_reloc; - struct drgn_error *(*linux_kernel_live_direct_mapping_fallback)(struct drgn_program *, - uint64_t *, - uint64_t *); - /* Allocate a Linux kernel page table iterator. */ + /** + * Return the address and size of the direct mapping virtual address + * range. + * + * This is a hack which is only called when debugging a live Linux + * kernel older than v4.11. + */ + struct drgn_error *(*linux_kernel_live_direct_mapping_fallback)(struct drgn_program *prog, + uint64_t *address_ret, + uint64_t *size_ret); + /** Allocate a Linux kernel page table iterator. */ struct drgn_error *(*linux_kernel_pgtable_iterator_create)(struct drgn_program *, struct pgtable_iterator **); - /* Destroy a Linux kernel page table iterator. */ + /** Free a Linux kernel page table iterator. */ void (*linux_kernel_pgtable_iterator_destroy)(struct pgtable_iterator *); - /* (Re)initialize a Linux kernel page table iterator. */ + /** + * (Re)initialize a Linux kernel page table iterator. + * + * This is called each time that the iterator will be used to translate + * a contiguous range of virtual addresses from a single page table. It + * is called with @ref pgtable_iterator::pgtable set to the address of + * the page table to use and @ref pgtable_iterator::virt_addr set to the + * starting virtual address to translate. + */ void (*linux_kernel_pgtable_iterator_init)(struct drgn_program *, struct pgtable_iterator *); - /* Iterate a (user or kernel) page table in the Linux kernel. */ + /** + * Iterate a (user or kernel) page table in the Linux kernel. + * + * This is called after @ref linux_kernel_pgtable_iterator_init() to + * translate the starting address and may be called again without + * reinitializing the iterator to translate subsequent adjacent + * addresses in the same page table. + * + * If the caller needs to translate from a different page table or + * virtual address, it will call @ref + * linux_kernel_pgtable_iterator_init() before calling this function + * again. + * + * @see pgtable_iterator_next_fn + */ pgtable_iterator_next_fn *linux_kernel_pgtable_iterator_next; }; +/** + * Implementation of @ref drgn_architecture_info::register_by_name that always + * returns `NULL`. + */ const struct drgn_register *drgn_register_by_name_unknown(const char *name); extern const struct drgn_architecture_info arch_info_unknown; @@ -184,12 +438,20 @@ extern const struct drgn_architecture_info arch_info_arm; extern const struct drgn_architecture_info arch_info_ppc64; extern const struct drgn_architecture_info arch_info_riscv64; extern const struct drgn_architecture_info arch_info_riscv32; +extern const struct drgn_architecture_info arch_info_s390x; +extern const struct drgn_architecture_info arch_info_s390; struct drgn_platform { const struct drgn_architecture_info *arch; enum drgn_platform_flags flags; }; +static inline bool drgn_platforms_equal(const struct drgn_platform *a, + const struct drgn_platform *b) +{ + return a->arch == b->arch && a->flags == b->flags; +} + static inline bool drgn_platform_is_little_endian(const struct drgn_platform *platform) { @@ -232,4 +494,6 @@ void drgn_platform_from_arch(const struct drgn_architecture_info *arch, /** Initialize a @ref drgn_platform from an ELF header. */ void drgn_platform_from_elf(GElf_Ehdr *ehdr, struct drgn_platform *ret); +/** @} */ + #endif /* DRGN_PLATFORM_H */ diff --git a/libdrgn/pp.h b/libdrgn/pp.h index 67805269a..cc93db6bb 100644 --- a/libdrgn/pp.h +++ b/libdrgn/pp.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file diff --git a/libdrgn/program.c b/libdrgn/program.c index c79e43bc9..52430348b 100644 --- a/libdrgn/program.c +++ b/libdrgn/program.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include @@ -18,6 +18,7 @@ #include #include +#include "cleanup.h" #include "debug_info.h" #include "error.h" #include "helpers.h" @@ -37,9 +38,9 @@ static inline uint32_t drgn_thread_to_key(const struct drgn_thread *entry) return entry->tid; } -DEFINE_VECTOR_FUNCTIONS(drgn_prstatus_vector) +DEFINE_VECTOR_FUNCTIONS(drgn_prstatus_vector); DEFINE_HASH_TABLE_FUNCTIONS(drgn_thread_set, drgn_thread_to_key, - int_key_hash_pair, scalar_key_eq) + int_key_hash_pair, scalar_key_eq); struct drgn_thread_iterator { struct drgn_program *prog; @@ -104,6 +105,8 @@ void drgn_program_init(struct drgn_program *prog, drgn_program_set_platform(prog, platform); char *env = getenv("DRGN_PREFER_ORC_UNWINDER"); prog->prefer_orc_unwinder = env && atoi(env); + drgn_program_set_log_level(prog, DRGN_LOG_NONE); + drgn_program_set_log_file(prog, stderr); drgn_object_init(&prog->vmemmap, prog); } @@ -134,6 +137,7 @@ void drgn_program_deinit(struct drgn_program *prog) drgn_memory_reader_deinit(&prog->reader); free(prog->file_segments); + free(prog->vmcoreinfo.raw); #ifdef WITH_LIBKDUMPFILE if (prog->kdump_ctx) @@ -205,12 +209,20 @@ drgn_program_check_initialized(struct drgn_program *prog) static struct drgn_error *has_kdump_signature(const char *path, int fd, bool *ret) { - char signature[KDUMP_SIG_LEN]; + char signature[max_iconst(KDUMP_SIG_LEN, FLATTENED_SIG_LEN)]; ssize_t r = pread_all(fd, signature, sizeof(signature), 0); if (r < 0) return drgn_error_create_os("pread", errno, path); - *ret = (r == sizeof(signature) - && memcmp(signature, KDUMP_SIGNATURE, sizeof(signature)) == 0); + if (r >= FLATTENED_SIG_LEN + && memcmp(signature, FLATTENED_SIGNATURE, FLATTENED_SIG_LEN) == 0) { + return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, + "the given file is in the makedumpfile flattened " + "format, which drgn does not support; use " + "'makedumpfile -R newfile = KDUMP_SIG_LEN + && memcmp(signature, KDUMP_SIGNATURE, KDUMP_SIG_LEN) == 0); return NULL; } @@ -570,6 +582,7 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path) out_notes: // Reset anything we parsed from ELF notes. prog->aarch64_insn_pac_mask = 0; + free(prog->vmcoreinfo.raw); memset(&prog->vmcoreinfo, 0, sizeof(prog->vmcoreinfo)); out_platform: prog->has_platform = had_platform; @@ -649,8 +662,10 @@ static void drgn_program_set_language_from_main(struct drgn_program *prog) return; const struct drgn_language *lang; err = drgn_debug_info_main_language(prog->dbinfo, &lang); - if (err) + if (err) { drgn_error_destroy(err); + return; + } if (lang) prog->lang = lang; } @@ -684,6 +699,7 @@ drgn_program_load_debug_info(struct drgn_program *prog, const char **paths, if (!n && !load_default && !load_main) return NULL; + drgn_blocking_guard(prog); struct drgn_debug_info *dbinfo = prog->dbinfo; if (!dbinfo) { err = drgn_debug_info_create(prog, &dbinfo); @@ -1232,6 +1248,101 @@ drgn_program_find_thread(struct drgn_program *prog, uint32_t tid, return drgn_program_find_thread_userspace_core(prog, tid, ret); } +// Get the CPU that crashed in a Linux kernel core dump. +static struct drgn_error * +drgn_program_kernel_get_crashed_cpu(struct drgn_program *prog, uint64_t *ret) +{ + struct drgn_error *err; + struct drgn_object cpu; + drgn_object_init(&cpu, prog); + union drgn_value cpu_value; + + // Since Linux kernel commit 1717f2096b54 ("panic, x86: Fix re-entrance + // problem due to panic on NMI") (in v4.5), the crashed CPU is stored in + // an atomic_t panic_cpu on all architectures. + err = drgn_program_find_object(prog, "panic_cpu", NULL, + DRGN_FIND_OBJECT_VARIABLE, &cpu); + if (!err) { + err = drgn_object_member(&cpu, &cpu, "counter"); + if (err) + goto out; + err = drgn_object_read_integer(&cpu, &cpu_value); + if (!err) + *ret = cpu_value.uvalue; + } else if (err->code == DRGN_ERROR_LOOKUP) { + // On x86 and x86-64 only, the crashed CPU is also in an int + // crashing_cpu. Use this as a fallback for kernels before + // commit 1717f2096b54 ("panic, x86: Fix re-entrance problem due + // to panic on NMI") (in v4.5). + drgn_error_destroy(err); + err = drgn_program_find_object(prog, "crashing_cpu", NULL, + DRGN_FIND_OBJECT_VARIABLE, &cpu); + if (!err) { + err = drgn_object_read_integer(&cpu, &cpu_value); + if (err) + goto out; + // Since Linux kernel commit 5bc329503e81 ("x86/mce: + // Handle broadcasted MCE gracefully with kexec") (in + // v4.12), crashing_cpu is defined in !SMP kernels, but + // it's always -1. + if (cpu_value.svalue == -1) + *ret = 0; + else + *ret = cpu_value.uvalue; + } else if (err->code == DRGN_ERROR_LOOKUP) { + // Before Linux kernel commit 5bc329503e81 ("x86/mce: + // Handle broadcasted MCE gracefully with kexec") (in + // v4.12), crashing_cpu is only defined in SMP kernels. + drgn_error_destroy(err); + err = NULL; + *ret = 0; + } + } + +out: + drgn_object_deinit(&cpu); + return err; +} + +static struct drgn_error * +drgn_program_find_thread_kernel_cpu_curr(struct drgn_program *prog, + uint64_t cpu, + struct drgn_thread **ret) +{ + struct drgn_error *err; + struct drgn_thread *thread = malloc(sizeof(*thread)); + if (!thread) + return &drgn_enomem; + thread->prog = prog; + + struct drgn_object tmp; + drgn_object_init(&tmp, prog); + drgn_object_init(&thread->object, prog); + + err = linux_helper_cpu_curr(&thread->object, cpu); + if (err) + goto out; + + err = drgn_object_member_dereference(&tmp, &thread->object, "pid"); + if (err) + goto out; + union drgn_value tid; + err = drgn_object_read_integer(&tmp, &tid); + if (err) + goto out; + thread->tid = tid.uvalue; + + *ret = thread; + +out: + if (err) { + drgn_object_deinit(&thread->object); + free(thread); + } + drgn_object_deinit(&tmp); + return err; +} + static struct drgn_error * drgn_program_kernel_core_dump_cache_crashed_thread(struct drgn_program *prog) { @@ -1243,68 +1354,22 @@ drgn_program_kernel_core_dump_cache_crashed_thread(struct drgn_program *prog) if (prog->crashed_thread) return NULL; - struct drgn_object crashing_cpu; - union drgn_value crashing_cpu_value; - drgn_object_init(&crashing_cpu, prog); - err = drgn_program_find_object(prog, "crashing_cpu", NULL, - DRGN_FIND_OBJECT_VARIABLE, - &crashing_cpu); - if (!err) { - err = drgn_object_read_integer(&crashing_cpu, - &crashing_cpu_value); - } else if (err->code == DRGN_ERROR_LOOKUP) { - /* - * Before Linux kernel commit 5bc329503e81 ("x86/mce: Handle - * broadcasted MCE gracefully with kexec") (in v4.12), - * crashing_cpu is only defined in SMP kernels. - */ - drgn_error_destroy(err); - err = NULL; - crashing_cpu_value.uvalue = 0; - } - drgn_object_deinit(&crashing_cpu); + uint64_t crashed_cpu; + err = drgn_program_kernel_get_crashed_cpu(prog, &crashed_cpu); if (err) return err; - /* - * Since Linux kernel commit 5bc329503e81 ("x86/mce: Handle broadcasted - * MCE gracefully with kexec") (in v4.12), crashing_cpu is defined in - * !SMP kernels, but it's always -1. - */ - if (crashing_cpu_value.svalue == -1) - crashing_cpu_value.uvalue = 0; - if (crashing_cpu_value.uvalue >= prog->prstatus_vector.size) - return NULL; - struct nstring *prstatus = - &prog->prstatus_vector.data[crashing_cpu_value.uvalue]; - uint32_t crashed_thread_tid; - err = get_prstatus_pid(prog, prstatus->str, prstatus->len, - &crashed_thread_tid); - if (err) - return err; + if (crashed_cpu >= drgn_prstatus_vector_size(&prog->prstatus_vector)) + return NULL; - if (crashed_thread_tid == 0) { - prog->crashed_thread = malloc(sizeof(*prog->crashed_thread)); - if (!prog->crashed_thread) - return &drgn_enomem; - prog->crashed_thread->prog = prog; - prog->crashed_thread->tid = crashed_thread_tid; - drgn_object_init(&prog->crashed_thread->object, prog); - err = linux_helper_idle_task(&prog->crashed_thread->object, - crashing_cpu_value.uvalue); - if (err) { - drgn_object_deinit(&prog->crashed_thread->object); - free(prog->crashed_thread); - } - } else { - err = drgn_program_find_thread(prog, crashed_thread_tid, - &prog->crashed_thread); - } + err = drgn_program_find_thread_kernel_cpu_curr(prog, crashed_cpu, + &prog->crashed_thread); if (err) { prog->crashed_thread = NULL; return err; } - prog->crashed_thread->prstatus = *prstatus; + prog->crashed_thread->prstatus = + *drgn_prstatus_vector_at(&prog->prstatus_vector, crashed_cpu); return NULL; } @@ -1385,8 +1450,8 @@ struct drgn_error *drgn_program_find_prstatus_by_cpu(struct drgn_program *prog, if (err) return err; - if (cpu < prog->prstatus_vector.size) { - *ret = prog->prstatus_vector.data[cpu]; + if (cpu < drgn_prstatus_vector_size(&prog->prstatus_vector)) { + *ret = *drgn_prstatus_vector_at(&prog->prstatus_vector, cpu); return get_prstatus_pid(prog, ret->str, ret->len, tid_ret); } else { ret->str = NULL; @@ -1551,7 +1616,7 @@ drgn_program_read_memory(struct drgn_program *prog, void *buf, uint64_t address, return NULL; } -DEFINE_VECTOR(char_vector, char) +DEFINE_VECTOR(char_vector, char); LIBDRGN_PUBLIC struct drgn_error * drgn_program_read_c_string(struct drgn_program *prog, uint64_t address, @@ -1561,21 +1626,17 @@ drgn_program_read_c_string(struct drgn_program *prog, uint64_t address, struct drgn_error *err = drgn_program_address_mask(prog, &address_mask); if (err) return err; - struct char_vector str = VECTOR_INIT; + _cleanup_(char_vector_deinit) struct char_vector str = VECTOR_INIT; for (;;) { address &= address_mask; char *c = char_vector_append_entry(&str); - if (!c) { - char_vector_deinit(&str); + if (!c) return &drgn_enomem; - } - if (str.size <= max_size) { + if (char_vector_size(&str) <= max_size) { err = drgn_memory_reader_read(&prog->reader, c, address, 1, physical); - if (err) { - char_vector_deinit(&str); + if (err) return err; - } if (!*c) break; } else { @@ -1585,7 +1646,7 @@ drgn_program_read_c_string(struct drgn_program *prog, uint64_t address, address++; } char_vector_shrink_to_fit(&str); - *ret = str.data; + char_vector_steal(&str, ret, NULL); return NULL; } @@ -1719,7 +1780,7 @@ drgn_program_find_symbol_by_address(struct drgn_program *prog, uint64_t address, return NULL; } -DEFINE_VECTOR(symbolp_vector, struct drgn_symbol *) +DEFINE_VECTOR(symbolp_vector, struct drgn_symbol *); enum { SYMBOLS_SEARCH_NAME = (1 << 0), @@ -1809,13 +1870,12 @@ symbols_search(struct drgn_program *prog, struct symbols_search_arg *arg, } if (err) { - for (size_t i = 0; i < arg->results.size; i++) - drgn_symbol_destroy(arg->results.data[i]); + vector_for_each(symbolp_vector, symbolp, &arg->results) + drgn_symbol_destroy(*symbolp); symbolp_vector_deinit(&arg->results); } else { symbolp_vector_shrink_to_fit(&arg->results); - *count_ret = arg->results.size; - *syms_ret = arg->results.data; + symbolp_vector_steal(&arg->results, syms_ret, count_ret); } return err; } @@ -1941,3 +2001,38 @@ drgn_program_element_info(struct drgn_program *prog, struct drgn_type *type, ret->qualified_type = drgn_type_type(underlying_type); return drgn_type_bit_size(ret->qualified_type.type, &ret->bit_size); } + +LIBDRGN_PUBLIC void +drgn_program_set_blocking_callback(struct drgn_program *prog, + drgn_program_begin_blocking_fn *begin_callback, + drgn_program_end_blocking_fn *end_callback, + void *callback_arg) +{ + prog->begin_blocking_fn = begin_callback; + prog->end_blocking_fn = end_callback; + prog->blocking_arg = callback_arg; +} + +LIBDRGN_PUBLIC void +drgn_program_get_blocking_callback(struct drgn_program *prog, + drgn_program_begin_blocking_fn **begin_callback_ret, + drgn_program_end_blocking_fn **end_callback_ret, + void **callback_arg_ret) +{ + *begin_callback_ret = prog->begin_blocking_fn; + *end_callback_ret = prog->end_blocking_fn; + *callback_arg_ret = prog->blocking_arg; +} + +void *drgn_program_begin_blocking(struct drgn_program *prog) +{ + if (!prog->begin_blocking_fn) + return NULL; + return prog->begin_blocking_fn(prog, prog->blocking_arg); +} + +void drgn_program_end_blocking(struct drgn_program *prog, void *state) +{ + if (prog->end_blocking_fn) + prog->end_blocking_fn(prog, prog->blocking_arg, state); +} diff --git a/libdrgn/program.h b/libdrgn/program.h index 1a4999f82..7b870dc5b 100644 --- a/libdrgn/program.h +++ b/libdrgn/program.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -25,11 +25,10 @@ #include "memory_reader.h" #include "object_index.h" #include "platform.h" +#include "pp.h" #include "type.h" #include "vector.h" -struct drgn_symbol; - /** * @defgroup Internals Internals * @@ -51,9 +50,9 @@ struct drgn_thread { struct drgn_object object; }; -DEFINE_VECTOR_TYPE(drgn_typep_vector, struct drgn_type *) -DEFINE_VECTOR_TYPE(drgn_prstatus_vector, struct nstring) -DEFINE_HASH_TABLE_TYPE(drgn_thread_set, struct drgn_thread) +DEFINE_VECTOR_TYPE(drgn_typep_vector, struct drgn_type *); +DEFINE_VECTOR_TYPE(drgn_prstatus_vector, struct nstring); +DEFINE_HASH_TABLE_TYPE(drgn_thread_set, struct drgn_thread); struct drgn_program { /** @privatesection */ @@ -169,6 +168,10 @@ struct drgn_program { bool pgtable_l5_enabled; /** PAGE_SHIFT of the kernel (derived from PAGE_SIZE). */ int page_shift; + + /** The original vmcoreinfo data, to expose as an object */ + char *raw; + size_t raw_size; } vmcoreinfo; /* * Difference between a virtual address in the direct mapping and the @@ -186,6 +189,20 @@ struct drgn_program { bool in_address_translation; /* Whether @ref drgn_program::direct_mapping_offset has been cached. */ bool direct_mapping_offset_cached; + + /* + * Logging. + */ + drgn_log_fn *log_fn; + void *log_arg; + enum drgn_log_level log_level; + + /* + * Blocking callbacks. + */ + drgn_program_begin_blocking_fn *begin_blocking_fn; + drgn_program_end_blocking_fn *end_blocking_fn; + void *blocking_arg; }; /** Initialize a @ref drgn_program. */ @@ -336,6 +353,50 @@ bool drgn_program_find_symbol_by_address_internal(struct drgn_program *prog, Dwfl_Module *module, struct drgn_symbol *ret); +/** + * Call before a blocking (I/O or long-running) operation. + * + * Must be paired with @ref drgn_program_end_blocking(). + * + * @return Opaque pointer to pass to @ref drgn_program_end_blocking(). + */ +void *drgn_program_begin_blocking(struct drgn_program *prog); + +/** + * Call after a blocking (I/O or long-running) operation. + * + * @param[in] state Return value of @ref drgn_program_begin_blocking(). + */ +void drgn_program_end_blocking(struct drgn_program *prog, void *state); + +struct drgn_blocking_guard_struct { + struct drgn_program *prog; + void *state; +}; + +static inline struct drgn_blocking_guard_struct +drgn_blocking_guard_init(struct drgn_program *prog) +{ + return (struct drgn_blocking_guard_struct){ + prog, drgn_program_begin_blocking(prog), + }; +} + +static inline void +drgn_blocking_guard_cleanup(struct drgn_blocking_guard_struct *guard) +{ + drgn_program_end_blocking(guard->prog, guard->state); +} + +/** + * Scope guard that wraps @ref drgn_program_begin_blocking() and @ref + * drgn_program_end_blocking(). + */ +#define drgn_blocking_guard(prog) \ + struct drgn_blocking_guard_struct PP_UNIQUE(guard) \ + __attribute__((__cleanup__(drgn_blocking_guard_cleanup), __unused__)) = \ + drgn_blocking_guard_init(prog) + /** * @} * @} diff --git a/libdrgn/python/drgnpy.h b/libdrgn/python/drgnpy.h index da96c1d6a..6f5fc6d57 100644 --- a/libdrgn/python/drgnpy.h +++ b/libdrgn/python/drgnpy.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #ifndef DRGNPY_H #define DRGNPY_H @@ -11,10 +11,13 @@ #include "structmember.h" #include "docstrings.h" +#include "../cleanup.h" #include "../drgn.h" // IWYU pragma: end_exports #include "../hash_table.h" +#include "../hash_table.h" +#include "../pp.h" #include "../program.h" /* These were added in Python 3.7. */ @@ -39,6 +42,33 @@ #define DRGNPY_PUBLIC __attribute__((__visibility__("default"))) +// PyLong_From* and PyLong_As* for stdint.h types. These use _Generic for +// slightly more type safety (e.g., so you can't pass an int64_t to +// PyLong_FromUint64()). +#if ULONG_MAX == UINT64_MAX +#define PyLong_FromUint64(v) _Generic((v), uint64_t: PyLong_FromUnsignedLong)(v) +#define PyLong_AsUint64(obj) ((uint64_t)PyLong_AsUnsignedLong(obj)) +#define PyLong_AsUint64Mask(obj) ((uint64_t)PyLong_AsUnsignedLongMask(obj)) +#elif ULLONG_MAX == UINT64_MAX +#define PyLong_FromUint64(v) _Generic((v), uint64_t: PyLong_FromUnsignedLongLong)(v) +#define PyLong_AsUint64(obj) ((uint64_t)PyLong_AsUnsignedLongLong(obj)) +#define PyLong_AsUint64Mask(obj) ((uint64_t)PyLong_AsUnsignedLongLongMask(obj)) +#endif + +#if LONG_MIN == INT64_MIN && LONG_MAX == INT64_MAX +#define PyLong_FromInt64(v) _Generic((v), int64_t: PyLong_FromLong)(v) +#define PyLong_AsInt64(obj) ((int64_t)PyLong_AsLong(obj)) +#elif LLONG_MIN == INT64_MIN && LLONG_MAX == INT64_MAX +#define PyLong_FromInt64(v) _Generic((v), int64_t: PyLong_FromLongLong)(v) +#define PyLong_AsInt64(obj) ((int64_t)PyLong_AsLongLong(obj)) +#endif + +#if ULONG_MAX >= UINT32_MAX +#define PyLong_FromUint32(v) _Generic((v), uint32_t: PyLong_FromUnsignedLong)(v) +#define PyLong_FromUint16(v) _Generic((v), uint16_t: PyLong_FromUnsignedLong)(v) +#define PyLong_FromUint8(v) _Generic((v), uint8_t: PyLong_FromUnsignedLong)(v) +#endif + #define Py_RETURN_BOOL(cond) do { \ if (cond) \ Py_RETURN_TRUE; \ @@ -46,6 +76,23 @@ Py_RETURN_FALSE; \ } while (0) +static inline void pydecrefp(void *p) +{ + Py_XDECREF(*(PyObject **)p); +} + +/** Scope guard that wraps PyGILState_Ensure() and PyGILState_Release(). */ +#define PyGILState_guard() \ + __attribute__((__cleanup__(PyGILState_Releasep), __unused__)) \ + PyGILState_STATE PP_UNIQUE(gstate) = PyGILState_Ensure() +static inline void PyGILState_Releasep(PyGILState_STATE *gstatep) +{ + PyGILState_Release(*gstatep); +} + +/** Call @c Py_XDECREF() when the variable goes out of scope. */ +#define _cleanup_pydecref_ _cleanup_(pydecrefp) + typedef struct { PyObject_HEAD struct drgn_object obj; @@ -84,7 +131,7 @@ typedef struct { struct drgn_platform *platform; } Platform; -DEFINE_HASH_SET_TYPE(pyobjectp_set, PyObject *) +DEFINE_HASH_SET_TYPE(pyobjectp_set, PyObject *); typedef struct { PyObject_HEAD @@ -197,6 +244,7 @@ extern PyObject *ObjectAbsentError; extern PyObject *OutOfBoundsError; int add_module_constants(PyObject *m); +int init_logging(void); bool set_drgn_in_python(void); void clear_drgn_in_python(void); @@ -205,15 +253,15 @@ void *set_drgn_error(struct drgn_error *err); void *set_error_type_name(const char *format, struct drgn_qualified_type qualified_type); +#define call_tp_alloc(type) ((type *)type##_type.tp_alloc(&type##_type, 0)) + PyObject *Language_wrap(const struct drgn_language *language); int language_converter(PyObject *o, void *p); int add_languages(void); static inline DrgnObject *DrgnObject_alloc(Program *prog) { - DrgnObject *ret; - - ret = (DrgnObject *)DrgnObject_type.tp_alloc(&DrgnObject_type, 0); + DrgnObject *ret = call_tp_alloc(DrgnObject); if (ret) { drgn_object_init(&ret->obj, &prog->prog); Py_INCREF(prog); @@ -281,13 +329,12 @@ struct index_arg { }; int index_converter(PyObject *o, void *p); -/* Helpers for path arguments based on posixmodule.c in CPython. */ struct path_arg { bool allow_none; char *path; Py_ssize_t length; PyObject *object; - PyObject *cleanup; + PyObject *bytes; }; int path_converter(PyObject *o, void *p); void path_cleanup(struct path_arg *path); @@ -303,13 +350,18 @@ PyObject *drgnpy_linux_helper_direct_mapping_offset(PyObject *self, PyObject *arg); PyObject *drgnpy_linux_helper_read_vm(PyObject *self, PyObject *args, PyObject *kwds); +PyObject *drgnpy_linux_helper_follow_phys(PyObject *self, PyObject *args, + PyObject *kwds); DrgnObject *drgnpy_linux_helper_per_cpu_ptr(PyObject *self, PyObject *args, PyObject *kwds); +DrgnObject *drgnpy_linux_helper_cpu_curr(PyObject *self, PyObject *args, + PyObject *kwds); DrgnObject *drgnpy_linux_helper_idle_task(PyObject *self, PyObject *args, PyObject *kwds); -DrgnObject *drgnpy_linux_helper_radix_tree_lookup(PyObject *self, - PyObject *args, - PyObject *kwds); +PyObject *drgnpy_linux_helper_task_cpu(PyObject *self, PyObject *args, + PyObject *kwds); +DrgnObject *drgnpy_linux_helper_xa_load(PyObject *self, PyObject *args, + PyObject *kwds); DrgnObject *drgnpy_linux_helper_idr_find(PyObject *self, PyObject *args, PyObject *kwds); DrgnObject *drgnpy_linux_helper_find_pid(PyObject *self, PyObject *args, diff --git a/libdrgn/python/error.c b/libdrgn/python/error.c index d04e8d2d3..8f23a9e1f 100644 --- a/libdrgn/python/error.c +++ b/libdrgn/python/error.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include "drgnpy.h" #include "../error.h" @@ -21,34 +21,26 @@ static int FaultError_init(PyObject *self, PyObject *args, PyObject *kwds) static PyObject *FaultError_str(PyObject *self) { - PyObject *message, *address, *args, *fmt, *ret = NULL; - - message = PyObject_GetAttrString(self, "message"); + _cleanup_pydecref_ PyObject *message = + PyObject_GetAttrString(self, "message"); if (!message) return NULL; - address = PyObject_GetAttrString(self, "address"); + _cleanup_pydecref_ PyObject *address = + PyObject_GetAttrString(self, "address"); if (!address) - goto out_message; + return NULL; - args = Py_BuildValue("OO", message, address); + _cleanup_pydecref_ PyObject *args = + Py_BuildValue("OO", message, address); if (!args) - goto out_address; + return NULL; - fmt = PyUnicode_FromString("%s: %#x"); + _cleanup_pydecref_ PyObject *fmt = PyUnicode_FromString("%s: %#x"); if (!fmt) - goto out_args; - - ret = PyUnicode_Format(fmt, args); - - Py_DECREF(fmt); -out_args: - Py_DECREF(args); -out_address: - Py_DECREF(address); -out_message: - Py_DECREF(message); - return ret; + return NULL; + + return PyUnicode_Format(fmt, args); } PyTypeObject FaultError_type = { @@ -157,14 +149,11 @@ DRGNPY_PUBLIC void *set_drgn_error(struct drgn_error *err) PyErr_SetString(PyExc_LookupError, err->message); break; case DRGN_ERROR_FAULT: { - PyObject *exc; - - exc = PyObject_CallFunction((PyObject *)&FaultError_type, "sK", - err->message, err->address); - if (exc) { + _cleanup_pydecref_ PyObject *exc = + PyObject_CallFunction((PyObject *)&FaultError_type, + "sK", err->message, err->address); + if (exc) PyErr_SetObject((PyObject *)&FaultError_type, exc); - Py_DECREF(exc); - } break; } case DRGN_ERROR_TYPE: diff --git a/libdrgn/python/helpers.c b/libdrgn/python/helpers.c index 73a97d59b..779349213 100644 --- a/libdrgn/python/helpers.c +++ b/libdrgn/python/helpers.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include "drgnpy.h" #include "../helpers.h" @@ -16,7 +16,7 @@ PyObject *drgnpy_linux_helper_direct_mapping_offset(PyObject *self, PyObject *ar err = linux_helper_direct_mapping_offset(&((Program *)arg)->prog, &ret); if (err) return set_drgn_error(err); - return PyLong_FromUnsignedLongLong(ret); + return PyLong_FromUint64(ret); } PyObject *drgnpy_linux_helper_read_vm(PyObject *self, PyObject *args, @@ -28,8 +28,6 @@ PyObject *drgnpy_linux_helper_read_vm(PyObject *self, PyObject *args, struct index_arg pgtable = {}; struct index_arg address = {}; Py_ssize_t size; - PyObject *buf; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O&O&n:read_vm", keywords, &Program_type, &prog, index_converter, &pgtable, @@ -40,16 +38,36 @@ PyObject *drgnpy_linux_helper_read_vm(PyObject *self, PyObject *args, PyErr_SetString(PyExc_ValueError, "negative size"); return NULL; } - buf = PyBytes_FromStringAndSize(NULL, size); + _cleanup_pydecref_ PyObject *buf = PyBytes_FromStringAndSize(NULL, size); if (!buf) return NULL; err = linux_helper_read_vm(&prog->prog, pgtable.uvalue, address.uvalue, PyBytes_AS_STRING(buf), size); - if (err) { - Py_DECREF(buf); + if (err) return set_drgn_error(err); - } - return buf; + return_ptr(buf); +} + +PyObject *drgnpy_linux_helper_follow_phys(PyObject *self, PyObject *args, + PyObject *kwds) +{ + static char *keywords[] = {"prog", "pgtable", "address", NULL}; + struct drgn_error *err; + Program *prog; + struct index_arg pgtable = {}; + struct index_arg address = {}; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O&O&:follow_phys", + keywords, &Program_type, &prog, + index_converter, &pgtable, + index_converter, &address)) + return NULL; + + uint64_t phys; + err = linux_helper_follow_phys(&prog->prog, pgtable.uvalue, + address.uvalue, &phys); + if (err) + return set_drgn_error(err); + return PyLong_FromUint64(phys); } DrgnObject *drgnpy_linux_helper_per_cpu_ptr(PyObject *self, PyObject *args, @@ -59,21 +77,39 @@ DrgnObject *drgnpy_linux_helper_per_cpu_ptr(PyObject *self, PyObject *args, struct drgn_error *err; DrgnObject *ptr; struct index_arg cpu = {}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O&:per_cpu_ptr", keywords, &DrgnObject_type, &ptr, index_converter, &cpu)) return NULL; - DrgnObject *res = DrgnObject_alloc(DrgnObject_prog(ptr)); + _cleanup_pydecref_ DrgnObject *res = DrgnObject_alloc(DrgnObject_prog(ptr)); if (!res) return NULL; err = linux_helper_per_cpu_ptr(&res->obj, &ptr->obj, cpu.uvalue); - if (err) { - Py_DECREF(res); + if (err) return set_drgn_error(err); - } - return res; + return_ptr(res); +} + +DrgnObject *drgnpy_linux_helper_cpu_curr(PyObject *self, PyObject *args, + PyObject *kwds) +{ + static char *keywords[] = {"prog", "cpu", NULL}; + struct drgn_error *err; + Program *prog; + struct index_arg cpu = {}; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O&:cpu_curr", keywords, + &Program_type, &prog, index_converter, + &cpu)) + return NULL; + + _cleanup_pydecref_ DrgnObject *res = DrgnObject_alloc(prog); + if (!res) + return NULL; + err = linux_helper_cpu_curr(&res->obj, cpu.uvalue); + if (err) + return set_drgn_error(err); + return_ptr(res); } DrgnObject *drgnpy_linux_helper_idle_task(PyObject *self, PyObject *args, @@ -83,48 +119,56 @@ DrgnObject *drgnpy_linux_helper_idle_task(PyObject *self, PyObject *args, struct drgn_error *err; Program *prog; struct index_arg cpu = {}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O&:idle_task", keywords, &Program_type, &prog, index_converter, &cpu)) return NULL; - DrgnObject *res = DrgnObject_alloc(prog); + _cleanup_pydecref_ DrgnObject *res = DrgnObject_alloc(prog); if (!res) return NULL; err = linux_helper_idle_task(&res->obj, cpu.uvalue); - if (err) { - Py_DECREF(res); + if (err) return set_drgn_error(err); - } - return res; + return_ptr(res); } -DrgnObject *drgnpy_linux_helper_radix_tree_lookup(PyObject *self, - PyObject *args, - PyObject *kwds) +PyObject *drgnpy_linux_helper_task_cpu(PyObject *self, PyObject *args, + PyObject *kwds) { - static char *keywords[] = {"root", "index", NULL}; + static char *keywords[] = {"task", NULL}; struct drgn_error *err; - DrgnObject *root; - struct index_arg index = {}; - DrgnObject *res; + DrgnObject *task; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O&:radix_tree_lookup", - keywords, &DrgnObject_type, &root, - index_converter, &index)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!:task_cpu", keywords, + &DrgnObject_type, &task)) return NULL; + uint64_t cpu; + err = linux_helper_task_cpu(&task->obj, &cpu); + if (err) + return set_drgn_error(err); + return PyLong_FromUint64(cpu); +} - res = DrgnObject_alloc(DrgnObject_prog(root)); +DrgnObject *drgnpy_linux_helper_xa_load(PyObject *self, PyObject *args, + PyObject *kwds) +{ + static char *keywords[] = {"xa", "index", NULL}; + struct drgn_error *err; + DrgnObject *xa; + struct index_arg index = {}; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O&:xa_load", keywords, + &DrgnObject_type, &xa, index_converter, + &index)) + return NULL; + + _cleanup_pydecref_ DrgnObject *res = DrgnObject_alloc(DrgnObject_prog(xa)); if (!res) return NULL; - err = linux_helper_radix_tree_lookup(&res->obj, &root->obj, - index.uvalue); - if (err) { - Py_DECREF(res); + err = linux_helper_xa_load(&res->obj, &xa->obj, index.uvalue); + if (err) return set_drgn_error(err); - } - return res; + return_ptr(res); } DrgnObject *drgnpy_linux_helper_idr_find(PyObject *self, PyObject *args, @@ -134,22 +178,19 @@ DrgnObject *drgnpy_linux_helper_idr_find(PyObject *self, PyObject *args, struct drgn_error *err; DrgnObject *idr; struct index_arg id = {}; - DrgnObject *res; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O&:idr_find", keywords, &DrgnObject_type, &idr, index_converter, &id)) return NULL; - res = DrgnObject_alloc(DrgnObject_prog(idr)); + _cleanup_pydecref_ DrgnObject *res = + DrgnObject_alloc(DrgnObject_prog(idr)); if (!res) return NULL; err = linux_helper_idr_find(&res->obj, &idr->obj, id.uvalue); - if (err) { - Py_DECREF(res); + if (err) return set_drgn_error(err); - } - return res; + return_ptr(res); } struct prog_or_ns_arg { @@ -236,22 +277,18 @@ DrgnObject *drgnpy_linux_helper_pid_task(PyObject *self, PyObject *args, struct drgn_error *err; DrgnObject *pid; struct index_arg pid_type = {}; - DrgnObject *res; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O&:pid_task", keywords, &DrgnObject_type, &pid, index_converter, &pid_type)) return NULL; - res = DrgnObject_alloc(DrgnObject_prog(pid)); + _cleanup_pydecref_ DrgnObject *res = DrgnObject_alloc(DrgnObject_prog(pid)); if (!res) return NULL; err = linux_helper_pid_task(&res->obj, &pid->obj, pid_type.uvalue); - if (err) { - Py_DECREF(res); + if (err) return set_drgn_error(err); - } - return res; + return_ptr(res); } DrgnObject *drgnpy_linux_helper_find_task(PyObject *self, PyObject *args, @@ -294,7 +331,7 @@ PyObject *drgnpy_linux_helper_kaslr_offset(PyObject *self, PyObject *args, if (!(prog->prog.flags & DRGN_PROGRAM_IS_LINUX_KERNEL)) return PyErr_Format(PyExc_ValueError, "not Linux kernel"); - return PyLong_FromUnsignedLongLong(prog->prog.vmcoreinfo.kaslr_offset); + return PyLong_FromUint64(prog->prog.vmcoreinfo.kaslr_offset); } PyObject *drgnpy_linux_helper_pgtable_l5_enabled(PyObject *self, PyObject *args, diff --git a/libdrgn/python/language.c b/libdrgn/python/language.c index 85ef5c5f1..1fa30370e 100644 --- a/libdrgn/python/language.c +++ b/libdrgn/python/language.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include "drgnpy.h" #include "../array.h" @@ -66,8 +66,7 @@ int add_languages(void) static_assert(array_size(attr_names) == DRGN_NUM_LANGUAGES, "missing language in attr_names"); for (size_t i = 0; i < DRGN_NUM_LANGUAGES; i++) { - Language *language_obj = - (Language *)Language_type.tp_alloc(&Language_type, 0); + Language *language_obj = call_tp_alloc(Language); if (!language_obj) return -1; language_obj->attr_name = attr_names[i]; diff --git a/libdrgn/python/module.c b/libdrgn/python/main.c similarity index 88% rename from libdrgn/python/module.c rename to libdrgn/python/main.c index de09da676..a001e000e 100644 --- a/libdrgn/python/module.c +++ b/libdrgn/python/main.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #ifdef WITH_KDUMPFILE @@ -70,7 +70,6 @@ static PyObject *sizeof_(PyObject *self, PyObject *arg) { struct drgn_error *err; uint64_t size; - if (PyObject_TypeCheck(arg, &DrgnType_type)) { err = drgn_type_sizeof(((DrgnType *)arg)->type, &size); } else if (PyObject_TypeCheck(arg, &DrgnObject_type)) { @@ -82,7 +81,7 @@ static PyObject *sizeof_(PyObject *self, PyObject *arg) } if (err) return set_drgn_error(err); - return PyLong_FromUnsignedLongLong(size); + return PyLong_FromUint64(size); } static PyObject *offsetof_(PyObject *self, PyObject *args, PyObject *kwds) @@ -99,7 +98,7 @@ static PyObject *offsetof_(PyObject *self, PyObject *args, PyObject *kwds) err = drgn_type_offsetof(type->type, member, &offset); if (err) return set_drgn_error(err); - return PyLong_FromUnsignedLongLong(offset); + return PyLong_FromUint64(offset); } static PyMethodDef drgn_methods[] = { @@ -126,13 +125,20 @@ static PyMethodDef drgn_methods[] = { (PyCFunction)drgnpy_linux_helper_direct_mapping_offset, METH_O}, {"_linux_helper_read_vm", (PyCFunction)drgnpy_linux_helper_read_vm, METH_VARARGS | METH_KEYWORDS}, + {"_linux_helper_follow_phys", + (PyCFunction)drgnpy_linux_helper_follow_phys, + METH_VARARGS | METH_KEYWORDS}, {"_linux_helper_per_cpu_ptr", (PyCFunction)drgnpy_linux_helper_per_cpu_ptr, METH_VARARGS | METH_KEYWORDS}, + {"_linux_helper_cpu_curr", (PyCFunction)drgnpy_linux_helper_cpu_curr, + METH_VARARGS | METH_KEYWORDS}, {"_linux_helper_idle_task", (PyCFunction)drgnpy_linux_helper_idle_task, METH_VARARGS | METH_KEYWORDS}, - {"_linux_helper_radix_tree_lookup", - (PyCFunction)drgnpy_linux_helper_radix_tree_lookup, + {"_linux_helper_task_cpu", (PyCFunction)drgnpy_linux_helper_task_cpu, + METH_VARARGS | METH_KEYWORDS}, + {"_linux_helper_xa_load", + (PyCFunction)drgnpy_linux_helper_xa_load, METH_VARARGS | METH_KEYWORDS}, {"_linux_helper_idr_find", (PyCFunction)drgnpy_linux_helper_idr_find, METH_VARARGS | METH_KEYWORDS}, @@ -182,34 +188,29 @@ static int add_type_aliases(PyObject *m) return -1; } - PyObject *os_module = PyImport_ImportModule("os"); + _cleanup_pydecref_ PyObject *os_module = PyImport_ImportModule("os"); if (!os_module) return -1; - PyObject *os_PathLike = PyObject_GetAttrString(os_module, "PathLike"); - Py_DECREF(os_module); + _cleanup_pydecref_ PyObject *os_PathLike = + PyObject_GetAttrString(os_module, "PathLike"); if (!os_PathLike) return -1; - PyObject *item = Py_BuildValue("OOO", &PyUnicode_Type, &PyBytes_Type, - os_PathLike); - Py_DECREF(os_PathLike); + _cleanup_pydecref_ PyObject *item = + Py_BuildValue("OOO", &PyUnicode_Type, &PyBytes_Type, + os_PathLike); if (!item) return -1; - PyObject *typing_module = PyImport_ImportModule("typing"); - if (!typing_module) { - Py_DECREF(item); + _cleanup_pydecref_ PyObject *typing_module = + PyImport_ImportModule("typing"); + if (!typing_module) return -1; - } - PyObject *typing_Union = PyObject_GetAttrString(typing_module, "Union"); - Py_DECREF(typing_module); - if (!typing_Union) { - Py_DECREF(item); + _cleanup_pydecref_ PyObject *typing_Union = + PyObject_GetAttrString(typing_module, "Union"); + if (!typing_Union) return -1; - } PyObject *Path = PyObject_GetItem(typing_Union, item); - Py_DECREF(typing_Union); - Py_DECREF(item); if (!Path) return -1; if (PyModule_AddObject(m, "Path", Path) == -1) { @@ -242,7 +243,8 @@ DRGNPY_PUBLIC PyMODINIT_FUNC PyInit__drgn(void) add_type(m, &TypeEnumerator_type) || add_type(m, &TypeMember_type) || add_type(m, &TypeParameter_type) || - add_type(m, &TypeTemplateParameter_type)) + add_type(m, &TypeTemplateParameter_type) || + init_logging()) goto err; FaultError_type.tp_base = (PyTypeObject *)PyExc_Exception; diff --git a/libdrgn/python/object.c b/libdrgn/python/object.c index 46a96ba0e..b524a2f0b 100644 --- a/libdrgn/python/object.c +++ b/libdrgn/python/object.c @@ -1,10 +1,11 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include #include "drgnpy.h" +#include "../cleanup.h" #include "../error.h" #include "../object.h" #include "../serialize.h" @@ -18,19 +19,21 @@ static int DrgnObject_literal(struct drgn_object *res, PyObject *literal) if (PyBool_Check(literal)) { err = drgn_object_bool_literal(res, literal == Py_True); } else if (PyLong_Check(literal)) { - unsigned long long uvalue; - bool is_negative; - - is_negative = Py_SIZE(literal) < 0; - if (is_negative) { - literal = PyNumber_Negative(literal); - if (!literal) + bool is_negative = false; + uint64_t uvalue = PyLong_AsUint64(literal); + + /* Assume an overflow is due to a negative number and retry */ + if (uvalue == (uint64_t)-1 && PyErr_Occurred() && + PyErr_ExceptionMatches(PyExc_OverflowError)) { + is_negative = true; + PyErr_Clear(); + _cleanup_pydecref_ PyObject *negated = + PyNumber_Negative(literal); + if (!negated) return -1; + uvalue = PyLong_AsUint64(negated); } - uvalue = PyLong_AsUnsignedLongLong(literal); - if (is_negative) - Py_DECREF(literal); - if (uvalue == (unsigned long long)-1 && PyErr_Occurred()) + if (uvalue == (uint64_t)-1 && PyErr_Occurred()) return -1; err = drgn_object_integer_literal(res, uvalue); if (!err && is_negative) @@ -48,6 +51,43 @@ static int DrgnObject_literal(struct drgn_object *res, PyObject *literal) return 0; } +static void * +py_long_to_bytes_for_object_type(PyObject *value_obj, + const struct drgn_object_type *type) +{ + if (!PyNumber_Check(value_obj)) { + return set_error_type_name("'%s' value must be number", + drgn_object_type_qualified(type)); + } + _cleanup_pydecref_ PyObject *long_obj = PyNumber_Long(value_obj); + if (!long_obj) + return NULL; + uint64_t size = drgn_value_size(type->bit_size); + _cleanup_free_ void *buf = malloc64(size); + if (!buf) { + PyErr_NoMemory(); + return NULL; + } + // _PyLong_AsByteArray() still returns the least significant bytes on + // OverflowError unless the object is negative and is_signed is false. + // So, we always pass is_signed as true. + int r = _PyLong_AsByteArray((PyLongObject *)long_obj, buf, size, + type->little_endian, true); + if (r) { + PyObject *exc_type, *exc_value, *exc_traceback; + PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); + if (PyErr_GivenExceptionMatches(exc_type, PyExc_OverflowError)) { + Py_XDECREF(exc_traceback); + Py_XDECREF(exc_value); + Py_DECREF(exc_type); + } else { + PyErr_Restore(exc_type, exc_value, exc_traceback); + return NULL; + } + } + return_ptr(buf); +} + static int serialize_py_object(struct drgn_program *prog, char *buf, uint64_t buf_bit_size, uint64_t bit_offset, PyObject *value_obj, @@ -59,7 +99,6 @@ static int serialize_compound_value(struct drgn_program *prog, char *buf, const struct drgn_object_type *type) { struct drgn_error *err; - int ret = -1; if (!PyMapping_Check(value_obj)) { set_error_type_name("'%s' value must be dictionary or mapping", @@ -67,16 +106,13 @@ static int serialize_compound_value(struct drgn_program *prog, char *buf, return -1; } - PyObject *tmp = PyMapping_Items(value_obj); + _cleanup_pydecref_ PyObject *tmp = PyMapping_Items(value_obj); if (!tmp) return -1; - - /* - * Since Python 3.7, PyMapping_Items() always returns a list. However, - * before that, it could also return a tuple. - */ - PyObject *items = PySequence_Fast(tmp, "items must be sequence"); - Py_DECREF(tmp); + // Since Python 3.7, PyMapping_Items() always returns a list. However, + // before that, it could also return a tuple. + _cleanup_pydecref_ PyObject *items = + PySequence_Fast(tmp, "items must be sequence"); if (!items) return -1; @@ -85,17 +121,17 @@ static int serialize_compound_value(struct drgn_program *prog, char *buf, PyObject *item = PySequence_Fast_GET_ITEM(items, i); if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 2) { PyErr_SetString(PyExc_TypeError, "invalid item"); - goto out; + return -1; } PyObject *key = PyTuple_GET_ITEM(item, 0); if (!PyUnicode_Check(key)) { PyErr_SetString(PyExc_TypeError, "member key must be string"); - goto out; + return -1; } const char *member_name = PyUnicode_AsUTF8(key); if (!member_name) - goto out; + return -1; struct drgn_type_member *member; uint64_t member_bit_offset; @@ -103,7 +139,7 @@ static int serialize_compound_value(struct drgn_program *prog, char *buf, &member, &member_bit_offset); if (err) { set_drgn_error(err); - goto out; + return -1; } struct drgn_qualified_type member_qualified_type; uint64_t member_bit_field_size; @@ -111,25 +147,22 @@ static int serialize_compound_value(struct drgn_program *prog, char *buf, &member_bit_field_size); if (err) { set_drgn_error(err); - goto out; + return -1; } struct drgn_object_type member_type; err = drgn_object_type(member_qualified_type, member_bit_field_size, &member_type); if (err) - goto out; + return -1; if (serialize_py_object(prog, buf, buf_bit_size, bit_offset + member_bit_offset, PyTuple_GET_ITEM(item, 1), &member_type) == -1) - goto out; + return -1; } - ret = 0; -out: - Py_DECREF(items); - return ret; + return 0; } static int serialize_array_value(struct drgn_program *prog, char *buf, @@ -153,7 +186,7 @@ static int serialize_array_value(struct drgn_program *prog, char *buf, return -1; } - PyObject *seq = PySequence_Fast(value_obj, ""); + _cleanup_pydecref_ PyObject *seq = PySequence_Fast(value_obj, ""); if (!seq) { if (PyErr_ExceptionMatches(PyExc_TypeError)) { set_error_type_name("'%s' value must be iterable", @@ -163,7 +196,6 @@ static int serialize_array_value(struct drgn_program *prog, char *buf, } size_t seq_length = PySequence_Fast_GET_SIZE(seq); if (seq_length > length) { - Py_DECREF(seq); PyErr_SetString(PyExc_ValueError, "too many items in array value"); return -1; @@ -173,13 +205,10 @@ static int serialize_array_value(struct drgn_program *prog, char *buf, if (serialize_py_object(prog, buf, buf_bit_size, bit_offset + i * element_type.bit_size, PySequence_Fast_GET_ITEM(seq, i), - &element_type) == -1) { - Py_DECREF(seq); + &element_type) == -1) return -1; - } } - Py_DECREF(seq); return 0; } @@ -207,16 +236,16 @@ static int serialize_py_object(struct drgn_program *prog, char *buf, drgn_object_type_qualified(type)); return -1; } - PyObject *long_obj = PyNumber_Long(value_obj); + _cleanup_pydecref_ PyObject *long_obj = + PyNumber_Long(value_obj); if (!long_obj) return -1; union { int64_t svalue; uint64_t uvalue; } tmp; - tmp.uvalue = PyLong_AsUnsignedLongLongMask(long_obj); - Py_DECREF(long_obj); - if (tmp.uvalue == (unsigned long long)-1 && PyErr_Occurred()) + tmp.uvalue = PyLong_AsUint64Mask(long_obj); + if (tmp.uvalue == (uint64_t)-1 && PyErr_Occurred()) return -1; if (type->encoding == DRGN_OBJECT_ENCODING_SIGNED) { tmp.svalue = truncate_signed(tmp.svalue, @@ -229,6 +258,19 @@ static int serialize_py_object(struct drgn_program *prog, char *buf, type->little_endian); return 0; } + case DRGN_OBJECT_ENCODING_SIGNED_BIG: + case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: { + _cleanup_free_ void *tmp = + py_long_to_bytes_for_object_type(value_obj, type); + if (!tmp) + return -1; + int src_bit_offset = 0; + if (!type->little_endian) + src_bit_offset = -type->bit_size % 8; + copy_bits(buf + bit_offset / 8, bit_offset % 8, tmp, + src_bit_offset, type->bit_size, type->little_endian); + return 0; + } case DRGN_OBJECT_ENCODING_FLOAT: { if (!PyNumber_Check(value_obj)) { set_error_type_name("'%s' value must be number", @@ -241,7 +283,12 @@ static int serialize_py_object(struct drgn_program *prog, char *buf, union { uint64_t uvalue; double fvalue64; - float fvalue32; + struct { +#if !HOST_LITTLE_ENDIAN + float pad; +#endif + float fvalue32; + }; } tmp; if (type->bit_size == 64) tmp.fvalue64 = fvalue; @@ -274,19 +321,10 @@ static int serialize_py_object(struct drgn_program *prog, char *buf, } static int buffer_object_from_value(struct drgn_object *res, - struct drgn_qualified_type qualified_type, + const struct drgn_object_type *type, PyObject *value_obj) { - struct drgn_error *err; - - struct drgn_object_type type; - err = drgn_object_type(qualified_type, 0, &type); - if (err) { - set_drgn_error(err); - return -1; - } - - uint64_t size = drgn_value_size(type.bit_size); + uint64_t size = drgn_value_size(type->bit_size); if (size > SIZE_MAX) { PyErr_NoMemory(); return -1; @@ -305,14 +343,14 @@ static int buffer_object_from_value(struct drgn_object *res, } memset(buf, 0, size); - if (serialize_py_object(drgn_object_program(res), buf, type.bit_size, 0, - value_obj, &type) == -1) { + if (serialize_py_object(drgn_object_program(res), buf, type->bit_size, + 0, value_obj, type) == -1) { if (buf != value.ibuf) free(buf); return -1; } - drgn_object_reinit(res, &type, DRGN_OBJECT_VALUE); + drgn_object_reinit(res, type, DRGN_OBJECT_VALUE); res->value = value; return 0; } @@ -331,8 +369,6 @@ static DrgnObject *DrgnObject_new(PyTypeObject *subtype, PyObject *args, struct index_arg bit_offset = { .allow_none = true, .is_none = true }; struct index_arg bit_field_size = { .allow_none = true, .is_none = true }; struct drgn_qualified_type qualified_type; - DrgnObject *obj; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|OO$O&O&O&:Object", keywords, &Program_type, &prog, &type_obj, &value_obj, index_converter, @@ -349,18 +385,18 @@ static DrgnObject *DrgnObject_new(PyTypeObject *subtype, PyObject *args, return NULL; } - obj = DrgnObject_alloc(prog); + _cleanup_pydecref_ DrgnObject *obj = DrgnObject_alloc(prog); if (!obj) return NULL; if (!address.is_none && value_obj != Py_None) { PyErr_SetString(PyExc_ValueError, "object cannot have address and value"); - goto err; + return NULL; } else if (!address.is_none) { if (!qualified_type.type) { PyErr_SetString(PyExc_ValueError, "reference must have type"); - goto err; + return NULL; } err = drgn_object_set_reference(&obj->obj, qualified_type, @@ -373,130 +409,126 @@ static DrgnObject *DrgnObject_new(PyTypeObject *subtype, PyObject *args, if (!bit_offset.is_none) { PyErr_SetString(PyExc_ValueError, "literal cannot have bit offset"); - goto err; + return NULL; } if (!bit_field_size.is_none) { PyErr_SetString(PyExc_ValueError, "literal cannot be bit field"); - goto err; + return NULL; } ret = DrgnObject_literal(&obj->obj, value_obj); if (ret == -1) { - goto err; + return NULL; } else if (ret) { PyErr_Format(PyExc_TypeError, "cannot create %s literal", Py_TYPE(value_obj)->tp_name); - goto err; + return NULL; } err = NULL; } else if (value_obj != Py_None) { if (!bit_offset.is_none) { PyErr_SetString(PyExc_ValueError, "value cannot have bit offset"); - goto err; + return NULL; } - enum drgn_object_encoding encoding = - drgn_type_object_encoding(qualified_type.type); - if (!drgn_object_encoding_is_complete(encoding)) { - err = drgn_error_incomplete_type("cannot create value with %s type", - qualified_type.type); - set_drgn_error(err); - goto err; - - } - if (!bit_field_size.is_none && - encoding != DRGN_OBJECT_ENCODING_SIGNED && - encoding != DRGN_OBJECT_ENCODING_UNSIGNED) { - PyErr_SetString(PyExc_ValueError, - "bit field must be integer"); - goto err; - } + struct drgn_object_type object_type; + err = drgn_object_type(qualified_type, bit_field_size.uvalue, + &object_type); + if (err) + return set_drgn_error(err); - switch (encoding) { + SWITCH_ENUM(object_type.encoding, case DRGN_OBJECT_ENCODING_BUFFER: - if (buffer_object_from_value(&obj->obj, qualified_type, + if (buffer_object_from_value(&obj->obj, &object_type, value_obj) == -1) - goto err; + return NULL; err = NULL; break; case DRGN_OBJECT_ENCODING_SIGNED: case DRGN_OBJECT_ENCODING_UNSIGNED: { - PyObject *long_obj; - union { - int64_t svalue; - uint64_t uvalue; - } tmp; - if (!PyNumber_Check(value_obj)) { - set_error_type_name("'%s' value must be number", - qualified_type); - goto err; + return set_error_type_name("'%s' value must be number", + qualified_type); } - long_obj = PyNumber_Long(value_obj); + _cleanup_pydecref_ PyObject *long_obj = + PyNumber_Long(value_obj); if (!long_obj) - goto err; - tmp.uvalue = PyLong_AsUnsignedLongLongMask(long_obj); - Py_DECREF(long_obj); - if (tmp.uvalue == (unsigned long long)-1 && - PyErr_Occurred()) - goto err; - if (encoding == DRGN_OBJECT_ENCODING_SIGNED) { - err = drgn_object_set_signed(&obj->obj, - qualified_type, - tmp.svalue, - bit_field_size.uvalue); + return NULL; + union { + int64_t svalue; + uint64_t uvalue; + } tmp = { + .uvalue = PyLong_AsUint64Mask(long_obj) + }; + if (tmp.uvalue == (uint64_t)-1 && PyErr_Occurred()) + return NULL; + if (object_type.encoding == DRGN_OBJECT_ENCODING_SIGNED) { + err = drgn_object_set_signed_internal(&obj->obj, + &object_type, + tmp.svalue); } else { - err = drgn_object_set_unsigned(&obj->obj, - qualified_type, - tmp.uvalue, - bit_field_size.uvalue); + err = drgn_object_set_unsigned_internal(&obj->obj, + &object_type, + tmp.uvalue); } break; } + case DRGN_OBJECT_ENCODING_SIGNED_BIG: + case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: { + _cleanup_free_ void *tmp = + py_long_to_bytes_for_object_type(value_obj, + &object_type); + if (!tmp) + return NULL; + uint64_t src_bit_offset = 0; + if (!object_type.little_endian) + src_bit_offset = -object_type.bit_size % 8; + err = drgn_object_set_from_buffer_internal(&obj->obj, + &object_type, + tmp, + src_bit_offset); + break; + } case DRGN_OBJECT_ENCODING_FLOAT: { - double fvalue; - if (!PyNumber_Check(value_obj)) { - set_error_type_name("'%s' value must be number", - qualified_type); - goto err; + return set_error_type_name("'%s' value must be number", + qualified_type); } - fvalue = PyFloat_AsDouble(value_obj); + double fvalue = PyFloat_AsDouble(value_obj); if (fvalue == -1.0 && PyErr_Occurred()) - goto err; - err = drgn_object_set_float(&obj->obj, qualified_type, - fvalue); + return NULL; + err = drgn_object_set_float_internal(&obj->obj, + &object_type, + fvalue); break; } - default: - UNREACHABLE(); - } + case DRGN_OBJECT_ENCODING_NONE: + case DRGN_OBJECT_ENCODING_INCOMPLETE_BUFFER: + case DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER: + err = drgn_error_incomplete_type("cannot create value with %s type", + qualified_type.type); + break; + ) } else { if (!qualified_type.type) { PyErr_SetString(PyExc_ValueError, "absent object must have type"); - goto err; + return NULL; } if (!bit_offset.is_none) { PyErr_SetString(PyExc_ValueError, "absent object cannot have bit offset"); - goto err; + return NULL; } err = drgn_object_set_absent(&obj->obj, qualified_type, bit_field_size.uvalue); } - if (err) { - set_drgn_error(err); - goto err; - } - return obj; - -err: - Py_DECREF(obj); - return NULL; + if (err) + return set_drgn_error(err); + return_ptr(obj); } static void DrgnObject_dealloc(DrgnObject *self) @@ -551,7 +583,8 @@ static PyObject *DrgnObject_compound_value(struct drgn_object *obj, goto out; } - PyObject *member_value = DrgnObject_value_impl(&member); + _cleanup_pydecref_ PyObject *member_value = + DrgnObject_value_impl(&member); if (!member_value) { Py_CLEAR(dict); goto out; @@ -564,7 +597,6 @@ static PyObject *DrgnObject_compound_value(struct drgn_object *obj, } else { ret = PyDict_Update(dict, member_value); } - Py_DECREF(member_value); if (ret) { Py_CLEAR(dict); goto out; @@ -641,22 +673,33 @@ static PyObject *DrgnObject_value_impl(struct drgn_object *obj) switch (obj->encoding) { case DRGN_OBJECT_ENCODING_SIGNED: { int64_t svalue; - err = drgn_object_read_signed(obj, &svalue); if (err) return set_drgn_error(err); - return PyLong_FromLongLong(svalue); + return PyLong_FromInt64(svalue); } case DRGN_OBJECT_ENCODING_UNSIGNED: { uint64_t uvalue; - err = drgn_object_read_unsigned(obj, &uvalue); if (err) return set_drgn_error(err); if (drgn_type_kind(underlying_type) == DRGN_TYPE_BOOL) Py_RETURN_BOOL(uvalue); else - return PyLong_FromUnsignedLongLong(uvalue); + return PyLong_FromUint64(uvalue); + } + case DRGN_OBJECT_ENCODING_SIGNED_BIG: + case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: { + union drgn_value value_mem; + const union drgn_value *value; + err = drgn_object_read_value(obj, &value_mem, &value); + if (err) + return set_drgn_error(err); + return _PyLong_FromByteArray((void *)value->bufp, + drgn_object_size(obj), + obj->little_endian, + obj->encoding + == DRGN_OBJECT_ENCODING_SIGNED_BIG); } case DRGN_OBJECT_ENCODING_FLOAT: { double fvalue; @@ -707,40 +750,33 @@ static PyObject *DrgnObject_string(DrgnObject *self) static DrgnObject *DrgnObject_address_of(DrgnObject *self) { struct drgn_error *err; - DrgnObject *res; - - res = DrgnObject_alloc(DrgnObject_prog(self)); + _cleanup_pydecref_ DrgnObject *res = + DrgnObject_alloc(DrgnObject_prog(self)); if (!res) return NULL; - err = drgn_object_address_of(&res->obj, &self->obj); - if (err) { - Py_DECREF(res); + if (err) return set_drgn_error(err); - } - return res; + return_ptr(res); } static DrgnObject *DrgnObject_read(DrgnObject *self) { struct drgn_error *err; - DrgnObject *res; - SWITCH_ENUM(self->obj.kind, case DRGN_OBJECT_VALUE: Py_INCREF(self); return self; - case DRGN_OBJECT_REFERENCE: - res = DrgnObject_alloc(DrgnObject_prog(self)); + case DRGN_OBJECT_REFERENCE: { + _cleanup_pydecref_ DrgnObject *res = + DrgnObject_alloc(DrgnObject_prog(self)); if (!res) return NULL; - err = drgn_object_read(&res->obj, &self->obj); - if (err) { - Py_DECREF(res); + if (err) return set_drgn_error(err); - } - return res; + return_ptr(res); + } case DRGN_OBJECT_ABSENT: return set_drgn_error(&drgn_error_object_absent); ) @@ -749,16 +785,14 @@ static DrgnObject *DrgnObject_read(DrgnObject *self) static PyObject *DrgnObject_to_bytes(DrgnObject *self) { struct drgn_error *err; - PyObject *buf = PyBytes_FromStringAndSize(NULL, - drgn_object_size(&self->obj)); + _cleanup_pydecref_ PyObject *buf = + PyBytes_FromStringAndSize(NULL, drgn_object_size(&self->obj)); if (!buf) return NULL; err = drgn_object_read_bytes(&self->obj, PyBytes_AS_STRING(buf)); - if (err) { - Py_DECREF(buf); + if (err) return set_drgn_error(err); - } - return buf; + return_ptr(buf); } static DrgnObject *DrgnObject_from_bytes(PyTypeObject *type, PyObject *args, @@ -820,50 +854,40 @@ static int append_bit_offset(PyObject *parts, uint8_t bit_offset) static PyObject *DrgnObject_repr(DrgnObject *self) { struct drgn_error *err; - PyObject *parts, *tmp, *ret = NULL; - char *type_name; - - parts = PyList_New(0); + _cleanup_pydecref_ PyObject *parts = PyList_New(0); if (!parts) return NULL; + char *type_name; err = drgn_format_type_name(drgn_object_qualified_type(&self->obj), &type_name); - if (err) { - set_drgn_error(err); - goto out; - } - tmp = PyUnicode_FromString(type_name); + if (err) + return set_drgn_error(err); + _cleanup_pydecref_ PyObject *tmp = PyUnicode_FromString(type_name); free(type_name); if (!tmp) - goto out; + return NULL; - if (append_format(parts, "Object(prog, %R", tmp) == -1) { - Py_DECREF(tmp); - goto out; - } - Py_DECREF(tmp); + if (append_format(parts, "Object(prog, %R", tmp) == -1) + return NULL; SWITCH_ENUM(self->obj.kind, case DRGN_OBJECT_VALUE: { if (append_string(parts, ", value=") == -1) - goto out; - PyObject *value_obj = DrgnObject_value(self); + return NULL; + _cleanup_pydecref_ PyObject *value_obj = DrgnObject_value(self); if (!value_obj) - goto out; - if (drgn_type_kind(drgn_underlying_type(self->obj.type)) == - DRGN_TYPE_POINTER) - tmp = PyNumber_ToBase(value_obj, 16); + return NULL; + _cleanup_pydecref_ PyObject *part; + if (drgn_type_kind(drgn_underlying_type(self->obj.type)) + == DRGN_TYPE_POINTER) + part = PyNumber_ToBase(value_obj, 16); else - tmp = PyObject_Repr(value_obj); - Py_DECREF(value_obj); - if (!tmp) - goto out; - if (PyList_Append(parts, tmp) == -1) { - Py_DECREF(tmp); - goto out; - } - Py_DECREF(tmp); + part = PyObject_Repr(value_obj); + if (!part) + return NULL; + if (PyList_Append(parts, part) == -1) + return NULL; break; } case DRGN_OBJECT_REFERENCE: { @@ -871,7 +895,7 @@ static PyObject *DrgnObject_repr(DrgnObject *self) snprintf(buf, sizeof(buf), "%" PRIx64, self->obj.address); if (append_format(parts, ", address=0x%s", buf) == -1 || append_bit_offset(parts, self->obj.bit_offset) == -1) - goto out; + return NULL; break; } case DRGN_OBJECT_ABSENT: @@ -881,15 +905,12 @@ static PyObject *DrgnObject_repr(DrgnObject *self) if (self->obj.is_bit_field && append_format(parts, ", bit_field_size=%llu", (unsigned long long)self->obj.bit_size) == -1) - goto out; + return NULL; if (append_string(parts, ")") == -1) - goto out; + return NULL; - ret = join_strings(parts); -out: - Py_DECREF(parts); - return ret; + return join_strings(parts); } static PyObject *DrgnObject_str(DrgnObject *self) @@ -1017,7 +1038,7 @@ static PyObject *DrgnObject_get_absent(DrgnObject *self, void *arg) static PyObject *DrgnObject_get_address(DrgnObject *self, void *arg) { if (self->obj.kind == DRGN_OBJECT_REFERENCE) - return PyLong_FromUnsignedLongLong(self->obj.address); + return PyLong_FromUint64(self->obj.address); else Py_RETURN_NONE; } @@ -1026,7 +1047,7 @@ static PyObject *DrgnObject_get_bit_offset(DrgnObject *self, void *arg) { SWITCH_ENUM(self->obj.kind, case DRGN_OBJECT_REFERENCE: - return PyLong_FromLong(self->obj.bit_offset); + return PyLong_FromUint8(self->obj.bit_offset); case DRGN_OBJECT_VALUE: case DRGN_OBJECT_ABSENT: Py_RETURN_NONE; @@ -1036,7 +1057,7 @@ static PyObject *DrgnObject_get_bit_offset(DrgnObject *self, void *arg) static PyObject *DrgnObject_get_bit_field_size(DrgnObject *self, void *arg) { if (self->obj.is_bit_field) - return PyLong_FromUnsignedLongLong(self->obj.bit_size); + return PyLong_FromUint64(self->obj.bit_size); else Py_RETURN_NONE; } @@ -1113,22 +1134,18 @@ DrgnObject_BINARY_OP(or) DrgnObject_BINARY_OP(xor) #undef DrgnObject_BINARY_OP -#define DrgnObject_UNARY_OP(op) \ -static DrgnObject *DrgnObject_##op(DrgnObject *self) \ -{ \ - struct drgn_error *err; \ - DrgnObject *res; \ - \ - res = DrgnObject_alloc(DrgnObject_prog(self)); \ - if (!res) \ - return NULL; \ - \ - err = drgn_object_##op(&res->obj, &self->obj); \ - if (err) { \ - Py_DECREF(res); \ - return set_drgn_error(err); \ - } \ - return res; \ +#define DrgnObject_UNARY_OP(op) \ +static DrgnObject *DrgnObject_##op(DrgnObject *self) \ +{ \ + struct drgn_error *err; \ + _cleanup_pydecref_ DrgnObject *res = \ + DrgnObject_alloc(DrgnObject_prog(self)); \ + if (!res) \ + return NULL; \ + err = drgn_object_##op(&res->obj, &self->obj); \ + if (err) \ + return set_drgn_error(err); \ + return_ptr(res); \ } DrgnObject_UNARY_OP(pos) DrgnObject_UNARY_OP(neg) @@ -1151,108 +1168,86 @@ static int DrgnObject_bool(DrgnObject *self) static PyObject *DrgnObject_int(DrgnObject *self) { struct drgn_error *err; - union drgn_value value_mem; - const union drgn_value *value; - PyObject *ret; - - if (!drgn_type_is_scalar(self->obj.type)) { - return set_error_type_name("cannot convert '%s' to int", - drgn_object_qualified_type(&self->obj)); - } - - err = drgn_object_read_value(&self->obj, &value_mem, &value); - if (err) - return set_drgn_error(err); - - switch (self->obj.encoding) { + SWITCH_ENUM(self->obj.encoding, case DRGN_OBJECT_ENCODING_SIGNED: - ret = PyLong_FromLongLong(value->svalue); - break; case DRGN_OBJECT_ENCODING_UNSIGNED: - ret = PyLong_FromUnsignedLongLong(value->uvalue); - break; - case DRGN_OBJECT_ENCODING_FLOAT: - ret = PyLong_FromDouble(value->fvalue); - break; - default: - UNREACHABLE(); + case DRGN_OBJECT_ENCODING_SIGNED_BIG: + case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: + return DrgnObject_value(self); + case DRGN_OBJECT_ENCODING_FLOAT: { + double fvalue; + err = drgn_object_read_float(&self->obj, &fvalue); + if (err) + return set_drgn_error(err); + return PyLong_FromDouble(fvalue); } - drgn_object_deinit_value(&self->obj, value); - return ret; + case DRGN_OBJECT_ENCODING_BUFFER: + case DRGN_OBJECT_ENCODING_NONE: + case DRGN_OBJECT_ENCODING_INCOMPLETE_BUFFER: + case DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER: + return set_error_type_name("cannot convert '%s' to int", + drgn_object_qualified_type(&self->obj)); + ) } static PyObject *DrgnObject_float(DrgnObject *self) { struct drgn_error *err; - union drgn_value value_mem; - const union drgn_value *value; - PyObject *ret; - - if (!drgn_type_is_arithmetic(self->obj.type)) { - return set_error_type_name("cannot convert '%s' to float", - drgn_object_qualified_type(&self->obj)); + SWITCH_ENUM(self->obj.encoding, + case DRGN_OBJECT_ENCODING_FLOAT: { + double fvalue; + err = drgn_object_read_float(&self->obj, &fvalue); + if (err) + return set_drgn_error(err); + return PyFloat_FromDouble(fvalue); } - - err = drgn_object_read_value(&self->obj, &value_mem, &value); - if (err) - return set_drgn_error(err); - - switch (self->obj.encoding) { case DRGN_OBJECT_ENCODING_SIGNED: - ret = PyFloat_FromDouble(value->svalue); - break; case DRGN_OBJECT_ENCODING_UNSIGNED: - ret = PyFloat_FromDouble(value->uvalue); - break; - case DRGN_OBJECT_ENCODING_FLOAT: - ret = PyFloat_FromDouble(value->fvalue); - break; - default: - UNREACHABLE(); + case DRGN_OBJECT_ENCODING_SIGNED_BIG: + case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: { + if (drgn_type_kind(drgn_underlying_type(self->obj.type)) + != DRGN_TYPE_POINTER) { + _cleanup_pydecref_ PyObject *value = + DrgnObject_value(self); + if (!value) + return NULL; + return PyObject_CallFunctionObjArgs((PyObject *)&PyFloat_Type, + value, NULL); + } + fallthrough; } - drgn_object_deinit_value(&self->obj, value); - return ret; + case DRGN_OBJECT_ENCODING_BUFFER: + case DRGN_OBJECT_ENCODING_NONE: + case DRGN_OBJECT_ENCODING_INCOMPLETE_BUFFER: + case DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER: + return set_error_type_name("cannot convert '%s' to float", + drgn_object_qualified_type(&self->obj)); + ) } static PyObject *DrgnObject_index(DrgnObject *self) { - struct drgn_error *err; - struct drgn_type *underlying_type; - union drgn_value value_mem; - const union drgn_value *value; - PyObject *ret; - - underlying_type = drgn_underlying_type(self->obj.type); - if (!drgn_type_is_integer(underlying_type) && - drgn_type_kind(underlying_type) != DRGN_TYPE_POINTER) { - return set_error_type_name("'%s' object cannot be interpreted as an integer", - drgn_object_qualified_type(&self->obj)); - } - - err = drgn_object_read_value(&self->obj, &value_mem, &value); - if (err) - return set_drgn_error(err); - - switch (self->obj.encoding) { + SWITCH_ENUM(self->obj.encoding, case DRGN_OBJECT_ENCODING_SIGNED: - ret = PyLong_FromLongLong(value->svalue); - break; case DRGN_OBJECT_ENCODING_UNSIGNED: - ret = PyLong_FromUnsignedLongLong(value->uvalue); - break; - default: - UNREACHABLE(); - } - drgn_object_deinit_value(&self->obj, value); - return ret; + case DRGN_OBJECT_ENCODING_SIGNED_BIG: + case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: + return DrgnObject_value(self); + case DRGN_OBJECT_ENCODING_FLOAT: + case DRGN_OBJECT_ENCODING_BUFFER: + case DRGN_OBJECT_ENCODING_NONE: + case DRGN_OBJECT_ENCODING_INCOMPLETE_BUFFER: + case DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER: + return set_error_type_name("'%s' object cannot be interpreted as an integer", + drgn_object_qualified_type(&self->obj)); + ) } static PyObject *DrgnObject_round(DrgnObject *self, PyObject *args, PyObject *kwds) { static char *keywords[] = {"ndigits", NULL}; - PyObject *ndigits = Py_None, *value, *ret; - + PyObject *ndigits = Py_None; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:round", keywords, &ndigits)) return NULL; @@ -1262,83 +1257,42 @@ static PyObject *DrgnObject_round(DrgnObject *self, PyObject *args, drgn_object_qualified_type(&self->obj)); } - value = DrgnObject_value(self); + _cleanup_pydecref_ PyObject *value = DrgnObject_value(self); if (!value) return NULL; - if (ndigits == Py_None) { - ret = PyObject_CallMethod(value, "__round__", NULL); - Py_DECREF(value); - } else { - PyObject *args, *kwds, *tmp, *type; - - tmp = PyObject_CallMethod(value, "__round__", "O", ndigits); - Py_DECREF(value); - if (!tmp) - return NULL; - value = tmp; - - kwds = PyDict_New(); - if (!kwds) { - Py_DECREF(value); - return NULL; - } - - if (PyDict_SetItemString(kwds, "value", value) == -1) { - Py_DECREF(value); - return NULL; - } - Py_DECREF(value); + if (ndigits == Py_None) + return PyObject_CallMethod(value, "__round__", NULL); - type = DrgnObject_get_type(self, NULL); - if (!type) { - Py_DECREF(kwds); - return NULL; - } - args = Py_BuildValue("OO", DrgnObject_prog(self), type); - Py_DECREF(type); - if (!args) { - Py_DECREF(kwds); - return NULL; - } + _cleanup_pydecref_ PyObject *rounded_value = + PyObject_CallMethod(value, "__round__", "O", ndigits); + if (!rounded_value) + return NULL; - ret = PyObject_Call((PyObject *)&DrgnObject_type, args, kwds); - Py_DECREF(args); - Py_DECREF(kwds); - } - return ret; + _cleanup_pydecref_ PyObject *type = DrgnObject_get_type(self, NULL); + if (!type) + return NULL; + return PyObject_CallFunctionObjArgs((PyObject *)&DrgnObject_type, + DrgnObject_prog(self), type, + rounded_value, NULL); } #define DrgnObject_round_method(func) \ static PyObject *DrgnObject_##func(DrgnObject *self) \ { \ - struct drgn_error *err; \ - union drgn_value value_mem; \ - const union drgn_value *value; \ - PyObject *ret; \ - \ if (!drgn_type_is_arithmetic(self->obj.type)) { \ return set_error_type_name("cannot round '%s'", \ drgn_object_qualified_type(&self->obj));\ } \ - \ - err = drgn_object_read_value(&self->obj, &value_mem, &value); \ + if (self->obj.encoding != DRGN_OBJECT_ENCODING_FLOAT) \ + return DrgnObject_value(self); \ + union drgn_value value_mem; \ + const union drgn_value *value; \ + struct drgn_error *err = \ + drgn_object_read_value(&self->obj, &value_mem, &value); \ if (err) \ return set_drgn_error(err); \ - \ - switch (self->obj.encoding) { \ - case DRGN_OBJECT_ENCODING_SIGNED: \ - ret = PyLong_FromLongLong(value->svalue); \ - break; \ - case DRGN_OBJECT_ENCODING_UNSIGNED: \ - ret = PyLong_FromUnsignedLongLong(value->uvalue); \ - break; \ - case DRGN_OBJECT_ENCODING_FLOAT: \ - ret = PyLong_FromDouble(func(value->fvalue)); \ - break; \ - default: \ - UNREACHABLE(); \ - } \ + PyObject *ret = PyLong_FromDouble(func(value->fvalue)); \ drgn_object_deinit_value(&self->obj, value); \ return ret; \ } @@ -1385,13 +1339,12 @@ static DrgnObject *DrgnObject_member(DrgnObject *self, PyObject *args, static char *keywords[] = {"name", NULL}; struct drgn_error *err; const char *name; - DrgnObject *res; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s:member_", keywords, &name)) return NULL; - res = DrgnObject_alloc(DrgnObject_prog(self)); + _cleanup_pydecref_ DrgnObject *res = + DrgnObject_alloc(DrgnObject_prog(self)); if (!res) return NULL; @@ -1401,11 +1354,9 @@ static DrgnObject *DrgnObject_member(DrgnObject *self, PyObject *args, } else { err = drgn_object_member(&res->obj, &self->obj, name); } - if (err) { - Py_DECREF(res); + if (err) return set_drgn_error(err); - } - return res; + return_ptr(res); } static PyObject *DrgnObject_getattro(DrgnObject *self, PyObject *attr_name) @@ -1509,18 +1460,14 @@ static DrgnObject *DrgnObject_subscript_impl(DrgnObject *self, int64_t index) { struct drgn_error *err; - DrgnObject *res; - - res = DrgnObject_alloc(DrgnObject_prog(self)); + _cleanup_pydecref_ DrgnObject *res = + DrgnObject_alloc(DrgnObject_prog(self)); if (!res) return NULL; - err = drgn_object_subscript(&res->obj, &self->obj, index); - if (err) { - Py_DECREF(res); + if (err) return set_drgn_error(err); - } - return res; + return_ptr(res); } static DrgnObject *DrgnObject_subscript(DrgnObject *self, PyObject *key) @@ -1534,10 +1481,8 @@ static DrgnObject *DrgnObject_subscript(DrgnObject *self, PyObject *key) static ObjectIterator *DrgnObject_iter(DrgnObject *self) { - struct drgn_type *underlying_type; - ObjectIterator *it; - - underlying_type = drgn_underlying_type(self->obj.type); + struct drgn_type *underlying_type = + drgn_underlying_type(self->obj.type); if (drgn_type_kind(underlying_type) != DRGN_TYPE_ARRAY || !drgn_type_is_complete(underlying_type)) { set_error_type_name("'%s' is not iterable", @@ -1545,8 +1490,7 @@ static ObjectIterator *DrgnObject_iter(DrgnObject *self) return NULL; } - it = (ObjectIterator *)ObjectIterator_type.tp_alloc(&ObjectIterator_type, - 0); + ObjectIterator *it = call_tp_alloc(ObjectIterator); if (!it) return NULL; it->obj = self; @@ -1570,14 +1514,12 @@ static int add_to_dir(PyObject *dir, struct drgn_type *type) member = &members[i]; if (member->name) { - PyObject *str = PyUnicode_FromString(member->name); + _cleanup_pydecref_ PyObject *str = + PyUnicode_FromString(member->name); if (!str) return -1; - if (PyList_Append(dir, str) == -1) { - Py_DECREF(str); + if (PyList_Append(dir, str) == -1) return -1; - } - Py_DECREF(str); } else { struct drgn_qualified_type member_type; err = drgn_member_type(member, &member_type, NULL); @@ -1595,28 +1537,23 @@ static int add_to_dir(PyObject *dir, struct drgn_type *type) static PyObject *DrgnObject_dir(DrgnObject *self) { _Py_IDENTIFIER(__dir__); - PyObject *method, *dir; - struct drgn_type *type; - - method = _PyObject_GetAttrId((PyObject *)Py_TYPE(self)->tp_base, - &PyId___dir__); + _cleanup_pydecref_ PyObject *method = + _PyObject_GetAttrId((PyObject *)Py_TYPE(self)->tp_base, + &PyId___dir__); if (!method) return NULL; - - dir = PyObject_CallFunctionObjArgs(method, self, NULL); - Py_DECREF(method); + _cleanup_pydecref_ PyObject *dir = + PyObject_CallFunctionObjArgs(method, self, NULL); if (!dir) return NULL; - type = drgn_underlying_type(self->obj.type); + struct drgn_type *type = drgn_underlying_type(self->obj.type); if (drgn_type_kind(type) == DRGN_TYPE_POINTER) type = drgn_type_type(type).type; - if (add_to_dir(dir, type) == -1) { - Py_DECREF(dir); + if (add_to_dir(dir, type) == -1) return NULL; - } - return dir; + return_ptr(dir); } static PyGetSetDef DrgnObject_getset[] = { @@ -1713,24 +1650,11 @@ PyObject *DrgnObject_NULL(PyObject *self, PyObject *args, PyObject *kwds) { static char *keywords[] = {"prog", "type", NULL}; PyObject *prog_obj, *type_obj; - PyObject *a, *k, *ret; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO:NULL", keywords, &prog_obj, &type_obj)) return NULL; - - a = Py_BuildValue("OO", prog_obj, type_obj); - if (!a) - return NULL; - k = Py_BuildValue("{s:i}", "value", 0); - if (!k) { - Py_DECREF(a); - return NULL; - } - ret = PyObject_Call((PyObject *)&DrgnObject_type, a, k); - Py_DECREF(k); - Py_DECREF(a); - return ret; + return PyObject_CallFunction((PyObject *)&DrgnObject_type, "OOi", + prog_obj, type_obj, 0); } DrgnObject *cast(PyObject *self, PyObject *args, PyObject *kwds) @@ -1739,8 +1663,7 @@ DrgnObject *cast(PyObject *self, PyObject *args, PyObject *kwds) struct drgn_error *err; struct drgn_qualified_type qualified_type; PyObject *type_obj; - DrgnObject *obj, *res; - + DrgnObject *obj; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO!:cast", keywords, &type_obj, &DrgnObject_type, &obj)) return NULL; @@ -1749,16 +1672,15 @@ DrgnObject *cast(PyObject *self, PyObject *args, PyObject *kwds) &qualified_type) == -1) return NULL; - res = DrgnObject_alloc(DrgnObject_prog(obj)); + _cleanup_pydecref_ DrgnObject *res = + DrgnObject_alloc(DrgnObject_prog(obj)); if (!res) return NULL; err = drgn_object_cast(&res->obj, qualified_type, &obj->obj); - if (err) { - Py_DECREF(res); + if (err) return set_drgn_error(err); - } - return res; + return_ptr(res); } DrgnObject *reinterpret(PyObject *self, PyObject *args, PyObject *kwds) @@ -1767,8 +1689,7 @@ DrgnObject *reinterpret(PyObject *self, PyObject *args, PyObject *kwds) struct drgn_error *err; PyObject *type_obj; struct drgn_qualified_type qualified_type; - DrgnObject *obj, *res; - + DrgnObject *obj; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO!:reinterpret", keywords, &type_obj, &DrgnObject_type, &obj)) @@ -1778,16 +1699,15 @@ DrgnObject *reinterpret(PyObject *self, PyObject *args, PyObject *kwds) &qualified_type) == -1) return NULL; - res = DrgnObject_alloc(DrgnObject_prog(obj)); + _cleanup_pydecref_ DrgnObject *res = + DrgnObject_alloc(DrgnObject_prog(obj)); if (!res) return NULL; err = drgn_object_reinterpret(&res->obj, qualified_type, &obj->obj); - if (err) { - Py_DECREF(res); + if (err) return set_drgn_error(err); - } - return res; + return_ptr(res); } DrgnObject *DrgnObject_container_of(PyObject *self, PyObject *args, @@ -1795,11 +1715,10 @@ DrgnObject *DrgnObject_container_of(PyObject *self, PyObject *args, { static char *keywords[] = {"ptr", "type", "member", NULL}; struct drgn_error *err; - DrgnObject *obj, *res; + DrgnObject *obj; PyObject *type_obj; struct drgn_qualified_type qualified_type; const char *member_designator; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!Os:container_of", keywords, &DrgnObject_type, &obj, &type_obj, &member_designator)) @@ -1809,17 +1728,15 @@ DrgnObject *DrgnObject_container_of(PyObject *self, PyObject *args, &qualified_type) == -1) return NULL; - res = DrgnObject_alloc(DrgnObject_prog(obj)); + _cleanup_pydecref_ DrgnObject *res = DrgnObject_alloc(DrgnObject_prog(obj)); if (!res) return NULL; err = drgn_object_container_of(&res->obj, &obj->obj, qualified_type, member_designator); - if (err) { - Py_DECREF(res); + if (err) return set_drgn_error(err); - } - return res; + return_ptr(res); } static void ObjectIterator_dealloc(ObjectIterator *self) @@ -1837,7 +1754,7 @@ static DrgnObject *ObjectIterator_next(ObjectIterator *self) static PyObject *ObjectIterator_length_hint(ObjectIterator *self) { - return PyLong_FromUnsignedLongLong(self->length); + return PyLong_FromUint64(self->length); } static PyMethodDef ObjectIterator_methods[] = { diff --git a/libdrgn/python/platform.c b/libdrgn/python/platform.c index b998f8c3e..0bced53fd 100644 --- a/libdrgn/python/platform.c +++ b/libdrgn/python/platform.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include "drgnpy.h" @@ -7,14 +7,12 @@ PyObject *Platform_wrap(const struct drgn_platform *platform) { struct drgn_error *err; struct drgn_platform *tmp; - Platform *ret; - err = drgn_platform_create(drgn_platform_arch(platform), drgn_platform_flags(platform), &tmp); if (err) return set_drgn_error(err); - ret = (Platform *)Platform_type.tp_alloc(&Platform_type, 0); + Platform *ret = call_tp_alloc(Platform); if (!ret) return NULL; ret->platform = tmp; @@ -85,39 +83,30 @@ static PyObject *Platform_get_flags(Platform *self, void *arg) static PyObject *Platform_get_registers(Platform *self, void *arg) { size_t num_registers = drgn_platform_num_registers(self->platform); - PyObject *tuple = PyTuple_New(num_registers); + _cleanup_pydecref_ PyObject *tuple = PyTuple_New(num_registers); if (!tuple) return NULL; for (size_t i = 0; i < num_registers; i++) { const struct drgn_register *reg = drgn_platform_register(self->platform, i); - PyObject *item = Register_type.tp_alloc(&Register_type, 0); - if (!item) { - Py_DECREF(tuple); + Register *item = call_tp_alloc(Register); + if (!item) return NULL; - } - ((Register *)item)->reg = reg; - PyTuple_SET_ITEM(tuple, i, item); + item->reg = reg; + PyTuple_SET_ITEM(tuple, i, (PyObject *)item); } - return tuple; + return_ptr(tuple); } static PyObject *Platform_repr(Platform *self) { - PyObject *arch_obj, *flags_obj, *ret; - - arch_obj = Platform_get_arch(self, NULL); + _cleanup_pydecref_ PyObject *arch_obj = Platform_get_arch(self, NULL); if (!arch_obj) return NULL; - flags_obj = Platform_get_flags(self, NULL); - if (!flags_obj) { - Py_DECREF(arch_obj); + _cleanup_pydecref_ PyObject *flags_obj = Platform_get_flags(self, NULL); + if (!flags_obj) return NULL; - } - ret = PyUnicode_FromFormat("Platform(%R, %R)", arch_obj, flags_obj); - Py_XDECREF(flags_obj); - Py_XDECREF(arch_obj); - return ret; + return PyUnicode_FromFormat("Platform(%R, %R)", arch_obj, flags_obj); } static PyGetSetDef Platform_getset[] = { @@ -156,26 +145,22 @@ static PyObject *Register_get_names(Register *self, void *arg) { size_t num_names; const char * const *names = drgn_register_names(self->reg, &num_names); - PyObject *ret = PyTuple_New(num_names); + _cleanup_pydecref_ PyObject *ret = PyTuple_New(num_names); for (size_t i = 0; i < num_names; i++) { PyObject *item = PyUnicode_FromString(names[i]); - if (!item) { - Py_DECREF(ret); + if (!item) return NULL; - } PyTuple_SET_ITEM(ret, i, item); } - return ret; + return_ptr(ret); } static PyObject *Register_repr(Register *self) { - PyObject *names_obj = Register_get_names(self, NULL); + _cleanup_pydecref_ PyObject *names_obj = Register_get_names(self, NULL); if (!names_obj) return NULL; - PyObject *ret = PyUnicode_FromFormat("Register(%R)", names_obj); - Py_DECREF(names_obj); - return ret; + return PyUnicode_FromFormat("Register(%R)", names_obj); } static PyGetSetDef Register_getset[] = { diff --git a/libdrgn/python/program.c b/libdrgn/python/program.c index f91352605..693e93cd7 100644 --- a/libdrgn/python/program.c +++ b/libdrgn/python/program.c @@ -1,13 +1,176 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include "drgnpy.h" +#include "../bitops.h" +#include "../error.h" #include "../hash_table.h" +#include "../log.h" #include "../program.h" +#include "../string_builder.h" #include "../util.h" #include "../vector.h" -DEFINE_HASH_SET_FUNCTIONS(pyobjectp_set, ptr_key_hash_pair, scalar_key_eq) +DEFINE_HASH_SET_FUNCTIONS(pyobjectp_set, ptr_key_hash_pair, scalar_key_eq); + +static PyObject *percent_s; +static PyObject *logger; +static PyObject *logger_log; + +static void drgnpy_log_fn(struct drgn_program *prog, void *arg, + enum drgn_log_level level, const char *format, + va_list ap, struct drgn_error *err) +{ + struct string_builder sb = STRING_BUILDER_INIT; + if (!string_builder_vappendf(&sb, format, ap)) + goto out; + if (err && !string_builder_append_error(&sb, err)) + goto out; + + PyGILState_STATE gstate = PyGILState_Ensure(); + PyObject *ret = PyObject_CallFunction(logger_log, "iOs#", + (level + 1) * 10, percent_s, + sb.str ? sb.str : "", + (Py_ssize_t)sb.len); + if (ret) + Py_DECREF(ret); + else + PyErr_WriteUnraisable(logger_log); + PyGILState_Release(gstate); + +out: + free(sb.str); +} + +static int get_log_level(void) +{ + // We don't use getEffectiveLevel() because that doesn't take + // logging.disable() into account. + int level; + for (level = 0; level < DRGN_LOG_NONE; level++) { + _cleanup_pydecref_ PyObject *enabled = + PyObject_CallMethod(logger, "isEnabledFor", "i", + (level + 1) * 10); + if (!enabled) + return -1; + int ret = PyObject_IsTrue(enabled); + if (ret < 0) + return -1; + if (ret) + break; + } + return level; +} + +// This is slightly heinous. We need to sync the Python log level with the +// libdrgn log level, but the Python log level can change at any time, and there +// is no API to be notified of this. So, we monkey patch logger._cache.clear() +// to update the log level on every live program. This only works since CPython +// commit 78c18a9b9a14 ("bpo-30962: Added caching to Logger.isEnabledFor() +// (GH-2752)") (in v3.7), though. Before that, the best we can do is sync the +// level at the time that the program is created. +#if PY_VERSION_HEX >= 0x030700a1 +static int cached_log_level; +static struct pyobjectp_set programs = HASH_TABLE_INIT; + +static int cache_log_level(void) +{ + int level = get_log_level(); + if (level < 0) + return level; + cached_log_level = level; + return 0; +} + +static PyObject *LoggerCacheWrapper_clear(PyObject *self) +{ + PyDict_Clear(self); + if (cache_log_level()) + return NULL; + for (struct pyobjectp_set_iterator it = pyobjectp_set_first(&programs); + it.entry; it = pyobjectp_set_next(it)) { + Program *prog = (Program *)*it.entry; + drgn_program_set_log_level(&prog->prog, cached_log_level); + } + Py_RETURN_NONE; +} + +static PyMethodDef LoggerCacheWrapper_methods[] = { + {"clear", (PyCFunction)LoggerCacheWrapper_clear, METH_NOARGS}, + {}, +}; + +static PyTypeObject LoggerCacheWrapper_type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "_drgn._LoggerCacheWrapper", + .tp_methods = LoggerCacheWrapper_methods, +}; + +static int init_logger_cache_wrapper(void) +{ + LoggerCacheWrapper_type.tp_base = &PyDict_Type; + if (PyType_Ready(&LoggerCacheWrapper_type)) + return -1; + _cleanup_pydecref_ PyObject *cache_wrapper = + PyObject_CallFunction((PyObject *)&LoggerCacheWrapper_type, + NULL); + if (!cache_wrapper) + return -1; + if (PyObject_SetAttrString(logger, "_cache", cache_wrapper)) + return -1; + + return cache_log_level(); +} + +static int Program_init_logging(Program *prog) +{ + PyObject *obj = (PyObject *)prog; + if (pyobjectp_set_insert(&programs, &obj, NULL) < 0) + return -1; + drgn_program_set_log_callback(&prog->prog, drgnpy_log_fn, NULL); + drgn_program_set_log_level(&prog->prog, cached_log_level); + return 0; +} + +static void Program_deinit_logging(Program *prog) +{ + PyObject *obj = (PyObject *)prog; + pyobjectp_set_delete(&programs, &obj); +} +#else +static int init_logger_cache_wrapper(void) { return 0; } + +static int Program_init_logging(Program *prog) +{ + int level = get_log_level(); + if (level < 0) + return level; + drgn_program_set_log_callback(&prog->prog, drgnpy_log_fn, NULL); + drgn_program_set_log_level(&prog->prog, level); + return 0; +} + +static void Program_deinit_logging(Program *prog) {} +#endif + +int init_logging(void) +{ + percent_s = PyUnicode_InternFromString("%s"); + if (!percent_s) + return -1; + + _cleanup_pydecref_ PyObject *logging = PyImport_ImportModule("logging"); + if (!logging) + return -1; + logger = PyObject_CallMethod(logging, "getLogger", "s", "drgn"); + if (!logger) + return -1; + logger_log = PyObject_GetAttrString(logger, "log"); + if (!logger_log) + return -1; + + return init_logger_cache_wrapper(); +} int Program_hold_object(Program *prog, PyObject *obj) { @@ -62,6 +225,16 @@ int Program_type_arg(Program *prog, PyObject *type_obj, bool can_be_none, return 0; } +static void *drgnpy_begin_blocking(struct drgn_program *prog, void *arg) +{ + return PyEval_SaveThread(); +} + +static void drgnpy_end_blocking(struct drgn_program *prog, void *arg, void *state) +{ + PyEval_RestoreThread(state); +} + static Program *Program_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) { @@ -82,23 +255,26 @@ static Program *Program_new(PyTypeObject *subtype, PyObject *args, return NULL; } - PyObject *cache = PyDict_New(); + _cleanup_pydecref_ PyObject *cache = PyDict_New(); if (!cache) return NULL; - Program *prog = (Program *)Program_type.tp_alloc(&Program_type, 0); - if (!prog) { - Py_DECREF(cache); + _cleanup_pydecref_ Program *prog = call_tp_alloc(Program); + if (!prog) return NULL; - } - prog->cache = cache; + prog->cache = no_cleanup_ptr(cache); pyobjectp_set_init(&prog->objects); drgn_program_init(&prog->prog, platform); - return prog; + drgn_program_set_blocking_callback(&prog->prog, drgnpy_begin_blocking, + drgnpy_end_blocking, NULL); + if (Program_init_logging(prog)) + return NULL; + return_ptr(prog); } static void Program_dealloc(Program *self) { + Program_deinit_logging(self); drgn_program_deinit(&self->prog); for (struct pyobjectp_set_iterator it = pyobjectp_set_first(&self->objects); it.entry; @@ -136,39 +312,30 @@ static struct drgn_error *py_memory_read_fn(void *buf, uint64_t address, void *arg, bool physical) { struct drgn_error *err; - PyGILState_STATE gstate; - PyObject *ret; - Py_buffer view; - gstate = PyGILState_Ensure(); - ret = PyObject_CallFunction(arg, "KKKO", (unsigned long long)address, - (unsigned long long)count, - (unsigned long long)offset, - physical ? Py_True : Py_False); - if (!ret) { - err = drgn_error_from_python(); - goto out; - } - if (PyObject_GetBuffer(ret, &view, PyBUF_SIMPLE) == -1) { - err = drgn_error_from_python(); - goto out_ret; - } + PyGILState_guard(); + + _cleanup_pydecref_ PyObject *ret = + PyObject_CallFunction(arg, "KKKO", (unsigned long long)address, + (unsigned long long)count, + (unsigned long long)offset, + physical ? Py_True : Py_False); + if (!ret) + return drgn_error_from_python(); + Py_buffer view; + if (PyObject_GetBuffer(ret, &view, PyBUF_SIMPLE) == -1) + return drgn_error_from_python(); if (view.len != count) { PyErr_Format(PyExc_ValueError, "memory read callback returned buffer of length %zd (expected %zu)", view.len, count); err = drgn_error_from_python(); - goto out_view; + goto out; } memcpy(buf, view.buf, count); - err = NULL; -out_view: - PyBuffer_Release(&view); -out_ret: - Py_DECREF(ret); out: - PyGILState_Release(gstate); + PyBuffer_Release(&view); return err; } @@ -206,79 +373,61 @@ static PyObject *Program_add_memory_segment(Program *self, PyObject *args, Py_RETURN_NONE; } -static struct drgn_error *py_type_find_fn(enum drgn_type_kind kind, - const char *name, size_t name_len, - const char *filename, void *arg, +static struct drgn_error *py_type_find_fn(uint64_t kinds, const char *name, + size_t name_len, const char *filename, + void *arg, struct drgn_qualified_type *ret) { - struct drgn_error *err; - PyGILState_STATE gstate; - PyObject *kind_obj, *name_obj; - PyObject *type_obj; - - gstate = PyGILState_Ensure(); - kind_obj = PyObject_CallFunction(TypeKind_class, "k", - (unsigned long)kind); - if (!kind_obj) { - err = drgn_error_from_python(); - goto out_gstate; - } - name_obj = PyUnicode_FromStringAndSize(name, name_len); - if (!name_obj) { - err = drgn_error_from_python(); - goto out_kind_obj; - } - type_obj = PyObject_CallFunction(PyTuple_GET_ITEM(arg, 1), "OOs", - kind_obj, name_obj, filename); - if (!type_obj) { - err = drgn_error_from_python(); - goto out_name_obj; - } - if (type_obj == Py_None) { - err = &drgn_not_found; - goto out_type_obj; - } - if (!PyObject_TypeCheck(type_obj, &DrgnType_type)) { - PyErr_SetString(PyExc_TypeError, - "type find callback must return Type or None"); - err = drgn_error_from_python(); - goto out_type_obj; - } - /* - * This check is also done in libdrgn, but we need it here because if - * the type isn't from this program, then there's no guarantee that it - * will remain valid after we decrement its reference count. - */ - if (DrgnType_prog((DrgnType *)type_obj) != - (Program *)PyTuple_GET_ITEM(arg, 0)) { - PyErr_SetString(PyExc_ValueError, - "type find callback returned type from wrong program"); - err = drgn_error_from_python(); - goto out_type_obj; + PyGILState_guard(); + + _cleanup_pydecref_ PyObject *name_obj = + PyUnicode_FromStringAndSize(name, name_len); + if (!name_obj) + return drgn_error_from_python(); + + int kind; + for_each_bit(kind, kinds) { + _cleanup_pydecref_ PyObject * + kind_obj = PyObject_CallFunction(TypeKind_class, "i", + kind); + if (!kind_obj) + return drgn_error_from_python(); + _cleanup_pydecref_ PyObject *type_obj = + PyObject_CallFunction(PyTuple_GET_ITEM(arg, 1), "OOs", + kind_obj, name_obj, filename); + if (!type_obj) + return drgn_error_from_python(); + if (type_obj == Py_None) + continue; + if (!PyObject_TypeCheck(type_obj, &DrgnType_type)) { + PyErr_SetString(PyExc_TypeError, + "type find callback must return Type or None"); + return drgn_error_from_python(); + } + // This check is also done in libdrgn, but we need it here + // because if the type isn't from this program, then there's no + // guarantee that it will remain valid after we decrement its + // reference count. + if (DrgnType_prog((DrgnType *)type_obj) + != (Program *)PyTuple_GET_ITEM(arg, 0)) { + PyErr_SetString(PyExc_ValueError, + "type find callback returned type from wrong program"); + return drgn_error_from_python(); + } + ret->type = ((DrgnType *)type_obj)->type; + ret->qualifiers = ((DrgnType *)type_obj)->qualifiers; + return NULL; } - - ret->type = ((DrgnType *)type_obj)->type; - ret->qualifiers = ((DrgnType *)type_obj)->qualifiers; - err = NULL; -out_type_obj: - Py_DECREF(type_obj); -out_name_obj: - Py_DECREF(name_obj); -out_kind_obj: - Py_DECREF(kind_obj); -out_gstate: - PyGILState_Release(gstate); - return err; + return &drgn_not_found; } static PyObject *Program_add_type_finder(Program *self, PyObject *args, PyObject *kwds) { - static char *keywords[] = {"fn", NULL}; struct drgn_error *err; - PyObject *fn, *arg; - int ret; + static char *keywords[] = {"fn", NULL}; + PyObject *fn; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:add_type_finder", keywords, &fn)) return NULL; @@ -288,12 +437,10 @@ static PyObject *Program_add_type_finder(Program *self, PyObject *args, return NULL; } - arg = Py_BuildValue("OO", self, fn); + _cleanup_pydecref_ PyObject *arg = Py_BuildValue("OO", self, fn); if (!arg) return NULL; - ret = Program_hold_object(self, arg); - Py_DECREF(arg); - if (ret == -1) + if (Program_hold_object(self, arg)) return NULL; err = drgn_program_add_type_finder(&self->prog, py_type_find_fn, arg); @@ -307,61 +454,40 @@ static struct drgn_error *py_object_find_fn(const char *name, size_t name_len, enum drgn_find_object_flags flags, void *arg, struct drgn_object *ret) { - struct drgn_error *err; - PyGILState_STATE gstate; - PyObject *name_obj, *flags_obj; - PyObject *obj; - - gstate = PyGILState_Ensure(); - name_obj = PyUnicode_FromStringAndSize(name, name_len); - if (!name_obj) { - err = drgn_error_from_python(); - goto out_gstate; - } - flags_obj = PyObject_CallFunction(FindObjectFlags_class, "i", - (int)flags); - if (!flags_obj) { - err = drgn_error_from_python(); - goto out_name_obj; - } - obj = PyObject_CallFunction(PyTuple_GET_ITEM(arg, 1), "OOOs", - PyTuple_GET_ITEM(arg, 0), name_obj, - flags_obj, filename); - if (!obj) { - err = drgn_error_from_python(); - goto out_flags_obj; - } - if (obj == Py_None) { - err = &drgn_not_found; - goto out_obj; - } + PyGILState_guard(); + + _cleanup_pydecref_ PyObject *name_obj = + PyUnicode_FromStringAndSize(name, name_len); + if (!name_obj) + return drgn_error_from_python(); + _cleanup_pydecref_ PyObject *flags_obj = + PyObject_CallFunction(FindObjectFlags_class, "i", (int)flags); + if (!flags_obj) + return drgn_error_from_python(); + _cleanup_pydecref_ PyObject *obj = + PyObject_CallFunction(PyTuple_GET_ITEM(arg, 1), "OOOs", + PyTuple_GET_ITEM(arg, 0), name_obj, + flags_obj, filename); + if (!obj) + return drgn_error_from_python(); + if (obj == Py_None) + return &drgn_not_found; if (!PyObject_TypeCheck(obj, &DrgnObject_type)) { PyErr_SetString(PyExc_TypeError, "object find callback must return Object or None"); - err = drgn_error_from_python(); - goto out_obj; + return drgn_error_from_python(); } - err = drgn_object_copy(ret, &((DrgnObject *)obj)->obj); -out_obj: - Py_DECREF(obj); -out_flags_obj: - Py_DECREF(flags_obj); -out_name_obj: - Py_DECREF(name_obj); -out_gstate: - PyGILState_Release(gstate); - return err; + return drgn_object_copy(ret, &((DrgnObject *)obj)->obj); } static PyObject *Program_add_object_finder(Program *self, PyObject *args, PyObject *kwds) { - static char *keywords[] = {"fn", NULL}; struct drgn_error *err; - PyObject *fn, *arg; - int ret; + static char *keywords[] = {"fn", NULL}; + PyObject *fn; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:add_object_finder", keywords, &fn)) return NULL; @@ -371,12 +497,10 @@ static PyObject *Program_add_object_finder(Program *self, PyObject *args, return NULL; } - arg = Py_BuildValue("OO", self, fn); + _cleanup_pydecref_ PyObject *arg = Py_BuildValue("OO", self, fn); if (!arg) return NULL; - ret = Program_hold_object(self, arg); - Py_DECREF(arg); - if (ret == -1) + if (Program_hold_object(self, arg)) return NULL; err = drgn_program_add_object_finder(&self->prog, py_object_find_fn, @@ -430,7 +554,7 @@ static PyObject *Program_set_pid(Program *self, PyObject *args, PyObject *kwds) Py_RETURN_NONE; } -DEFINE_VECTOR(path_arg_vector, struct path_arg) +DEFINE_VECTOR(path_arg_vector, struct path_arg); static PyObject *Program_load_debug_info(Program *self, PyObject *args, PyObject *kwds) @@ -448,63 +572,57 @@ static PyObject *Program_load_debug_info(Program *self, PyObject *args, struct path_arg_vector path_args = VECTOR_INIT; const char **paths = NULL; if (paths_obj != Py_None) { - Py_ssize_t length_hint; - PyObject *it, *item; - - it = PyObject_GetIter(paths_obj); + _cleanup_pydecref_ PyObject *it = PyObject_GetIter(paths_obj); if (!it) goto out; - length_hint = PyObject_LengthHint(paths_obj, 1); - if (length_hint == -1) { - Py_DECREF(it); + Py_ssize_t length_hint = PyObject_LengthHint(paths_obj, 1); + if (length_hint == -1) goto out; - } if (!path_arg_vector_reserve(&path_args, length_hint)) { PyErr_NoMemory(); - Py_DECREF(it); goto out; } - while ((item = PyIter_Next(it))) { - struct path_arg *path_arg; - int ret; + for (;;) { + _cleanup_pydecref_ PyObject *item = PyIter_Next(it); + if (!item) + break; - path_arg = path_arg_vector_append_entry(&path_args); + struct path_arg *path_arg = + path_arg_vector_append_entry(&path_args); if (!path_arg) { PyErr_NoMemory(); - Py_DECREF(item); break; } memset(path_arg, 0, sizeof(*path_arg)); - ret = path_converter(item, path_arg); - Py_DECREF(item); - if (!ret) { - path_args.size--; + if (!path_converter(item, path_arg)) { + path_arg_vector_pop(&path_args); break; } } - Py_DECREF(it); if (PyErr_Occurred()) goto out; - paths = malloc_array(path_args.size, sizeof(*paths)); + paths = malloc_array(path_arg_vector_size(&path_args), + sizeof(*paths)); if (!paths) { PyErr_NoMemory(); goto out; } - for (size_t i = 0; i < path_args.size; i++) - paths[i] = path_args.data[i].path; + for (size_t i = 0; i < path_arg_vector_size(&path_args); i++) + paths[i] = path_arg_vector_at(&path_args, i)->path; } - err = drgn_program_load_debug_info(&self->prog, paths, path_args.size, + err = drgn_program_load_debug_info(&self->prog, paths, + path_arg_vector_size(&path_args), load_default, load_main); free(paths); if (err) set_drgn_error(err); out: - for (size_t i = 0; i < path_args.size; i++) - path_cleanup(&path_args.data[i]); + vector_for_each(path_arg_vector, path_arg, &path_args) + path_cleanup(path_arg); path_arg_vector_deinit(&path_args); if (PyErr_Occurred()) return NULL; @@ -528,9 +646,7 @@ static PyObject *Program_read(Program *self, PyObject *args, PyObject *kwds) struct index_arg address = {}; Py_ssize_t size; int physical = 0; - PyObject *buf; bool clear; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&n|p:read", keywords, index_converter, &address, &size, &physical)) @@ -540,7 +656,8 @@ static PyObject *Program_read(Program *self, PyObject *args, PyObject *kwds) PyErr_SetString(PyExc_ValueError, "negative size"); return NULL; } - buf = PyBytes_FromStringAndSize(NULL, size); + _cleanup_pydecref_ PyObject *buf = + PyBytes_FromStringAndSize(NULL, size); if (!buf) return NULL; clear = set_drgn_in_python(); @@ -548,11 +665,9 @@ static PyObject *Program_read(Program *self, PyObject *args, PyObject *kwds) address.uvalue, size, physical); if (clear) clear_drgn_in_python(); - if (err) { - Py_DECREF(buf); + if (err) return set_drgn_error(err); - } - return buf; + return_ptr(buf); } #define METHOD_READ(x, type) \ @@ -596,33 +711,39 @@ static PyObject *Program_find_type(Program *self, PyObject *args, PyObject *kwds &filename)) return NULL; + PyObject *ret = NULL; if (PyObject_TypeCheck(name_or_type, &DrgnType_type)) { if (DrgnType_prog((DrgnType *)name_or_type) != self) { PyErr_SetString(PyExc_ValueError, "type is from different program"); - return NULL; + goto out; } Py_INCREF(name_or_type); - return name_or_type; + ret = name_or_type; + goto out; } else if (!PyUnicode_Check(name_or_type)) { PyErr_SetString(PyExc_TypeError, "type() argument 1 must be str or Type"); - return NULL; + goto out; } const char *name = PyUnicode_AsUTF8(name_or_type); if (!name) - return NULL; + goto out; bool clear = set_drgn_in_python(); struct drgn_qualified_type qualified_type; err = drgn_program_find_type(&self->prog, name, filename.path, &qualified_type); if (clear) clear_drgn_in_python(); + if (err) { + set_drgn_error(err); + goto out; + } + ret = DrgnType_wrap(qualified_type); +out: path_cleanup(&filename); - if (err) - return set_drgn_error(err); - return DrgnType_wrap(qualified_type); + return ret; } static DrgnObject *Program_find_object(Program *self, const char *name, @@ -630,23 +751,22 @@ static DrgnObject *Program_find_object(Program *self, const char *name, enum drgn_find_object_flags flags) { struct drgn_error *err; - DrgnObject *ret; - bool clear; - ret = DrgnObject_alloc(self); + DrgnObject *ret = DrgnObject_alloc(self); if (!ret) - return NULL; - - clear = set_drgn_in_python(); + goto out; + bool clear = set_drgn_in_python(); err = drgn_program_find_object(&self->prog, name, filename->path, flags, &ret->obj); if (clear) clear_drgn_in_python(); - path_cleanup(filename); if (err) { + set_drgn_error(err); Py_DECREF(ret); - return set_drgn_error(err); + ret = NULL; } +out: + path_cleanup(filename); return ret; } @@ -775,7 +895,7 @@ static PyObject *Program_symbols(Program *self, PyObject *args) if (err) return set_drgn_error(err); - PyObject *list = PyList_New(count); + _cleanup_pydecref_ PyObject *list = PyList_New(count); if (!list) { drgn_symbols_destroy(symbols, count); return NULL; @@ -785,15 +905,13 @@ static PyObject *Program_symbols(Program *self, PyObject *args) if (!pysym) { /* Free symbols which aren't yet added to list. */ drgn_symbols_destroy(symbols, count); - /* Free list and all symbols already added. */ - Py_DECREF(list); return NULL; } symbols[i] = NULL; PyList_SET_ITEM(list, i, pysym); } free(symbols); - return list; + return_ptr(list); } static PyObject *Program_symbol(Program *self, PyObject *arg) @@ -833,9 +951,7 @@ static ThreadIterator *Program_threads(Program *self) struct drgn_error *err = drgn_thread_iterator_create(&self->prog, &it); if (err) return set_drgn_error(err); - ThreadIterator *ret = - (ThreadIterator *)ThreadIterator_type.tp_alloc(&ThreadIterator_type, - 0); + ThreadIterator *ret = call_tp_alloc(ThreadIterator); if (!ret) { drgn_thread_iterator_destroy(it); return NULL; @@ -890,27 +1006,35 @@ static PyObject *Program_crashed_thread(Program *self) return Thread_wrap(thread); } +// Used for testing. +static PyObject *Program__log(Program *self, PyObject *args, PyObject *kwds) +{ + int level; + const char *str; + if (!PyArg_ParseTuple(args, "is", &level, &str)) + return NULL; + drgn_log(level, &self->prog, "%s", str); + Py_RETURN_NONE; +} + static DrgnObject *Program_subscript(Program *self, PyObject *key) { struct drgn_error *err; - const char *name; - DrgnObject *ret; - bool clear; if (!PyUnicode_Check(key)) { PyErr_SetObject(PyExc_KeyError, key); return NULL; } - name = PyUnicode_AsUTF8(key); + const char *name = PyUnicode_AsUTF8(key); if (!name) return NULL; - ret = DrgnObject_alloc(self); + _cleanup_pydecref_ DrgnObject *ret = DrgnObject_alloc(self); if (!ret) return NULL; - clear = set_drgn_in_python(); + bool clear = set_drgn_in_python(); err = drgn_program_find_object(&self->prog, name, NULL, DRGN_FIND_OBJECT_ANY, &ret->obj); if (clear) @@ -922,10 +1046,9 @@ static DrgnObject *Program_subscript(Program *self, PyObject *key) } else { set_drgn_error(err); } - Py_DECREF(ret); return NULL; } - return ret; + return_ptr(ret); } static int Program_contains(Program *self, PyObject *key) @@ -1076,6 +1199,7 @@ static PyMethodDef Program_methods[] = { METH_VARARGS | METH_KEYWORDS, drgn_Program_array_type_DOC}, {"function_type", (PyCFunction)Program_function_type, METH_VARARGS | METH_KEYWORDS, drgn_Program_function_type_DOC}, + {"_log", (PyCFunction)Program__log, METH_VARARGS}, {}, }; @@ -1124,14 +1248,13 @@ Program *program_from_core_dump(PyObject *self, PyObject *args, PyObject *kwds) static char *keywords[] = {"path", NULL}; struct drgn_error *err; struct path_arg path = {}; - Program *prog; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:program_from_core_dump", keywords, path_converter, &path)) return NULL; - prog = (Program *)PyObject_CallObject((PyObject *)&Program_type, NULL); + _cleanup_pydecref_ Program *prog = + (Program *)PyObject_CallObject((PyObject *)&Program_type, NULL); if (!prog) { path_cleanup(&path); return NULL; @@ -1139,49 +1262,39 @@ Program *program_from_core_dump(PyObject *self, PyObject *args, PyObject *kwds) err = drgn_program_init_core_dump(&prog->prog, path.path); path_cleanup(&path); - if (err) { - Py_DECREF(prog); + if (err) return set_drgn_error(err); - } - return prog; + return_ptr(prog); } Program *program_from_kernel(PyObject *self) { struct drgn_error *err; - Program *prog; - - prog = (Program *)PyObject_CallObject((PyObject *)&Program_type, NULL); + _cleanup_pydecref_ Program *prog = + (Program *)PyObject_CallObject((PyObject *)&Program_type, NULL); if (!prog) return NULL; - err = drgn_program_init_kernel(&prog->prog); - if (err) { - Py_DECREF(prog); + if (err) return set_drgn_error(err); - } - return prog; + return_ptr(prog); } Program *program_from_pid(PyObject *self, PyObject *args, PyObject *kwds) { - static char *keywords[] = {"pid", NULL}; struct drgn_error *err; + static char *keywords[] = {"pid", NULL}; int pid; - Program *prog; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:program_from_pid", keywords, &pid)) return NULL; - prog = (Program *)PyObject_CallObject((PyObject *)&Program_type, NULL); + _cleanup_pydecref_ Program *prog = + (Program *)PyObject_CallObject((PyObject *)&Program_type, NULL); if (!prog) return NULL; - err = drgn_program_init_pid(&prog->prog, pid); - if (err) { - Py_DECREF(prog); + if (err) return set_drgn_error(err); - } - return prog; + return_ptr(prog); } diff --git a/libdrgn/python/stack_trace.c b/libdrgn/python/stack_trace.c index aa80bb83b..d738da14a 100644 --- a/libdrgn/python/stack_trace.c +++ b/libdrgn/python/stack_trace.c @@ -1,13 +1,12 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include "drgnpy.h" #include "../stack_trace.h" #include "../util.h" PyObject *StackTrace_wrap(struct drgn_stack_trace *trace) { - StackTrace *ret = - (StackTrace *)StackTrace_type.tp_alloc(&StackTrace_type, 0); + StackTrace *ret = call_tp_alloc(StackTrace); if (!ret) return NULL; Py_INCREF(container_of(trace->prog, Program, prog)); @@ -58,8 +57,7 @@ static StackFrame *StackTrace_item(StackTrace *self, Py_ssize_t i) "stack frame index out of range"); return NULL; } - StackFrame *ret = - (StackFrame *)StackFrame_type.tp_alloc(&StackFrame_type, 0); + StackFrame *ret = call_tp_alloc(StackFrame); if (!ret) return NULL; ret->i = i; @@ -115,6 +113,33 @@ static PyObject *StackFrame_str(StackFrame *self) return ret; } +static PyObject *StackFrame_locals(StackFrame *self) +{ + struct drgn_error *err; + const char **names; + size_t count; + err = drgn_stack_frame_locals(self->trace->trace, self->i, &names, + &count); + if (err) + return set_drgn_error(err); + + _cleanup_pydecref_ PyObject *list = PyList_New(count); + if (!list) { + drgn_stack_frame_locals_destroy(names, count); + return NULL; + } + for (size_t i = 0; i < count; i++) { + PyObject *string = PyUnicode_FromString(names[i]); + if (!string) { + drgn_stack_frame_locals_destroy(names, count); + return NULL; + } + PyList_SET_ITEM(list, i, string); + } + drgn_stack_frame_locals_destroy(names, count); + return_ptr(list); +} + static DrgnObject *StackFrame_subscript(StackFrame *self, PyObject *key) { struct drgn_error *err; @@ -126,7 +151,7 @@ static DrgnObject *StackFrame_subscript(StackFrame *self, PyObject *key) const char *name = PyUnicode_AsUTF8(key); if (!name) return NULL; - DrgnObject *ret = DrgnObject_alloc(prog); + _cleanup_pydecref_ DrgnObject *ret = DrgnObject_alloc(prog); if (!ret) return NULL; bool clear = set_drgn_in_python(); @@ -141,10 +166,9 @@ static DrgnObject *StackFrame_subscript(StackFrame *self, PyObject *key) } else { set_drgn_error(err); } - Py_DECREF(ret); return NULL; } - return ret; + return_ptr(ret); } static int StackFrame_contains(StackFrame *self, PyObject *key) @@ -222,12 +246,12 @@ static PyObject *StackFrame_register(StackFrame *self, PyObject *arg) "register value is not known"); return NULL; } - return PyLong_FromUnsignedLongLong(value); + return PyLong_FromUint64(value); } static PyObject *StackFrame_registers(StackFrame *self) { - PyObject *dict = PyDict_New(); + _cleanup_pydecref_ PyObject *dict = PyDict_New(); if (!dict) return NULL; const struct drgn_platform *platform = @@ -240,26 +264,19 @@ static PyObject *StackFrame_registers(StackFrame *self) if (!drgn_stack_frame_register(self->trace->trace, self->i, reg, &value)) continue; - PyObject *value_obj = PyLong_FromUnsignedLongLong(value); - if (!value_obj) { - Py_DECREF(dict); + _cleanup_pydecref_ PyObject *value_obj = + PyLong_FromUint64(value); + if (!value_obj) return NULL; - } size_t num_names; const char * const *names = drgn_register_names(reg, &num_names); for (size_t j = 0; j < num_names; j++) { - int ret = PyDict_SetItemString(dict, names[j], - value_obj); - if (ret == -1) { - Py_DECREF(value_obj); - Py_DECREF(dict); + if (PyDict_SetItemString(dict, names[j], value_obj)) return NULL; - } } - Py_DECREF(value_obj); } - return dict; + return_ptr(dict); } static PyObject *StackFrame_get_name(StackFrame *self, void *arg) @@ -286,7 +303,7 @@ static PyObject *StackFrame_get_pc(StackFrame *self, void *arg) { uint64_t pc; if (drgn_stack_frame_pc(self->trace->trace, self->i, &pc)) { - return PyLong_FromUnsignedLongLong(pc); + return PyLong_FromUint64(pc); } else { PyErr_SetString(PyExc_LookupError, "program counter is not known"); @@ -294,11 +311,25 @@ static PyObject *StackFrame_get_pc(StackFrame *self, void *arg) } } +static PyObject *StackFrame_get_sp(StackFrame *self, void *arg) +{ + uint64_t sp; + if (drgn_stack_frame_sp(self->trace->trace, self->i, &sp)) { + return PyLong_FromUint64(sp); + } else { + PyErr_SetString(PyExc_LookupError, + "stack pointer is not known"); + return NULL; + } +} + static PyMethodDef StackFrame_methods[] = { {"__getitem__", (PyCFunction)StackFrame_subscript, METH_O | METH_COEXIST, drgn_StackFrame___getitem___DOC}, {"__contains__", (PyCFunction)StackFrame_contains, METH_O | METH_COEXIST, drgn_StackFrame___contains___DOC}, + {"locals", (PyCFunction)StackFrame_locals, + METH_NOARGS, drgn_StackFrame_locals_DOC}, {"source", (PyCFunction)StackFrame_source, METH_NOARGS, drgn_StackFrame_source_DOC}, {"symbol", (PyCFunction)StackFrame_symbol, METH_NOARGS, @@ -319,6 +350,7 @@ static PyGetSetDef StackFrame_getset[] = { {"interrupted", (getter)StackFrame_get_interrupted, NULL, drgn_StackFrame_interrupted_DOC}, {"pc", (getter)StackFrame_get_pc, NULL, drgn_StackFrame_pc_DOC}, + {"sp", (getter)StackFrame_get_sp, NULL, drgn_StackFrame_sp_DOC}, {}, }; diff --git a/libdrgn/python/symbol.c b/libdrgn/python/symbol.c index 6220eaa77..ea0d98cb3 100644 --- a/libdrgn/python/symbol.c +++ b/libdrgn/python/symbol.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include @@ -7,9 +7,7 @@ PyObject *Symbol_wrap(struct drgn_symbol *sym, Program *prog) { - Symbol *ret; - - ret = (Symbol *)Symbol_type.tp_alloc(&Symbol_type, 0); + Symbol *ret = call_tp_alloc(Symbol); if (ret) { ret->sym = sym; ret->prog = prog; @@ -43,12 +41,12 @@ static PyObject *Symbol_get_name(Symbol *self, void *arg) static PyObject *Symbol_get_address(Symbol *self, void *arg) { - return PyLong_FromUnsignedLongLong(drgn_symbol_address(self->sym)); + return PyLong_FromUint64(drgn_symbol_address(self->sym)); } static PyObject *Symbol_get_size(Symbol *self, void *arg) { - return PyLong_FromUnsignedLongLong(drgn_symbol_size(self->sym)); + return PyLong_FromUint64(drgn_symbol_size(self->sym)); } static PyObject *Symbol_get_binding(Symbol *self, void *arg) @@ -65,32 +63,22 @@ static PyObject *Symbol_get_kind(Symbol *self, void *arg) static PyObject *Symbol_repr(Symbol *self) { - PyObject *ret = NULL; - PyObject *tmp = PyUnicode_FromString(drgn_symbol_name(self->sym)); - if (!tmp) + _cleanup_pydecref_ PyObject *name = + PyUnicode_FromString(drgn_symbol_name(self->sym)); + if (!name) return NULL; - - PyObject *binding = Symbol_get_binding(self, NULL); + _cleanup_pydecref_ PyObject *binding = Symbol_get_binding(self, NULL); if (!binding) - goto out_tmp; - - PyObject *kind = Symbol_get_kind(self, NULL); + return NULL; + _cleanup_pydecref_ PyObject *kind = Symbol_get_kind(self, NULL); if (!kind) - goto out_binding; + return NULL; char address[19], size[19]; sprintf(address, "0x%" PRIx64, drgn_symbol_address(self->sym)); sprintf(size, "0x%" PRIx64, drgn_symbol_size(self->sym)); - ret = PyUnicode_FromFormat("Symbol(name=%R, address=%s, size=%s, binding=%R, kind=%R)", - tmp, address, size, binding, kind); - - Py_DECREF(kind); -out_binding: - Py_DECREF(binding); -out_tmp: - Py_DECREF(tmp); - return ret; - + return PyUnicode_FromFormat("Symbol(name=%R, address=%s, size=%s, binding=%R, kind=%R)", + name, address, size, binding, kind); } static PyGetSetDef Symbol_getset[] = { diff --git a/libdrgn/python/test.c b/libdrgn/python/test.c index 76ab024e8..65d17f799 100644 --- a/libdrgn/python/test.c +++ b/libdrgn/python/test.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /* * Wrapper functions for testing. diff --git a/libdrgn/python/thread.c b/libdrgn/python/thread.c index 42c3a9cbe..88bdca86b 100644 --- a/libdrgn/python/thread.c +++ b/libdrgn/python/thread.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include "drgnpy.h" #include "../program.h" @@ -12,18 +12,17 @@ static Program *Thread_prog(Thread *self) PyObject *Thread_wrap(struct drgn_thread *thread) { - Thread *ret = (Thread *)Thread_type.tp_alloc(&Thread_type, 0); + _cleanup_pydecref_ Thread *ret = call_tp_alloc(Thread); if (!ret) return NULL; struct drgn_error *err = drgn_thread_dup_internal(thread, &ret->thread); if (err) { ret->thread.prog = NULL; - Py_DECREF(ret); return set_drgn_error(err); } Py_INCREF(container_of(thread->prog, Program, prog)); - return (PyObject *)ret; + return (PyObject *)no_cleanup_ptr(ret); } static void Thread_dealloc(Thread *self) @@ -38,7 +37,7 @@ static void Thread_dealloc(Thread *self) static PyObject *Thread_get_tid(Thread *self) { - return PyLong_FromUnsignedLong(self->thread.tid); + return PyLong_FromUint32(self->thread.tid); } static DrgnObject *Thread_get_object(Thread *self) @@ -47,15 +46,14 @@ static DrgnObject *Thread_get_object(Thread *self) struct drgn_error *err = drgn_thread_object(&self->thread, &object); if (err) return set_drgn_error(err); - DrgnObject *ret = DrgnObject_alloc(Thread_prog(self)); + _cleanup_pydecref_ DrgnObject *ret = + DrgnObject_alloc(Thread_prog(self)); if (!ret) return NULL; err = drgn_object_copy(&ret->obj, object); - if (err) { - Py_DECREF(ret); + if (err) return set_drgn_error(err); - } - return ret; + return_ptr(ret); } static PyObject *Thread_stack_trace(Thread *self) diff --git a/libdrgn/python/type.c b/libdrgn/python/type.c index 6edd2c902..730d783fe 100644 --- a/libdrgn/python/type.c +++ b/libdrgn/python/type.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include @@ -23,19 +23,16 @@ static const char *drgn_type_kind_str(struct drgn_type *type) DRGNPY_PUBLIC PyObject *DrgnType_wrap(struct drgn_qualified_type qualified_type) { - DrgnType *type_obj = (DrgnType *)DrgnType_type.tp_alloc(&DrgnType_type, - 0); + _cleanup_pydecref_ DrgnType *type_obj = call_tp_alloc(DrgnType); if (!type_obj) return NULL; type_obj->type = qualified_type.type; type_obj->qualifiers = qualified_type.qualifiers; Py_INCREF(DrgnType_prog(type_obj)); type_obj->attr_cache = PyDict_New(); - if (!type_obj->attr_cache) { - Py_DECREF(type_obj); + if (!type_obj->attr_cache) return NULL; - } - return (PyObject *)type_obj; + return (PyObject *)no_cleanup_ptr(type_obj); } static inline struct drgn_qualified_type DrgnType_unwrap(DrgnType *type) @@ -118,7 +115,7 @@ static PyObject *DrgnType_get_size(DrgnType *self) } if (!drgn_type_is_complete(self->type)) Py_RETURN_NONE; - return PyLong_FromUnsignedLongLong(drgn_type_size(self->type)); + return PyLong_FromUint64(drgn_type_size(self->type)); } static PyObject *DrgnType_get_length(DrgnType *self) @@ -129,7 +126,7 @@ static PyObject *DrgnType_get_length(DrgnType *self) drgn_type_kind_str(self->type)); } if (drgn_type_is_complete(self->type)) - return PyLong_FromUnsignedLongLong(drgn_type_length(self->type)); + return PyLong_FromUint64(drgn_type_length(self->type)); else Py_RETURN_NONE; } @@ -182,8 +179,7 @@ static TypeMember *TypeMember_wrap(PyObject *parent, struct drgn_type_member *member, uint64_t bit_offset) { - TypeMember *py_member = - (TypeMember *)TypeMember_type.tp_alloc(&TypeMember_type, 0); + _cleanup_pydecref_ TypeMember *py_member = call_tp_alloc(TypeMember); if (!py_member) return NULL; @@ -193,27 +189,19 @@ static TypeMember *TypeMember_wrap(PyObject *parent, if (member->name) { py_member->name = PyUnicode_FromString(member->name); if (!py_member->name) - goto err; + return NULL; } else { Py_INCREF(Py_None); py_member->name = Py_None; } - py_member->bit_offset = PyLong_FromUnsignedLongLong(bit_offset); + py_member->bit_offset = PyLong_FromUint64(bit_offset); if (!py_member->bit_offset) - goto err; - return py_member; - -err: - Py_DECREF(py_member); - return NULL; + return NULL; + return_ptr(py_member); } static PyObject *DrgnType_get_members(DrgnType *self) { - PyObject *members_obj; - struct drgn_type_member *members; - size_t num_members, i; - if (!drgn_type_has_members(self->type)) { return PyErr_Format(PyExc_AttributeError, "%s type does not have members", @@ -223,34 +211,26 @@ static PyObject *DrgnType_get_members(DrgnType *self) if (!drgn_type_is_complete(self->type)) Py_RETURN_NONE; - members = drgn_type_members(self->type); - num_members = drgn_type_num_members(self->type); - members_obj = PyTuple_New(num_members); + struct drgn_type_member *members = drgn_type_members(self->type); + size_t num_members = drgn_type_num_members(self->type); + + _cleanup_pydecref_ PyObject *members_obj = PyTuple_New(num_members); if (!members_obj) return NULL; - for (i = 0; i < num_members; i++) { + for (size_t i = 0; i < num_members; i++) { TypeMember *item = TypeMember_wrap((PyObject *)self, &members[i], members[i].bit_offset); if (!item) - goto err; + return NULL; PyTuple_SET_ITEM(members_obj, i, (PyObject *)item); } - return members_obj; - -err: - Py_DECREF(members_obj); - return NULL; + return_ptr(members_obj); } static PyObject *DrgnType_get_enumerators(DrgnType *self) { - PyObject *enumerators_obj; - const struct drgn_type_enumerator *enumerators; - bool is_signed; - size_t num_enumerators, i; - if (!drgn_type_has_enumerators(self->type)) { return PyErr_Format(PyExc_AttributeError, "%s type does not have enumerators", @@ -260,15 +240,16 @@ static PyObject *DrgnType_get_enumerators(DrgnType *self) if (!drgn_type_is_complete(self->type)) Py_RETURN_NONE; - enumerators = drgn_type_enumerators(self->type); - num_enumerators = drgn_type_num_enumerators(self->type); - is_signed = drgn_enum_type_is_signed(self->type); + const struct drgn_type_enumerator *enumerators = + drgn_type_enumerators(self->type); + size_t num_enumerators = drgn_type_num_enumerators(self->type); + bool is_signed = drgn_enum_type_is_signed(self->type); - enumerators_obj = PyTuple_New(num_enumerators); + _cleanup_pydecref_ PyObject *enumerators_obj = PyTuple_New(num_enumerators); if (!enumerators_obj) return NULL; - for (i = 0; i < num_enumerators; i++) { + for (size_t i = 0; i < num_enumerators; i++) { PyObject *item; if (is_signed) { @@ -280,42 +261,36 @@ static PyObject *DrgnType_get_enumerators(DrgnType *self) "sK", enumerators[i].name, (unsigned long long)enumerators[i].uvalue); } - if (!item) { - Py_DECREF(enumerators_obj); + if (!item) return NULL; - } PyTuple_SET_ITEM(enumerators_obj, i, item); } - return enumerators_obj; + return_ptr(enumerators_obj); } static PyObject *DrgnType_get_parameters(DrgnType *self) { - PyObject *parameters_obj; - struct drgn_type_parameter *parameters; - size_t num_parameters, i; - if (!drgn_type_has_parameters(self->type)) { return PyErr_Format(PyExc_AttributeError, "%s type does not have parameters", drgn_type_kind_str(self->type)); } - parameters = drgn_type_parameters(self->type); - num_parameters = drgn_type_num_parameters(self->type); - parameters_obj = PyTuple_New(num_parameters); + struct drgn_type_parameter *parameters = + drgn_type_parameters(self->type); + size_t num_parameters = drgn_type_num_parameters(self->type); + + _cleanup_pydecref_ PyObject *parameters_obj = + PyTuple_New(num_parameters); if (!parameters_obj) return NULL; - for (i = 0; i < num_parameters; i++) { + for (size_t i = 0; i < num_parameters; i++) { struct drgn_type_parameter *parameter = ¶meters[i]; - TypeParameter *item; - - item = (TypeParameter *)TypeParameter_type.tp_alloc(&TypeParameter_type, - 0); + TypeParameter *item = call_tp_alloc(TypeParameter); if (!item) - goto err; + return NULL; PyTuple_SET_ITEM(parameters_obj, i, (PyObject *)item); Py_INCREF(self); item->lazy_obj.obj = (PyObject *)self; @@ -323,17 +298,13 @@ static PyObject *DrgnType_get_parameters(DrgnType *self) if (parameter->name) { item->name = PyUnicode_FromString(parameter->name); if (!item->name) - goto err; + return NULL; } else { Py_INCREF(Py_None); item->name = Py_None; } } - return parameters_obj; - -err: - Py_DECREF(parameters_obj); - return NULL; + return_ptr(parameters_obj); } static PyObject *DrgnType_get_is_variadic(DrgnType *self) @@ -358,7 +329,9 @@ static PyObject *DrgnType_get_template_parameters(DrgnType *self) drgn_type_template_parameters(self->type); size_t num_template_parameters = drgn_type_num_template_parameters(self->type); - PyObject *template_parameters_obj = PyTuple_New(num_template_parameters); + + _cleanup_pydecref_ PyObject *template_parameters_obj = + PyTuple_New(num_template_parameters); if (!template_parameters_obj) return NULL; @@ -367,11 +340,9 @@ static PyObject *DrgnType_get_template_parameters(DrgnType *self) &template_parameters[i]; TypeTemplateParameter *item = - (TypeTemplateParameter *) - TypeTemplateParameter_type.tp_alloc(&TypeTemplateParameter_type, - 0); + call_tp_alloc(TypeTemplateParameter); if (!item) - goto err; + return NULL; PyTuple_SET_ITEM(template_parameters_obj, i, (PyObject *)item); Py_INCREF(self); item->lazy_obj.obj = (PyObject *)self; @@ -379,7 +350,7 @@ static PyObject *DrgnType_get_template_parameters(DrgnType *self) if (template_parameter->name) { item->name = PyUnicode_FromString(template_parameter->name); if (!item->name) - goto err; + return NULL; } else { Py_INCREF(Py_None); item->name = Py_None; @@ -387,11 +358,7 @@ static PyObject *DrgnType_get_template_parameters(DrgnType *self) item->is_default = PyBool_FromLong(template_parameter->is_default); } - return template_parameters_obj; - -err: - Py_DECREF(template_parameters_obj); - return NULL; + return_ptr(template_parameters_obj); } struct DrgnType_Attr { @@ -513,35 +480,27 @@ static int DrgnType_clear(DrgnType *self) static int append_field(PyObject *parts, bool *first, const char *format, ...) { - va_list ap; - PyObject *str; - int ret; - if (!*first && append_string(parts, ", ") == -1) return -1; *first = false; + va_list ap; va_start(ap, format); - str = PyUnicode_FromFormatV(format, ap); + _cleanup_pydecref_ PyObject *str = PyUnicode_FromFormatV(format, ap); va_end(ap); if (!str) return -1; - - ret = PyList_Append(parts, str); - Py_DECREF(str); - return ret; + return PyList_Append(parts, str); } #define append_member(parts, type_obj, first, member) ({ \ int _ret = 0; \ - PyObject *_obj; \ - \ if (drgn_type_has_##member((type_obj)->type)) { \ - _obj = DrgnType_getter((type_obj), &DrgnType_attr_##member); \ + _cleanup_pydecref_ PyObject *_obj = \ + DrgnType_getter((type_obj), &DrgnType_attr_##member); \ if (_obj) { \ _ret = append_field((parts), (first), #member"=%R", \ _obj); \ - Py_DECREF(_obj); \ } else { \ _ret = -1; \ } \ @@ -551,87 +510,74 @@ static int append_field(PyObject *parts, bool *first, const char *format, ...) static PyObject *DrgnType_repr(DrgnType *self) { - PyObject *parts = PyList_New(0); + _cleanup_pydecref_ PyObject *parts = PyList_New(0); if (!parts) return NULL; - PyObject *ret = NULL; bool first = true; if (append_format(parts, "prog.%s_type(", drgn_type_kind_str(self->type)) == -1) - goto out; + return NULL; if (append_member(parts, self, &first, name) == -1) - goto out; + return NULL; if (append_member(parts, self, &first, tag) == -1) - goto out; + return NULL; if (drgn_type_kind(self->type) != DRGN_TYPE_POINTER && append_member(parts, self, &first, size) == -1) - goto out; + return NULL; if (append_member(parts, self, &first, is_signed) == -1) - goto out; + return NULL; if (append_member(parts, self, &first, type) == -1) - goto out; + return NULL; if (drgn_type_kind(self->type) == DRGN_TYPE_POINTER && (!drgn_type_program(self->type)->has_platform || drgn_type_size(self->type) != drgn_platform_address_size(&drgn_type_program(self->type)->platform)) && append_member(parts, self, &first, size) == -1) - goto out; + return NULL; if (drgn_type_has_little_endian(self->type) && (!drgn_type_program(self->type)->has_platform || drgn_type_little_endian(self->type) != drgn_platform_is_little_endian(&drgn_type_program(self->type)->platform))) { - PyObject *obj = DrgnType_get_byteorder(self, NULL); - if (!obj) - goto out; - if (append_field(parts, &first, "byteorder=%R", obj) == -1) { - Py_DECREF(obj); - goto out; - } - Py_DECREF(obj); + _cleanup_pydecref_ PyObject *obj = + DrgnType_get_byteorder(self, NULL); + if (!obj + || append_field(parts, &first, "byteorder=%R", obj) == -1) + return NULL; } if (append_member(parts, self, &first, length) == -1) - goto out; + return NULL; if (append_member(parts, self, &first, members) == -1) - goto out; + return NULL; if (append_member(parts, self, &first, enumerators) == -1) - goto out; + return NULL; if (append_member(parts, self, &first, parameters) == -1) - goto out; + return NULL; if (append_member(parts, self, &first, is_variadic) == -1) - goto out; + return NULL; if (drgn_type_has_template_parameters(self->type) && drgn_type_num_template_parameters(self->type) > 0 && append_member(parts, self, &first, template_parameters) == -1) - goto out; + return NULL; if (self->qualifiers) { - PyObject *obj = DrgnType_getter(self, - &DrgnType_attr_qualifiers); - if (!obj) - goto out; - if (append_field(parts, &first, "qualifiers=%R", obj) == -1) { - Py_DECREF(obj); - goto out; - } - Py_DECREF(obj); + _cleanup_pydecref_ PyObject *obj = + DrgnType_getter(self, &DrgnType_attr_qualifiers); + if (!obj + || append_field(parts, &first, "qualifiers=%R", obj) == -1) + return NULL; } if (drgn_type_language(self->type) != drgn_program_language(drgn_type_program(self->type))) { - PyObject *obj = DrgnType_get_language(self, NULL); - if (append_field(parts, &first, "language=%R", obj) == -1) { - Py_DECREF(obj); - goto out; - } - Py_DECREF(obj); + _cleanup_pydecref_ PyObject *obj = DrgnType_get_language(self, NULL); + if (!obj + || append_field(parts, &first, "language=%R", obj) == -1) + return NULL; } if (append_string(parts, ")") == -1) - goto out; + return NULL; - ret = join_strings(parts); -out: - Py_DECREF(parts); - return ret; + return join_strings(parts); } static PyObject *DrgnType_str(DrgnType *self) @@ -874,16 +820,14 @@ PyTypeObject TypeEnumerator_type = { static DrgnObject *DrgnType_to_absent_DrgnObject(DrgnType *type) { - DrgnObject *obj = DrgnObject_alloc(DrgnType_prog(type)); + _cleanup_pydecref_ DrgnObject *obj = DrgnObject_alloc(DrgnType_prog(type)); if (!obj) return NULL; struct drgn_error *err = drgn_object_set_absent(&obj->obj, DrgnType_unwrap(type), 0); - if (err) { - Py_DECREF(obj); + if (err) return set_drgn_error(err); - } - return obj; + return_ptr(obj); } static const char *PyType_name(PyTypeObject *type) @@ -989,11 +933,11 @@ static int append_lazy_object_repr(PyObject *parts, LazyObject *self) set_drgn_error(err); return -1; } - PyObject *tmp = PyUnicode_FromString(type_name); + _cleanup_pydecref_ PyObject *tmp = PyUnicode_FromString(type_name); free(type_name); - int ret = append_format(parts, "prog.type(%R)", tmp); - Py_DECREF(tmp); - return ret; + if (!tmp) + return -1; + return append_format(parts, "prog.type(%R)", tmp); } else { return append_format(parts, "%R", object); } @@ -1055,7 +999,8 @@ static TypeMember *TypeMember_new(PyTypeObject *subtype, PyObject *args, if (LazyObject_arg(object, "TypeMember", true, &obj, &state)) return NULL; - TypeMember *member = (TypeMember *)subtype->tp_alloc(subtype, 0); + _cleanup_pydecref_ TypeMember *member = + (TypeMember *)subtype->tp_alloc(subtype, 0); if (!member) { Py_DECREF(obj); return NULL; @@ -1069,14 +1014,10 @@ static TypeMember *TypeMember_new(PyTypeObject *subtype, PyObject *args, } else { bit_offset = PyLong_FromLong(0); if (!bit_offset) - goto err; + return NULL; } member->bit_offset = bit_offset; - return member; - -err: - Py_DECREF(member); - return NULL; + return_ptr(member); } static void TypeMember_dealloc(TypeMember *self) @@ -1088,17 +1029,15 @@ static void TypeMember_dealloc(TypeMember *self) static PyObject *TypeMember_get_offset(TypeMember *self, void *arg) { - unsigned long long bit_offset; - - bit_offset = PyLong_AsUnsignedLongLong(self->bit_offset); - if (bit_offset == (unsigned long long)-1 && PyErr_Occurred()) + uint64_t bit_offset = PyLong_AsUint64(self->bit_offset); + if (bit_offset == (uint64_t)-1 && PyErr_Occurred()) return NULL; if (bit_offset % 8) { PyErr_SetString(PyExc_ValueError, "member is not byte-aligned"); return NULL; } - return PyLong_FromUnsignedLongLong(bit_offset / 8); + return PyLong_FromUint64(bit_offset / 8); } static PyObject *TypeMember_get_bit_field_size(TypeMember *self, void *arg) @@ -1107,29 +1046,26 @@ static PyObject *TypeMember_get_bit_field_size(TypeMember *self, void *arg) if (!object) return NULL; if (object->obj.is_bit_field) - return PyLong_FromUnsignedLongLong(object->obj.bit_size); + return PyLong_FromUint64(object->obj.bit_size); else Py_RETURN_NONE; } static PyObject *TypeMember_repr(TypeMember *self) { - PyObject *parts = PyList_New(0), *ret = NULL; + _cleanup_pydecref_ PyObject *parts = PyList_New(0); if (!parts) return NULL; if (append_format(parts, "TypeMember(") < 0 || append_lazy_object_repr(parts, (LazyObject *)self) < 0) - goto out; + return NULL; if (self->name != Py_None && append_format(parts, ", name=%R", self->name) < 0) - goto out; + return NULL; /* Include the bit offset even if it is the default of 0 for clarity. */ if (append_format(parts, ", bit_offset=%R)", self->bit_offset) < 0) - goto out; - ret = join_strings(parts); -out: - Py_DECREF(parts); - return ret; + return NULL; + return join_strings(parts); } static PyMemberDef TypeMember_members[] = { @@ -1207,21 +1143,18 @@ static void TypeParameter_dealloc(TypeParameter *self) static PyObject *TypeParameter_repr(TypeParameter *self) { - PyObject *parts = PyList_New(0), *ret = NULL; + _cleanup_pydecref_ PyObject *parts = PyList_New(0); if (!parts) return NULL; if (append_format(parts, "TypeParameter(") < 0 || append_lazy_object_repr(parts, (LazyObject *)self) < 0) - goto out; + return NULL; if (self->name != Py_None && append_format(parts, ", name=%R", self->name) < 0) - goto out; + return NULL; if (append_string(parts, ")") < 0) - goto out; - ret = join_strings(parts); -out: - Py_DECREF(parts); - return ret; + return NULL; + return join_strings(parts); } static PyMemberDef TypeParameter_members[] = { @@ -1299,24 +1232,21 @@ static void TypeTemplateParameter_dealloc(TypeTemplateParameter *self) static PyObject *TypeTemplateParameter_repr(TypeTemplateParameter *self) { - PyObject *parts = PyList_New(0), *ret = NULL; + _cleanup_pydecref_ PyObject *parts = PyList_New(0); if (!parts) return NULL; if (append_format(parts, "TypeTemplateParameter(") < 0 || append_lazy_object_repr(parts, (LazyObject *)self) < 0) - goto out; + return NULL; if (self->name != Py_None && append_format(parts, ", name=%R", self->name) < 0) - goto out; + return NULL; if (self->is_default == Py_True && append_string(parts, ", is_default=True") < 0) - goto out; + return NULL; if (append_string(parts, ")") < 0) - goto out; - ret = join_strings(parts); -out: - Py_DECREF(parts); - return ret; + return NULL; + return join_strings(parts); } static PyObject *TypeTemplateParameter_get_argument(TypeTemplateParameter *self, @@ -1452,18 +1382,17 @@ DrgnType *Program_int_type(Program *self, PyObject *args, PyObject *kwds) Program_hold_object(self, name_obj); qualified_type.qualifiers = qualifiers; - DrgnType *type_obj = (DrgnType *)DrgnType_wrap(qualified_type); + _cleanup_pydecref_ DrgnType *type_obj = + (DrgnType *)DrgnType_wrap(qualified_type); if (!type_obj) return NULL; if (drgn_type_name(qualified_type.type) == name && _PyDict_SetItemId(type_obj->attr_cache, &DrgnType_attr_name.id, - name_obj) == -1) { - Py_DECREF(type_obj); + name_obj) == -1) return NULL; - } - return type_obj; + return_ptr(type_obj); } DrgnType *Program_bool_type(Program *self, PyObject *args, PyObject *kwds) @@ -1508,18 +1437,17 @@ DrgnType *Program_bool_type(Program *self, PyObject *args, PyObject *kwds) Program_hold_object(self, name_obj); qualified_type.qualifiers = qualifiers; - DrgnType *type_obj = (DrgnType *)DrgnType_wrap(qualified_type); + _cleanup_pydecref_ DrgnType *type_obj = + (DrgnType *)DrgnType_wrap(qualified_type); if (!type_obj) return NULL; if (drgn_type_name(qualified_type.type) == name && _PyDict_SetItemId(type_obj->attr_cache, &DrgnType_attr_name.id, - name_obj) == -1) { - Py_DECREF(type_obj); + name_obj) == -1) return NULL; - } - return type_obj; + return_ptr(type_obj); } DrgnType *Program_float_type(Program *self, PyObject *args, PyObject *kwds) @@ -1565,18 +1493,17 @@ DrgnType *Program_float_type(Program *self, PyObject *args, PyObject *kwds) Program_hold_object(self, name_obj); qualified_type.qualifiers = qualifiers; - DrgnType *type_obj = (DrgnType *)DrgnType_wrap(qualified_type); + _cleanup_pydecref_ DrgnType *type_obj = + (DrgnType *)DrgnType_wrap(qualified_type); if (!type_obj) return NULL; if (drgn_type_name(qualified_type.type) == name && _PyDict_SetItemId(type_obj->attr_cache, &DrgnType_attr_name.id, - name_obj) == -1) { - Py_DECREF(type_obj); + name_obj) == -1) return NULL; - } - return type_obj; + return_ptr(type_obj); } static struct drgn_error *py_lazy_object_thunk_fn(struct drgn_object *res, @@ -1584,15 +1511,12 @@ static struct drgn_error *py_lazy_object_thunk_fn(struct drgn_object *res, { if (!res) return NULL; /* Nothing to free. */ - PyGILState_STATE gstate = PyGILState_Ensure(); + PyGILState_guard(); DrgnObject *obj = LazyObject_get_borrowed(arg); - struct drgn_error *err; if (obj) - err = drgn_object_copy(res, &obj->obj); + return drgn_object_copy(res, &obj->obj); else - err = drgn_error_from_python(); - PyGILState_Release(gstate); - return err; + return drgn_error_from_python(); } static int lazy_object_from_py(union drgn_lazy_object *lazy_obj, @@ -1640,9 +1564,8 @@ static int unpack_member(struct drgn_compound_type_builder *builder, return -1; } - unsigned long long bit_offset = - PyLong_AsUnsignedLongLong(member->bit_offset); - if (bit_offset == (unsigned long long)-1 && PyErr_Occurred()) + uint64_t bit_offset = PyLong_AsUint64(member->bit_offset); + if (bit_offset == (uint64_t)-1 && PyErr_Occurred()) return -1; union drgn_lazy_object object; @@ -1736,7 +1659,7 @@ static DrgnType *Program_compound_type(Program *self, PyObject *args, return NULL; } - PyObject *cached_members; + _cleanup_pydecref_ PyObject *cached_members = NULL; size_t num_members; if (members_obj == Py_None) { if (!size.is_none) { @@ -1745,7 +1668,6 @@ static DrgnType *Program_compound_type(Program *self, PyObject *args, drgn_type_kind_spelling[kind]); return NULL; } - cached_members = NULL; num_members = 0; } else { if (size.is_none) { @@ -1765,7 +1687,7 @@ static DrgnType *Program_compound_type(Program *self, PyObject *args, } bool can_cache_members = true; - PyObject *cached_template_parameters; + _cleanup_pydecref_ PyObject *cached_template_parameters; if (template_parameters_obj) { cached_template_parameters = PySequence_Tuple(template_parameters_obj); @@ -1773,7 +1695,7 @@ static DrgnType *Program_compound_type(Program *self, PyObject *args, cached_template_parameters = PyTuple_New(0); } if (!cached_template_parameters) - goto err_members; + return NULL; size_t num_template_parameters = PyTuple_GET_SIZE(cached_template_parameters); bool can_cache_template_parameters = true; @@ -1808,7 +1730,7 @@ static DrgnType *Program_compound_type(Program *self, PyObject *args, set_drgn_error(err); err_builder: drgn_compound_type_builder_deinit(&builder); - goto err_template_parameters; + return NULL; } if (tag_obj != Py_None && drgn_type_tag(qualified_type.type) == tag) @@ -1819,9 +1741,10 @@ static DrgnType *Program_compound_type(Program *self, PyObject *args, Program_hold_object(self, cached_template_parameters); qualified_type.qualifiers = qualifiers; - DrgnType *type_obj = (DrgnType *)DrgnType_wrap(qualified_type); + _cleanup_pydecref_ DrgnType *type_obj = + (DrgnType *)DrgnType_wrap(qualified_type); if (!type_obj) - goto err_template_parameters; + return NULL; if (_PyDict_SetItemId(type_obj->attr_cache, &DrgnType_attr_tag.id, tag_obj) == -1 || @@ -1833,19 +1756,9 @@ static DrgnType *Program_compound_type(Program *self, PyObject *args, _PyDict_SetItemId(type_obj->attr_cache, &DrgnType_attr_template_parameters.id, cached_template_parameters) == -1)) - goto err_type; - Py_XDECREF(cached_members); - Py_DECREF(cached_template_parameters); - - return type_obj; + return NULL; -err_type: - Py_DECREF(type_obj); -err_template_parameters: - Py_DECREF(cached_template_parameters); -err_members: - Py_XDECREF(cached_members); - return NULL; + return_ptr(type_obj); } DrgnType *Program_struct_type(Program *self, PyObject *args, PyObject *kwds) @@ -1885,14 +1798,13 @@ static int unpack_enumerator(struct drgn_enum_type_builder *builder, struct drgn_error *err; if (is_signed) { - long long svalue = PyLong_AsLongLong(enumerator->value); + int64_t svalue = PyLong_AsInt64(enumerator->value); if (svalue == -1 && PyErr_Occurred()) return -1; err = drgn_enum_type_builder_add_signed(builder, name, svalue); } else { - unsigned long long uvalue = - PyLong_AsUnsignedLongLong(enumerator->value); - if (uvalue == (unsigned long long)-1 && PyErr_Occurred()) + uint64_t uvalue = PyLong_AsUint64(enumerator->value); + if (uvalue == (uint64_t)-1 && PyErr_Occurred()) return -1; err = drgn_enum_type_builder_add_unsigned(builder, name, uvalue); @@ -1941,7 +1853,7 @@ DrgnType *Program_enum_type(Program *self, PyObject *args, PyObject *kwds) return NULL; } - PyObject *cached_enumerators; + _cleanup_pydecref_ PyObject *cached_enumerators = NULL; struct drgn_qualified_type qualified_type; struct drgn_error *err; if (enumerators_obj == Py_None) { @@ -1959,8 +1871,6 @@ DrgnType *Program_enum_type(Program *self, PyObject *args, PyObject *kwds) &qualified_type.type); if (err) return set_drgn_error(err); - - cached_enumerators = NULL; } else { if (compatible_type_obj == Py_None) { PyErr_SetString(PyExc_ValueError, @@ -1997,7 +1907,7 @@ DrgnType *Program_enum_type(Program *self, PyObject *args, PyObject *kwds) if (unpack_enumerator(&builder, PyTuple_GET_ITEM(cached_enumerators, i), is_signed) == -1) - goto err_enumerators; + return NULL; } if (!Program_hold_reserve(self, 1 + (tag_obj != Py_None))) @@ -2009,7 +1919,7 @@ DrgnType *Program_enum_type(Program *self, PyObject *args, PyObject *kwds) set_drgn_error(err); err_builder: drgn_enum_type_builder_deinit(&builder); - goto err_enumerators; + return NULL; } Program_hold_object(self, cached_enumerators); @@ -2019,9 +1929,10 @@ DrgnType *Program_enum_type(Program *self, PyObject *args, PyObject *kwds) Program_hold_object(self, tag_obj); qualified_type.qualifiers = qualifiers; - DrgnType *type_obj = (DrgnType *)DrgnType_wrap(qualified_type); + _cleanup_pydecref_ DrgnType *type_obj = + (DrgnType *)DrgnType_wrap(qualified_type); if (!type_obj) - goto err_enumerators; + return NULL; if (_PyDict_SetItemId(type_obj->attr_cache, &DrgnType_attr_tag.id, tag_obj) == -1 || @@ -2031,16 +1942,9 @@ DrgnType *Program_enum_type(Program *self, PyObject *args, PyObject *kwds) &DrgnType_attr_enumerators.id, cached_enumerators ? cached_enumerators : Py_None) == -1) - goto err_type; - Py_XDECREF(cached_enumerators); - - return type_obj; + return NULL; -err_type: - Py_DECREF(type_obj); -err_enumerators: - Py_XDECREF(cached_enumerators); - return NULL; + return_ptr(type_obj); } DrgnType *Program_typedef_type(Program *self, PyObject *args, PyObject *kwds) @@ -2079,19 +1983,18 @@ DrgnType *Program_typedef_type(Program *self, PyObject *args, PyObject *kwds) Program_hold_object(self, name_obj); qualified_type.qualifiers = qualifiers; - DrgnType *type_obj = (DrgnType *)DrgnType_wrap(qualified_type); + _cleanup_pydecref_ DrgnType *type_obj = + (DrgnType *)DrgnType_wrap(qualified_type); if (!type_obj) return NULL; if (_PyDict_SetItemId(type_obj->attr_cache, &DrgnType_attr_type.id, (PyObject *)aliased_type_obj) == -1 || _PyDict_SetItemId(type_obj->attr_cache, &DrgnType_attr_name.id, - name_obj) == -1) { - Py_DECREF(type_obj); + name_obj) == -1) return NULL; - } - return type_obj; + return_ptr(type_obj); } DrgnType *Program_pointer_type(Program *self, PyObject *args, PyObject *kwds) @@ -2135,17 +2038,16 @@ DrgnType *Program_pointer_type(Program *self, PyObject *args, PyObject *kwds) if (err) return set_drgn_error(err); qualified_type.qualifiers = qualifiers; - DrgnType *type_obj = (DrgnType *)DrgnType_wrap(qualified_type); + _cleanup_pydecref_ DrgnType *type_obj = + (DrgnType *)DrgnType_wrap(qualified_type); if (!type_obj) return NULL; if (_PyDict_SetItemId(type_obj->attr_cache, &DrgnType_attr_type.id, - (PyObject *)referenced_type_obj) == -1) { - Py_DECREF(type_obj); + (PyObject *)referenced_type_obj) == -1) return NULL; - } - return type_obj; + return_ptr(type_obj); } DrgnType *Program_array_type(Program *self, PyObject *args, PyObject *kwds) @@ -2181,17 +2083,16 @@ DrgnType *Program_array_type(Program *self, PyObject *args, PyObject *kwds) if (err) return set_drgn_error(err); qualified_type.qualifiers = qualifiers; - DrgnType *type_obj = (DrgnType *)DrgnType_wrap(qualified_type); + _cleanup_pydecref_ DrgnType *type_obj = + (DrgnType *)DrgnType_wrap(qualified_type); if (!type_obj) return NULL; if (_PyDict_SetItemId(type_obj->attr_cache, &DrgnType_attr_type.id, - (PyObject *)element_type_obj) == -1) { - Py_DECREF(type_obj); + (PyObject *)element_type_obj) == -1) return NULL; - } - return type_obj; + return_ptr(type_obj); } static int unpack_parameter(struct drgn_function_type_builder *builder, @@ -2256,13 +2157,14 @@ DrgnType *Program_function_type(Program *self, PyObject *args, PyObject *kwds) return NULL; } - PyObject *cached_parameters = PySequence_Tuple(parameters_obj); + _cleanup_pydecref_ PyObject *cached_parameters = + PySequence_Tuple(parameters_obj); if (!cached_parameters) return NULL; size_t num_parameters = PyTuple_GET_SIZE(cached_parameters); bool can_cache_parameters = true; - PyObject *cached_template_parameters; + _cleanup_pydecref_ PyObject *cached_template_parameters; if (template_parameters_obj) { cached_template_parameters = PySequence_Tuple(template_parameters_obj); @@ -2270,7 +2172,7 @@ DrgnType *Program_function_type(Program *self, PyObject *args, PyObject *kwds) cached_template_parameters = PyTuple_New(0); } if (!cached_template_parameters) - goto err_parameters; + return NULL; size_t num_template_parameters = PyTuple_GET_SIZE(cached_template_parameters); bool can_cache_template_parameters = true; @@ -2305,7 +2207,7 @@ DrgnType *Program_function_type(Program *self, PyObject *args, PyObject *kwds) set_drgn_error(err); err_builder: drgn_function_type_builder_deinit(&builder); - goto err_template_parameters; + return NULL; } if (num_parameters > 0) @@ -2314,9 +2216,10 @@ DrgnType *Program_function_type(Program *self, PyObject *args, PyObject *kwds) Program_hold_object(self, cached_template_parameters); qualified_type.qualifiers = qualifiers; - DrgnType *type_obj = (DrgnType *)DrgnType_wrap(qualified_type); + _cleanup_pydecref_ DrgnType *type_obj = + (DrgnType *)DrgnType_wrap(qualified_type); if (!type_obj) - goto err_template_parameters; + return NULL; if (_PyDict_SetItemId(type_obj->attr_cache, &DrgnType_attr_type.id, (PyObject *)return_type_obj) == -1 || @@ -2328,17 +2231,7 @@ DrgnType *Program_function_type(Program *self, PyObject *args, PyObject *kwds) _PyDict_SetItemId(type_obj->attr_cache, &DrgnType_attr_template_parameters.id, cached_template_parameters) == -1)) - goto err_type; - Py_DECREF(cached_parameters); - Py_DECREF(cached_template_parameters); - - return type_obj; - -err_type: - Py_DECREF(type_obj); -err_template_parameters: - Py_DECREF(cached_template_parameters); -err_parameters: - Py_DECREF(cached_parameters); - return NULL; + return NULL; + + return_ptr(type_obj); } diff --git a/libdrgn/python/util.c b/libdrgn/python/util.c index c2391036b..c01e86554 100644 --- a/libdrgn/python/util.c +++ b/libdrgn/python/util.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include @@ -7,30 +7,18 @@ int append_string(PyObject *parts, const char *s) { - PyObject *str; - int ret; - - str = PyUnicode_FromString(s); + _cleanup_pydecref_ PyObject *str = PyUnicode_FromString(s); if (!str) return -1; - - ret = PyList_Append(parts, str); - Py_DECREF(str); - return ret; + return PyList_Append(parts, str); } static int append_formatv(PyObject *parts, const char *format, va_list ap) { - PyObject *str; - int ret; - - str = PyUnicode_FromFormatV(format, ap); + _cleanup_pydecref_ PyObject *str = PyUnicode_FromFormatV(format, ap); if (!str) return -1; - - ret = PyList_Append(parts, str); - Py_DECREF(str); - return ret; + return PyList_Append(parts, str); } int append_format(PyObject *parts, const char *format, ...) @@ -46,12 +34,10 @@ int append_format(PyObject *parts, const char *format, ...) PyObject *join_strings(PyObject *parts) { - PyObject *sep = PyUnicode_New(0, 0); + _cleanup_pydecref_ PyObject *sep = PyUnicode_New(0, 0); if (!sep) return NULL; - PyObject *ret = PyUnicode_Join(sep, parts); - Py_DECREF(sep); - return ret; + return PyUnicode_Join(sep, parts); } PyObject *repr_pretty_from_str(PyObject *self, PyObject *args, PyObject *kwds) @@ -66,121 +52,61 @@ PyObject *repr_pretty_from_str(PyObject *self, PyObject *args, PyObject *kwds) if (cycle) return PyObject_CallMethod(p, "text", "s", "..."); - PyObject *str_obj = PyObject_Str(self); + _cleanup_pydecref_ PyObject *str_obj = PyObject_Str(self); if (!str_obj) return NULL; - PyObject *ret = PyObject_CallMethod(p, "text", "O", str_obj); - Py_DECREF(str_obj); - return ret; + return PyObject_CallMethod(p, "text", "O", str_obj); } int index_converter(PyObject *o, void *p) { struct index_arg *arg = p; - PyObject *index_obj; arg->is_none = o == Py_None; if (arg->allow_none && arg->is_none) return 1; - index_obj = PyNumber_Index(o); + _cleanup_pydecref_ PyObject *index_obj = PyNumber_Index(o); if (!index_obj) return 0; if (arg->is_signed) { arg->svalue = PyLong_AsLongLong(index_obj); - Py_DECREF(index_obj); return (arg->svalue != -1LL || !PyErr_Occurred()); } else { arg->uvalue = PyLong_AsUnsignedLongLong(index_obj); - Py_DECREF(index_obj); return (arg->uvalue != -1ULL || !PyErr_Occurred()); } } int path_converter(PyObject *o, void *p) { - struct path_arg *path = p; - int is_bytes, is_unicode; - PyObject *bytes = NULL; - Py_ssize_t length = 0; - char *tmp; - if (o == NULL) { path_cleanup(p); return 1; } - path->object = path->cleanup = NULL; - Py_INCREF(o); - + struct path_arg *path = p; if (path->allow_none && o == Py_None) { path->path = NULL; path->length = 0; - goto out; - } - - is_bytes = PyBytes_Check(o); - is_unicode = PyUnicode_Check(o); - - if (!is_bytes && !is_unicode) { - _Py_IDENTIFIER(__fspath__); - PyObject *func; - - func = _PyObject_LookupSpecial(o, &PyId___fspath__); - if (func == NULL) - goto err_format; - Py_DECREF(o); - o = PyObject_CallObject(func, NULL); - Py_DECREF(func); - if (o == NULL) - return 0; - is_bytes = PyBytes_Check(o); - is_unicode = PyUnicode_Check(o); - } - - if (is_unicode) { - if (!PyUnicode_FSConverter(o, &bytes)) - goto err; - } else if (is_bytes) { - bytes = o; - Py_INCREF(bytes); + path->bytes = NULL; } else { -err_format: - PyErr_Format(PyExc_TypeError, - "expected string, bytes, or os.PathLike, not %s", - Py_TYPE(o)->tp_name); - goto err; - } - - length = PyBytes_GET_SIZE(bytes); - tmp = PyBytes_AS_STRING(bytes); - if ((size_t)length != strlen(tmp)) { - PyErr_SetString(PyExc_TypeError, - "path has embedded nul character"); - goto err; + if (!PyUnicode_FSConverter(o, &path->bytes)) { + path->object = path->bytes = NULL; + return 0; + } + path->path = PyBytes_AS_STRING(path->bytes); + path->length = PyBytes_GET_SIZE(path->bytes); } - - path->path = tmp; - if (bytes == o) - Py_DECREF(bytes); - else - path->cleanup = bytes; - path->length = length; - -out: + Py_INCREF(o); path->object = o; return Py_CLEANUP_SUPPORTED; - -err: - Py_XDECREF(o); - Py_XDECREF(bytes); - return 0; } void path_cleanup(struct path_arg *path) { + Py_CLEAR(path->bytes); Py_CLEAR(path->object); - Py_CLEAR(path->cleanup); } int enum_converter(PyObject *o, void *p) @@ -199,12 +125,10 @@ int enum_converter(PyObject *o, void *p) return 0; } - o = PyObject_GetAttrString(o, "value"); - if (!o) + _cleanup_pydecref_ PyObject *value = PyObject_GetAttrString(o, "value"); + if (!value) return 0; - - arg->value = PyLong_AsUnsignedLong(o); - Py_DECREF(o); + arg->value = PyLong_AsUnsignedLong(value); if (arg->value == -1 && PyErr_Occurred()) return 0; return 1; diff --git a/libdrgn/register_state.c b/libdrgn/register_state.c index 52cf5eb05..a405b0885 100644 --- a/libdrgn/register_state.c +++ b/libdrgn/register_state.c @@ -1,11 +1,10 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include #include "debug_info.h" -#include "drgn.h" #include "register_state.h" #define drgn_register_state_known_bitset(regs) ({ \ @@ -13,11 +12,16 @@ &_state->buf[_state->regs_size]; \ }) +static inline uint32_t drgn_register_state_bitset_size(uint16_t num_regs) +{ + return ((uint32_t)num_regs + CHAR_BIT + 1) / CHAR_BIT; +} + struct drgn_register_state *drgn_register_state_create_impl(uint32_t regs_size, uint16_t num_regs, bool interrupted) { - uint32_t bitset_size = ((uint32_t)num_regs + CHAR_BIT + 1) / CHAR_BIT; + uint32_t bitset_size = drgn_register_state_bitset_size(num_regs); size_t size; struct drgn_register_state *regs; if (__builtin_add_overflow(regs_size, bitset_size, &size) || @@ -32,6 +36,21 @@ struct drgn_register_state *drgn_register_state_create_impl(uint32_t regs_size, return regs; } +struct drgn_register_state * +drgn_register_state_dup(const struct drgn_register_state *regs) +{ + size_t size; + struct drgn_register_state *ret; + if (__builtin_add_overflow(regs->regs_size, + drgn_register_state_bitset_size(regs->num_regs), + &size) || + __builtin_add_overflow(size, sizeof(*ret), &size) || + !(ret = malloc(size))) + return NULL; + memcpy(ret, regs, size); + return ret; +} + static bool drgn_register_state_is_known(const struct drgn_register_state *regs, uint32_t i) { @@ -93,14 +112,7 @@ void drgn_register_state_set_pc(struct drgn_program *prog, void **userdatap; dwfl_module_info(dwfl_module, &userdatap, NULL, NULL, NULL, NULL, NULL, NULL); - struct drgn_debug_info_module *module = *userdatap; - static const enum drgn_platform_flags check_flags = - (DRGN_PLATFORM_IS_64_BIT | - DRGN_PLATFORM_IS_LITTLE_ENDIAN); - if (module->platform.arch == prog->platform.arch && - (module->platform.flags & check_flags) == - (prog->platform.flags & check_flags)) - regs->module = module; + regs->module = *userdatap; } } } diff --git a/libdrgn/register_state.h b/libdrgn/register_state.h index 3b7b6fac9..8b1f5b435 100644 --- a/libdrgn/register_state.h +++ b/libdrgn/register_state.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -13,13 +13,11 @@ #define DRGN_REGISTER_STATE_H #include -#include -#include -#include #include #include #include "cfi.h" +#include "drgn.h" #include "platform.h" #include "program.h" #include "serialize.h" @@ -50,14 +48,12 @@ */ struct drgn_register_state { /** - * Cached @ref drgn_debug_info_module that contains the program counter. + * Cached @ref drgn_module that contains the program counter. * - * This is `NULL` if the program counter is not known, if the containing - * module could not be found, or if the containing module's platform - * does not match the program's platform (in which case we can't use it - * anyways). + * This is `NULL` if the program counter is not known or if the + * containing module could not be found. */ - struct drgn_debug_info_module *module; + struct drgn_module *module; /** Total size of registers allocated in @ref drgn_register_state::buf. */ uint32_t regs_size; /** Number of registers allocated in @ref drgn_register_state::buf. */ @@ -134,6 +130,14 @@ struct drgn_register_state *drgn_register_state_create_impl(uint32_t regs_size, DRGN_REGISTER_NUMBER(last_reg) + 1, \ interrupted) +/** + * Create a copy of a @ref drgn_register_state. + * + * @return New register state on success, @c NULL on failure to allocate memory. + */ +struct drgn_register_state * +drgn_register_state_dup(const struct drgn_register_state *regs); + /** Free a @ref drgn_register_state. */ static inline void drgn_register_state_destroy(struct drgn_register_state *regs) diff --git a/libdrgn/serialize.c b/libdrgn/serialize.c index b4228e427..53937c9d1 100644 --- a/libdrgn/serialize.c +++ b/libdrgn/serialize.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include diff --git a/libdrgn/serialize.h b/libdrgn/serialize.h index b28eab6d6..5c995dcbd 100644 --- a/libdrgn/serialize.h +++ b/libdrgn/serialize.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -40,16 +40,27 @@ static inline uint64_t truncate_unsigned(uint64_t uvalue, uint64_t bit_size) return uvalue << (64 - bit_size) >> (64 - bit_size); } +static inline int8_t truncate_signed8(int8_t svalue, int bit_size) +{ + return (int8_t)((uint8_t)svalue << (8 - bit_size)) >> (8 - bit_size); +} + +static inline uint8_t truncate_unsigned8(uint8_t uvalue, int bit_size) +{ + return (uint8_t)(uvalue << (8 - bit_size)) >> (8 - bit_size); +} + /** * Copy the @p src_size least-significant bytes from @p src to the @p dst_size * least-significant bytes of @p dst. * * If `src_size > dst_size`, the extra bytes are discarded. If `src_size < - * dst_size`, the extra bytes are zero-filled. + * dst_size`, the extra bytes are filled with @p fill. */ -static inline void copy_lsbytes(void *dst, size_t dst_size, - bool dst_little_endian, const void *src, - size_t src_size, bool src_little_endian) +static inline void copy_lsbytes_fill(void *dst, size_t dst_size, + bool dst_little_endian, const void *src, + size_t src_size, bool src_little_endian, + int fill) { char *d = dst; const char *s = src; @@ -61,9 +72,9 @@ static inline void copy_lsbytes(void *dst, size_t dst_size, for (size_t i = 0; i < size; i++) d[i] = s[src_size - 1 - i]; } - memset(d + size, 0, dst_size - size); + memset(d + size, fill, dst_size - size); } else { - memset(d, 0, dst_size - size); + memset(d, fill, dst_size - size); if (src_little_endian) { for (size_t i = dst_size - size; i < dst_size; i++) d[i] = s[dst_size - 1 - i]; @@ -73,6 +84,21 @@ static inline void copy_lsbytes(void *dst, size_t dst_size, } } +/** + * Copy the @p src_size least-significant bytes from @p src to the @p dst_size + * least-significant bytes of @p dst. + * + * If `src_size > dst_size`, the extra bytes are discarded. If `src_size < + * dst_size`, the extra bytes are zero-filled. + */ +static inline void copy_lsbytes(void *dst, size_t dst_size, + bool dst_little_endian, const void *src, + size_t src_size, bool src_little_endian) +{ + return copy_lsbytes_fill(dst, dst_size, dst_little_endian, src, + src_size, src_little_endian, 0); +} + /** * Return a bit mask with bits `[bit_offset, 7]` set. * diff --git a/libdrgn/splay_tree.c b/libdrgn/splay_tree.c index cd84f1a53..5a15e27d3 100644 --- a/libdrgn/splay_tree.c +++ b/libdrgn/splay_tree.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include "binary_search_tree.h" // IWYU pragma: associated diff --git a/libdrgn/stack_trace.c b/libdrgn/stack_trace.c index 972fb1969..1ef8710d8 100644 --- a/libdrgn/stack_trace.c +++ b/libdrgn/stack_trace.c @@ -1,8 +1,7 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include -#include #include #include #include @@ -10,9 +9,12 @@ #include #include "cfi.h" +#include "cleanup.h" #include "debug_info.h" #include "drgn.h" +#include "dwarf_constants.h" #include "dwarf_info.h" +#include "elf_file.h" #include "error.h" #include "helpers.h" #include "minmax.h" @@ -107,7 +109,7 @@ drgn_stack_trace_num_frames(struct drgn_stack_trace *trace) LIBDRGN_PUBLIC struct drgn_error * drgn_format_stack_trace(struct drgn_stack_trace *trace, char **ret) { - struct string_builder str = {}; + struct string_builder str = STRING_BUILDER_INIT; for (size_t frame = 0; frame < trace->num_frames; frame++) { if (!string_builder_appendf(&str, "#%-2zu ", frame)) goto enomem; @@ -160,7 +162,7 @@ drgn_format_stack_trace(struct drgn_stack_trace *trace, char **ret) !string_builder_appendc(&str, '\n')) goto enomem; } - if (!string_builder_finalize(&str, ret)) + if (!(*ret = string_builder_null_terminate(&str))) goto enomem; return NULL; @@ -172,7 +174,7 @@ drgn_format_stack_trace(struct drgn_stack_trace *trace, char **ret) LIBDRGN_PUBLIC struct drgn_error * drgn_format_stack_frame(struct drgn_stack_trace *trace, size_t frame, char **ret) { - struct string_builder str = {}; + struct string_builder str = STRING_BUILDER_INIT; struct drgn_register_state *regs = trace->frames[frame].regs; if (!string_builder_appendf(&str, "#%zu at ", frame)) goto enomem; @@ -219,7 +221,7 @@ drgn_format_stack_frame(struct drgn_stack_trace *trace, size_t frame, char **ret !string_builder_append(&str, " (inlined)")) goto enomem; - if (!string_builder_finalize(&str, ret)) + if (!(*ret = string_builder_null_terminate(&str))) goto enomem; return NULL; @@ -301,18 +303,13 @@ drgn_stack_frame_source(struct drgn_stack_trace *trace, size_t frame, return filename; } else if (trace->frames[frame].num_scopes > 0) { struct drgn_register_state *regs = trace->frames[frame].regs; - Dwfl_Module *dwfl_module = - regs->module ? regs->module->dwfl_module : NULL; - if (!dwfl_module) + if (!regs->module) return NULL; struct optional_uint64 pc = drgn_register_state_get_pc(regs); if (!pc.has_value) return NULL; - Dwarf_Addr bias; - if (!dwfl_module_getdwarf(dwfl_module, &bias)) - return NULL; - pc.value = pc.value - bias - !regs->interrupted; + pc.value -= !regs->interrupted + regs->module->debug_file_bias; Dwarf_Die *scopes = trace->frames[frame].scopes; size_t num_scopes = trace->frames[frame].num_scopes; @@ -350,6 +347,22 @@ LIBDRGN_PUBLIC bool drgn_stack_frame_pc(struct drgn_stack_trace *trace, return pc.has_value; } +LIBDRGN_PUBLIC bool drgn_stack_frame_sp(struct drgn_stack_trace *trace, + size_t frame, uint64_t *ret) +{ + struct drgn_program *prog = trace->prog; + drgn_register_number regno = prog->platform.arch->stack_pointer_regno; + struct drgn_register_state *regs = trace->frames[frame].regs; + if (!drgn_register_state_has_register(regs, regno)) + return false; + const struct drgn_register_layout *layout = + &prog->platform.arch->register_layout[regno]; + copy_lsbytes(ret, sizeof(*ret), HOST_LITTLE_ENDIAN, + ®s->buf[layout->offset], layout->size, + drgn_platform_is_little_endian(&prog->platform)); + return true; +} + LIBDRGN_PUBLIC struct drgn_error * drgn_stack_frame_symbol(struct drgn_stack_trace *trace, size_t frame, struct drgn_symbol **ret) @@ -365,18 +378,37 @@ drgn_stack_frame_symbol(struct drgn_stack_trace *trace, size_t frame, regs->module ? regs->module->dwfl_module : NULL; if (!dwfl_module) return drgn_error_symbol_not_found(pc.value); - struct drgn_symbol *sym = malloc(sizeof(*sym)); + _cleanup_free_ struct drgn_symbol *sym = malloc(sizeof(*sym)); if (!sym) return &drgn_enomem; if (!drgn_program_find_symbol_by_address_internal(trace->prog, pc.value, - dwfl_module, sym)) { - free(sym); + dwfl_module, sym)) return drgn_error_symbol_not_found(pc.value); - } - *ret = sym; + *ret = no_cleanup_ptr(sym); return NULL; } +LIBDRGN_PUBLIC struct drgn_error * +drgn_stack_frame_locals(struct drgn_stack_trace *trace, size_t frame_i, + const char ***names_ret, size_t *count_ret) +{ + struct drgn_stack_frame *frame = &trace->frames[frame_i]; + if (frame->function_scope >= frame->num_scopes) { + *names_ret = NULL; + *count_ret = 0; + return NULL; + } + return drgn_dwarf_scopes_names(frame->scopes + frame->function_scope, + frame->num_scopes - frame->function_scope, + names_ret, count_ret); +} + +LIBDRGN_PUBLIC +void drgn_stack_frame_locals_destroy(const char **names, size_t count) +{ + free(names); +} + LIBDRGN_PUBLIC struct drgn_error * drgn_stack_frame_find_object(struct drgn_stack_trace *trace, size_t frame_i, const char *name, struct drgn_object *ret) @@ -450,12 +482,23 @@ not_found:; } } + const struct drgn_register_state *regs = frame->regs; + struct drgn_elf_file *file = + drgn_module_find_dwarf_file(regs->module, + dwarf_cu_getdwarf(die.cu)); + if (!file) { + return drgn_error_create(DRGN_ERROR_OTHER, + "couldn't find file containing DIE"); + } + // It doesn't make sense to use the registers if the file has a + // different platform than the program. + if (!drgn_platforms_equal(&file->platform, &trace->prog->platform)) + regs = NULL; Dwarf_Die function_die = frame->scopes[frame->function_scope]; - return drgn_object_from_dwarf(trace->prog->dbinfo, frame->regs->module, - &die, + return drgn_object_from_dwarf(trace->prog->dbinfo, file, &die, dwarf_tag(&die) == DW_TAG_enumerator ? &type_die : NULL, - &function_die, frame->regs, ret); + &function_die, regs, ret); } LIBDRGN_PUBLIC bool drgn_stack_frame_register(struct drgn_stack_trace *trace, @@ -611,22 +654,12 @@ drgn_get_initial_registers(struct drgn_program *prog, uint32_t tid, * the idle task by CPU. Rather than making PID 0 a * special case, we handle all tasks this way. */ - union drgn_value value; - err = drgn_object_member_dereference(&tmp, &obj, "cpu"); - if (!err) { - err = drgn_object_read_integer(&tmp, &value); - if (err) - goto out; - } else if (err->code == DRGN_ERROR_LOOKUP) { - /* !SMP. Must be CPU 0. */ - drgn_error_destroy(err); - value.uvalue = 0; - } else { + uint64_t cpu; + err = linux_helper_task_cpu(&obj, &cpu); + if (err) goto out; - } uint32_t prstatus_tid; - err = drgn_program_find_prstatus_by_cpu(prog, - value.uvalue, + err = drgn_program_find_prstatus_by_cpu(prog, cpu, prstatus, &prstatus_tid); if (err) @@ -653,6 +686,7 @@ drgn_get_initial_registers(struct drgn_program *prog, uint32_t tid, err = drgn_object_member_dereference(&tmp, &obj, "pid"); if (err) goto out; + union drgn_value value; err = drgn_object_read_integer(&tmp, &value); if (err) goto out; @@ -747,8 +781,8 @@ drgn_stack_trace_add_frames(struct drgn_stack_trace **trace, uint64_t bias; Dwarf_Die *scopes; size_t num_scopes; - err = drgn_debug_info_module_find_dwarf_scopes(regs->module, pc, &bias, - &scopes, &num_scopes); + err = drgn_module_find_dwarf_scopes(regs->module, pc, &bias, &scopes, + &num_scopes); if (err) goto out; pc -= bias; @@ -871,7 +905,7 @@ drgn_stack_trace_add_frames(struct drgn_stack_trace **trace, } static struct drgn_error * -drgn_unwind_one_register(struct drgn_program *prog, +drgn_unwind_one_register(struct drgn_program *prog, struct drgn_elf_file *file, const struct drgn_cfi_rule *rule, const struct drgn_register_state *regs, void *buf, size_t size) @@ -930,8 +964,8 @@ drgn_unwind_one_register(struct drgn_program *prog, } case DRGN_CFI_RULE_AT_DWARF_EXPRESSION: case DRGN_CFI_RULE_DWARF_EXPRESSION: - err = drgn_eval_cfi_dwarf_expression(prog, rule, regs, buf, - size); + err = drgn_eval_cfi_dwarf_expression(prog, file, rule, regs, + buf, size); break; case DRGN_CFI_RULE_CONSTANT: copy_lsbytes(buf, size, little_endian, &rule->constant, @@ -950,6 +984,7 @@ drgn_unwind_one_register(struct drgn_program *prog, } static struct drgn_error *drgn_unwind_cfa(struct drgn_program *prog, + struct drgn_elf_file *file, const struct drgn_cfi_row *row, struct drgn_register_state *regs) { @@ -958,7 +993,8 @@ static struct drgn_error *drgn_unwind_cfa(struct drgn_program *prog, drgn_cfi_row_get_cfa(row, &rule); uint8_t address_size = drgn_platform_address_size(&prog->platform); char buf[8]; - err = drgn_unwind_one_register(prog, &rule, regs, buf, address_size); + err = drgn_unwind_one_register(prog, file, &rule, regs, buf, + address_size); if (!err) { uint64_t cfa; copy_lsbytes(&cfa, sizeof(cfa), HOST_LITTLE_ENDIAN, buf, @@ -981,17 +1017,17 @@ drgn_unwind_with_cfi(struct drgn_program *prog, struct drgn_cfi_row **row, if (!regs->module) return &drgn_not_found; + struct drgn_elf_file *file; bool interrupted; drgn_register_number ret_addr_regno; /* If we found the module, then we must have the PC. */ - err = drgn_debug_info_module_find_cfi(prog, regs->module, - regs->_pc - !regs->interrupted, - row, &interrupted, - &ret_addr_regno); + err = drgn_module_find_cfi(prog, regs->module, + regs->_pc - !regs->interrupted, &file, row, + &interrupted, &ret_addr_regno); if (err) return err; - err = drgn_unwind_cfa(prog, *row, regs); + err = drgn_unwind_cfa(prog, file, *row, regs); if (err) return err; @@ -1012,7 +1048,7 @@ drgn_unwind_with_cfi(struct drgn_program *prog, struct drgn_cfi_row **row, struct drgn_cfi_rule rule; drgn_cfi_row_get_register(*row, regno, &rule); layout = &prog->platform.arch->register_layout[regno]; - err = drgn_unwind_one_register(prog, &rule, regs, + err = drgn_unwind_one_register(prog, file, &rule, regs, &unwound->buf[layout->offset], layout->size); if (!err) { @@ -1028,12 +1064,9 @@ drgn_unwind_with_cfi(struct drgn_program *prog, struct drgn_cfi_row **row, drgn_register_state_destroy(unwound); return &drgn_stop; } + if (prog->platform.arch->demangle_cfi_registers) + prog->platform.arch->demangle_cfi_registers(prog, unwound); if (drgn_register_state_has_register(unwound, ret_addr_regno)) { - if (prog->platform.arch->demangle_return_address) { - prog->platform.arch->demangle_return_address(prog, - unwound, - ret_addr_regno); - } layout = &prog->platform.arch->register_layout[ret_addr_regno]; drgn_register_state_set_pc_from_register_impl(prog, unwound, ret_addr_regno, diff --git a/libdrgn/stack_trace.h b/libdrgn/stack_trace.h index 004e556c2..9c4511360 100644 --- a/libdrgn/stack_trace.h +++ b/libdrgn/stack_trace.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file diff --git a/libdrgn/string_builder.c b/libdrgn/string_builder.c index 5003a290b..703539743 100644 --- a/libdrgn/string_builder.c +++ b/libdrgn/string_builder.c @@ -1,6 +1,7 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later +#include #include #include @@ -8,24 +9,19 @@ #include "string_builder.h" #include "util.h" -bool string_builder_finalize(struct string_builder *sb, char **ret) +char *string_builder_null_terminate(struct string_builder *sb) { - if (!string_builder_reserve(sb, sb->len + 1)) - return false; + if (!string_builder_reserve_for_append(sb, 1)) + return NULL; sb->str[sb->len] = '\0'; - *ret = sb->str; - return true; + return sb->str; } bool string_builder_reserve(struct string_builder *sb, size_t capacity) { - char *tmp; - if (capacity <= sb->capacity) return true; - - capacity = next_power_of_two(capacity); - tmp = realloc(sb->str, capacity); + char *tmp = realloc(sb->str, capacity); if (!tmp) return false; sb->str = tmp; @@ -33,9 +29,21 @@ bool string_builder_reserve(struct string_builder *sb, size_t capacity) return true; } +bool string_builder_reserve_for_append(struct string_builder *sb, size_t n) +{ + if (n == 0) + return true; + size_t capacity; + if (__builtin_add_overflow(sb->len, n, &capacity)) + return false; + if (capacity < (SIZE_MAX / 2) + 1) + capacity = next_power_of_two(capacity); + return string_builder_reserve(sb, capacity); +} + bool string_builder_appendc(struct string_builder *sb, char c) { - if (!string_builder_reserve(sb, sb->len + 1)) + if (!string_builder_reserve_for_append(sb, 1)) return false; sb->str[sb->len++] = c; return true; @@ -44,7 +52,7 @@ bool string_builder_appendc(struct string_builder *sb, char c) bool string_builder_appendn(struct string_builder *sb, const char *str, size_t len) { - if (!string_builder_reserve(sb, sb->len + len)) + if (!string_builder_reserve_for_append(sb, len)) return false; memcpy(&sb->str[sb->len], str, len); sb->len += len; @@ -73,7 +81,7 @@ bool string_builder_vappendf(struct string_builder *sb, const char *format, * vsnprintf() always null-terminates the string, so we have to allocate * an extra character. */ - if (!string_builder_reserve(sb, sb->len + len + 1)) + if (!string_builder_reserve_for_append(sb, len + 1)) return false; goto again; } diff --git a/libdrgn/string_builder.h b/libdrgn/string_builder.h index f4ca10a25..8496ed242 100644 --- a/libdrgn/string_builder.h +++ b/libdrgn/string_builder.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -42,47 +42,55 @@ struct string_builder { * Current string buffer. * * This may be reallocated when appending. It must be freed with @c - * free() when it will no longer be used. It should be initialized to @c - * NULL. + * free() when it will no longer be used. */ char *str; - /** - * Length of @c str. - * - * It should be initialized to zero. - */ + /** Length of @c str. */ size_t len; - /** - * Allocated size of @c str. - * - * It should be initialized to zero. - */ + /** Allocated size of @c str. */ size_t capacity; }; +/** String builder initializer. */ +#define STRING_BUILDER_INIT { 0 } + /** * Null-terminate and return a string from a @ref string_builder. * - * On success, the string builder must be reinitialized before being reused. + * This appends a null character without incrementing @ref string_builder::len. * - * @param[out] ret Returned string. - * @return @c true on success, @c false on error (if we couldn't allocate - * memory). + * @return @ref string_builder::str on success, @c NULL on error (if we couldn't + * allocate memory). */ -bool string_builder_finalize(struct string_builder *sb, char **ret); +char *string_builder_null_terminate(struct string_builder *sb); /** - * Resize the buffer of a @ref string_builder. + * Resize the buffer of a @ref string_builder to a given capacity. * * On success, the allocated size of the string buffer is at least @p capacity. * * @param[in] sb String builder. - * @param[in] capacity New minimum size of the string buffer. + * @param[in] capacity New minimum allocated size of the string buffer. * @return @c true on success, @c false on error (if we couldn't allocate * memory). */ bool string_builder_reserve(struct string_builder *sb, size_t capacity); +/** + * Resize the buffer of a @ref string_builder to accomodate appending + * characters. + * + * On success, the allocated size of the string buffer is at least + * `sb->len + n`. This will also allocate extra space so that appends have + * amortized constant time complexity. + * + * @param[in] sb String builder. + * @param[in] n Minimum number of additional characters to reserve. + * @return @c true on success, @c false on error (if we couldn't allocate + * memory). + */ +bool string_builder_reserve_for_append(struct string_builder *sb, size_t n); + /** * Append a character to a @ref string_builder. * diff --git a/libdrgn/symbol.c b/libdrgn/symbol.c index 5f40b06db..89c92532b 100644 --- a/libdrgn/symbol.c +++ b/libdrgn/symbol.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include diff --git a/libdrgn/symbol.h b/libdrgn/symbol.h index 4fb765640..e136e86ee 100644 --- a/libdrgn/symbol.h +++ b/libdrgn/symbol.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #ifndef DRGN_SYMBOL_H #define DRGN_SYMBOL_H diff --git a/libdrgn/type.c b/libdrgn/type.c index d0b266c5a..37567f70c 100644 --- a/libdrgn/type.c +++ b/libdrgn/type.c @@ -1,11 +1,12 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include #include #include "array.h" +#include "cleanup.h" #include "error.h" #include "hash_table.h" #include "language.h" @@ -171,9 +172,9 @@ static bool drgn_member_key_eq(const struct drgn_member_key *a, } DEFINE_HASH_MAP_FUNCTIONS(drgn_member_map, drgn_member_key_hash_pair, - drgn_member_key_eq) + drgn_member_key_eq); -DEFINE_HASH_SET_FUNCTIONS(drgn_type_set, ptr_key_hash_pair, scalar_key_eq) +DEFINE_HASH_SET_FUNCTIONS(drgn_type_set, ptr_key_hash_pair, scalar_key_eq); LIBDRGN_PUBLIC struct drgn_error * drgn_member_object(struct drgn_type_member *member, @@ -330,9 +331,9 @@ static bool drgn_type_dedupe_eq(struct drgn_type * const *entry_a, * enumerators, so the hash and comparison functions ignore those. */ DEFINE_HASH_SET_FUNCTIONS(drgn_dedupe_type_set, drgn_type_dedupe_hash_pair, - drgn_type_dedupe_eq) + drgn_type_dedupe_eq); -DEFINE_VECTOR_FUNCTIONS(drgn_typep_vector) +DEFINE_VECTOR_FUNCTIONS(drgn_typep_vector); static struct drgn_error *find_or_create_type(struct drgn_type *key, struct drgn_type **ret) @@ -347,17 +348,15 @@ static struct drgn_error *find_or_create_type(struct drgn_type *key, return NULL; } - struct drgn_type *type = malloc(sizeof(*type)); + _cleanup_free_ struct drgn_type *type = malloc(sizeof(*type)); if (!type) return &drgn_enomem; *type = *key; if (drgn_dedupe_type_set_insert_searched(&prog->dedupe_types, &type, hp, - NULL) < 0) { - free(type); + NULL) < 0) return &drgn_enomem; - } - *ret = type; + *ret = no_cleanup_ptr(type); return NULL; } @@ -488,7 +487,7 @@ struct drgn_error *drgn_float_type_create(struct drgn_program *prog, return find_or_create_type(&key, ret); } -DEFINE_VECTOR_FUNCTIONS(drgn_type_template_parameter_vector) +DEFINE_VECTOR_FUNCTIONS(drgn_type_template_parameter_vector); static void drgn_template_parameters_builder_init(struct drgn_template_parameters_builder *builder, @@ -501,8 +500,9 @@ drgn_template_parameters_builder_init(struct drgn_template_parameters_builder *b static void drgn_template_parameters_builder_deinit(struct drgn_template_parameters_builder *builder) { - for (size_t i = 0; i < builder->parameters.size; i++) - drgn_lazy_object_deinit(&builder->parameters.data[i].argument); + vector_for_each(drgn_type_template_parameter_vector, parameter, + &builder->parameters) + drgn_lazy_object_deinit(¶meter->argument); drgn_type_template_parameter_vector_deinit(&builder->parameters); } @@ -525,7 +525,7 @@ drgn_template_parameters_builder_add(struct drgn_template_parameters_builder *bu return NULL; } -DEFINE_VECTOR_FUNCTIONS(drgn_type_member_vector) +DEFINE_VECTOR_FUNCTIONS(drgn_type_member_vector); void drgn_compound_type_builder_init(struct drgn_compound_type_builder *builder, struct drgn_program *prog, @@ -542,8 +542,8 @@ void drgn_compound_type_builder_init(struct drgn_compound_type_builder *builder, void drgn_compound_type_builder_deinit(struct drgn_compound_type_builder *builder) { - for (size_t i = 0; i < builder->members.size; i++) - drgn_lazy_object_deinit(&builder->members.data[i].object); + vector_for_each(drgn_type_member_vector, member, &builder->members) + drgn_lazy_object_deinit(&member->object); drgn_type_member_vector_deinit(&builder->members); drgn_template_parameters_builder_deinit(&builder->template_builder); } @@ -578,7 +578,7 @@ drgn_compound_type_create(struct drgn_compound_type_builder *builder, struct drgn_program *prog = builder->template_builder.prog; if (!is_complete) { - if (builder->members.size) { + if (!drgn_type_member_vector_empty(&builder->members)) { return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, "incomplete type must not have members"); } @@ -588,8 +588,8 @@ drgn_compound_type_create(struct drgn_compound_type_builder *builder, } } - if (!builder->members.size && - !builder->template_builder.parameters.size) { + if (drgn_type_member_vector_empty(&builder->members) + && drgn_type_template_parameter_vector_empty(&builder->template_builder.parameters)) { struct drgn_type key = { { .kind = builder->kind, @@ -608,13 +608,9 @@ drgn_compound_type_create(struct drgn_compound_type_builder *builder, return err; } - struct drgn_type *type = malloc(sizeof(*type)); - if (!type) - return &drgn_enomem; - if (!drgn_typep_vector_append(&prog->created_types, &type)) { - free(type); + _cleanup_free_ struct drgn_type *type = malloc(sizeof(*type)); + if (!type || !drgn_typep_vector_append(&prog->created_types, &type)) return &drgn_enomem; - } drgn_type_member_vector_shrink_to_fit(&builder->members); drgn_type_template_parameter_vector_shrink_to_fit(&builder->template_builder.parameters); @@ -624,19 +620,19 @@ drgn_compound_type_create(struct drgn_compound_type_builder *builder, type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE; type->_private.tag = tag; type->_private.size = size; - type->_private.members = builder->members.data; - type->_private.num_members = builder->members.size; - type->_private.template_parameters = - builder->template_builder.parameters.data; - type->_private.num_template_parameters = - builder->template_builder.parameters.size; + drgn_type_member_vector_steal(&builder->members, + &type->_private.members, + &type->_private.num_members); + drgn_type_template_parameter_vector_steal(&builder->template_builder.parameters, + &type->_private.template_parameters, + &type->_private.num_template_parameters); type->_private.program = prog; type->_private.language = lang ? lang : drgn_program_language(prog); - *ret = type; + *ret = no_cleanup_ptr(type); return NULL; } -DEFINE_VECTOR_FUNCTIONS(drgn_type_enumerator_vector) +DEFINE_VECTOR_FUNCTIONS(drgn_type_enumerator_vector); void drgn_enum_type_builder_init(struct drgn_enum_type_builder *builder, struct drgn_program *prog) @@ -693,7 +689,7 @@ struct drgn_error *drgn_enum_type_create(struct drgn_enum_type_builder *builder, "compatible type of enum type must be integer type"); } - if (!builder->enumerators.size) { + if (drgn_type_enumerator_vector_empty(&builder->enumerators)) { struct drgn_type key = { { .kind = DRGN_TYPE_ENUM, @@ -712,13 +708,10 @@ struct drgn_error *drgn_enum_type_create(struct drgn_enum_type_builder *builder, return err; } - struct drgn_type *type = malloc(sizeof(*type)); - if (!type) - return &drgn_enomem; - if (!drgn_typep_vector_append(&builder->prog->created_types, &type)) { - free(type); + _cleanup_free_ struct drgn_type *type = malloc(sizeof(*type)); + if (!type || + !drgn_typep_vector_append(&builder->prog->created_types, &type)) return &drgn_enomem; - } drgn_type_enumerator_vector_shrink_to_fit(&builder->enumerators); @@ -728,12 +721,13 @@ struct drgn_error *drgn_enum_type_create(struct drgn_enum_type_builder *builder, type->_private.tag = tag; type->_private.type = compatible_type; type->_private.qualifiers = 0; - type->_private.enumerators = builder->enumerators.data; - type->_private.num_enumerators = builder->enumerators.size; + drgn_type_enumerator_vector_steal(&builder->enumerators, + &type->_private.enumerators, + &type->_private.num_enumerators); type->_private.program = builder->prog; type->_private.language = lang ? lang : drgn_program_language(builder->prog); - *ret = type; + *ret = no_cleanup_ptr(type); return NULL; } @@ -873,7 +867,7 @@ drgn_incomplete_array_type_create(struct drgn_program *prog, return find_or_create_type(&key, ret); } -DEFINE_VECTOR_FUNCTIONS(drgn_type_parameter_vector) +DEFINE_VECTOR_FUNCTIONS(drgn_type_parameter_vector); void drgn_function_type_builder_init(struct drgn_function_type_builder *builder, struct drgn_program *prog) @@ -885,8 +879,9 @@ void drgn_function_type_builder_init(struct drgn_function_type_builder *builder, void drgn_function_type_builder_deinit(struct drgn_function_type_builder *builder) { - for (size_t i = 0; i < builder->parameters.size; i++) - drgn_lazy_object_deinit(&builder->parameters.data[i].default_argument); + vector_for_each(drgn_type_parameter_vector, parameter, + &builder->parameters) + drgn_lazy_object_deinit(¶meter->default_argument); drgn_type_parameter_vector_deinit(&builder->parameters); drgn_template_parameters_builder_deinit(&builder->template_builder); } @@ -924,8 +919,8 @@ drgn_function_type_create(struct drgn_function_type_builder *builder, "type is from different program"); } - if (!builder->parameters.size && - !builder->template_builder.parameters.size) { + if (drgn_type_parameter_vector_empty(&builder->parameters) + && drgn_type_template_parameter_vector_empty(&builder->template_builder.parameters)) { struct drgn_type key = { { .kind = DRGN_TYPE_FUNCTION, @@ -945,13 +940,9 @@ drgn_function_type_create(struct drgn_function_type_builder *builder, return err; } - struct drgn_type *type = malloc(sizeof(*type)); - if (!type) - return &drgn_enomem; - if (!drgn_typep_vector_append(&prog->created_types, &type)) { - free(type); + _cleanup_free_ struct drgn_type *type = malloc(sizeof(*type)); + if (!type ||!drgn_typep_vector_append(&prog->created_types, &type)) return &drgn_enomem; - } drgn_type_parameter_vector_shrink_to_fit(&builder->parameters); drgn_type_template_parameter_vector_shrink_to_fit(&builder->template_builder.parameters); @@ -961,16 +952,16 @@ drgn_function_type_create(struct drgn_function_type_builder *builder, type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE; type->_private.type = return_type.type; type->_private.qualifiers = return_type.qualifiers; - type->_private.parameters = builder->parameters.data; - type->_private.num_parameters = builder->parameters.size; + drgn_type_parameter_vector_steal(&builder->parameters, + &type->_private.parameters, + &type->_private.num_parameters); type->_private.is_variadic = is_variadic; - type->_private.template_parameters = - builder->template_builder.parameters.data; - type->_private.num_template_parameters = - builder->template_builder.parameters.size; + drgn_type_template_parameter_vector_steal(&builder->template_builder.parameters, + &type->_private.template_parameters, + &type->_private.num_template_parameters); type->_private.program = prog; type->_private.language = lang ? lang : drgn_program_language(prog); - *ret = type; + *ret = no_cleanup_ptr(type); return NULL; } @@ -1043,12 +1034,12 @@ drgn_type_with_byte_order_impl(struct drgn_type **type, size_t num_enumerators = drgn_type_num_enumerators(*type); if (num_enumerators) { - if (!drgn_type_enumerator_vector_reserve(&builder.enumerators, - num_enumerators)) { + if (!drgn_type_enumerator_vector_resize(&builder.enumerators, + num_enumerators)) { drgn_enum_type_builder_deinit(&builder); return &drgn_enomem; } - memcpy(&builder.enumerators.data, + memcpy(drgn_type_enumerator_vector_begin(&builder.enumerators), drgn_type_enumerators(*type), num_enumerators * sizeof(struct drgn_type_enumerator)); } @@ -1212,37 +1203,6 @@ struct drgn_error *drgn_type_bit_size(struct drgn_type *type, uint64_t *ret) return NULL; } -enum drgn_object_encoding drgn_type_object_encoding(struct drgn_type *type) -{ - SWITCH_ENUM(drgn_type_kind(type), - case DRGN_TYPE_INT: - return (drgn_type_is_signed(type) ? - DRGN_OBJECT_ENCODING_SIGNED : - DRGN_OBJECT_ENCODING_UNSIGNED); - case DRGN_TYPE_BOOL: - case DRGN_TYPE_POINTER: - return DRGN_OBJECT_ENCODING_UNSIGNED; - case DRGN_TYPE_FLOAT: - return DRGN_OBJECT_ENCODING_FLOAT; - case DRGN_TYPE_STRUCT: - case DRGN_TYPE_UNION: - case DRGN_TYPE_CLASS: - case DRGN_TYPE_ARRAY: - return (drgn_type_is_complete(type) ? - DRGN_OBJECT_ENCODING_BUFFER : - DRGN_OBJECT_ENCODING_INCOMPLETE_BUFFER); - case DRGN_TYPE_ENUM: - if (!drgn_type_is_complete(type)) - return DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER; - /* fallthrough */ - case DRGN_TYPE_TYPEDEF: - return drgn_type_object_encoding(drgn_type_type(type).type); - case DRGN_TYPE_VOID: - case DRGN_TYPE_FUNCTION: - return DRGN_OBJECT_ENCODING_NONE; - ) -} - struct drgn_error *drgn_type_error(const char *format, struct drgn_type *type) { struct drgn_qualified_type qualified_type = { type }; @@ -1314,8 +1274,8 @@ void drgn_program_deinit_types(struct drgn_program *prog) drgn_member_map_deinit(&prog->members); drgn_type_set_deinit(&prog->members_cached); - for (size_t i = 0; i < prog->created_types.size; i++) { - struct drgn_type *type = prog->created_types.data[i]; + vector_for_each(drgn_typep_vector, typep, &prog->created_types) { + struct drgn_type *type = *typep; if (drgn_type_has_members(type)) { struct drgn_type_member *members = drgn_type_members(type); @@ -1366,23 +1326,23 @@ drgn_program_add_type_finder(struct drgn_program *prog, drgn_type_find_fn fn, return NULL; } -struct drgn_error * -drgn_program_find_type_impl(struct drgn_program *prog, - enum drgn_type_kind kind, const char *name, - size_t name_len, const char *filename, - struct drgn_qualified_type *ret) +struct drgn_error *drgn_program_find_type_impl(struct drgn_program *prog, + uint64_t kinds, const char *name, + size_t name_len, + const char *filename, + struct drgn_qualified_type *ret) { struct drgn_type_finder *finder = prog->type_finders; while (finder) { struct drgn_error *err = - finder->fn(kind, name, name_len, filename, finder->arg, + finder->fn(kinds, name, name_len, filename, finder->arg, ret); if (!err) { if (drgn_type_program(ret->type) != prog) { return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, "type find callback returned type from wrong program"); } - if (drgn_type_kind(ret->type) != kind) { + if (!(kinds & (UINT64_C(1) << drgn_type_kind(ret->type)))) { return drgn_error_create(DRGN_ERROR_TYPE, "type find callback returned wrong kind of type"); } @@ -1485,7 +1445,8 @@ drgn_program_find_primitive_type(struct drgn_program *prog, spellings = drgn_primitive_type_spellings[type]; for (i = 0; spellings[i]; i++) { - err = drgn_program_find_type_impl(prog, kind, spellings[i], + err = drgn_program_find_type_impl(prog, UINT64_C(1) << kind, + spellings[i], strlen(spellings[i]), NULL, &qualified_type); if (!err && drgn_type_primitive(qualified_type.type) == type) { diff --git a/libdrgn/type.h b/libdrgn/type.h index bab54b32e..bf27639aa 100644 --- a/libdrgn/type.h +++ b/libdrgn/type.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -18,8 +18,6 @@ #include "hash_table.h" #include "vector.h" -struct drgn_language; - /** * @ingroup Internals * @@ -60,7 +58,7 @@ struct drgn_type_finder { struct drgn_type_finder *next; }; -DEFINE_HASH_SET_TYPE(drgn_dedupe_type_set, struct drgn_type *) +DEFINE_HASH_SET_TYPE(drgn_dedupe_type_set, struct drgn_type *); /** (type, member name) pair. */ struct drgn_member_key { @@ -89,8 +87,8 @@ struct drgn_member_value { */ #else DEFINE_HASH_MAP_TYPE(drgn_member_map, struct drgn_member_key, - struct drgn_member_value) -DEFINE_HASH_SET_TYPE(drgn_type_set, struct drgn_type *) + struct drgn_member_value); +DEFINE_HASH_SET_TYPE(drgn_type_set, struct drgn_type *); #endif /** @@ -179,7 +177,7 @@ struct drgn_error *drgn_float_type_create(struct drgn_program *prog, struct drgn_type **ret); DEFINE_VECTOR_TYPE(drgn_type_template_parameter_vector, - struct drgn_type_template_parameter) + struct drgn_type_template_parameter); /** * Common builder shared between compound and function types for template @@ -201,7 +199,7 @@ drgn_template_parameters_builder_add(struct drgn_template_parameters_builder *bu const union drgn_lazy_object *argument, const char *name, bool is_default); -DEFINE_VECTOR_TYPE(drgn_type_member_vector, struct drgn_type_member) +DEFINE_VECTOR_TYPE(drgn_type_member_vector, struct drgn_type_member); /** Builder for members of a structure, union, or class type. */ struct drgn_compound_type_builder { @@ -263,7 +261,7 @@ drgn_compound_type_create(struct drgn_compound_type_builder *builder, const struct drgn_language *lang, struct drgn_type **ret); -DEFINE_VECTOR_TYPE(drgn_type_enumerator_vector, struct drgn_type_enumerator) +DEFINE_VECTOR_TYPE(drgn_type_enumerator_vector, struct drgn_type_enumerator); /** Builder for enumerators of an enumerated type. */ struct drgn_enum_type_builder { @@ -409,7 +407,7 @@ drgn_incomplete_array_type_create(struct drgn_program *prog, const struct drgn_language *lang, struct drgn_type **ret); -DEFINE_VECTOR_TYPE(drgn_type_parameter_vector, struct drgn_type_parameter) +DEFINE_VECTOR_TYPE(drgn_type_parameter_vector, struct drgn_type_parameter); /** Builder for parameters of a function type. */ struct drgn_function_type_builder { @@ -560,9 +558,6 @@ bool drgn_type_is_scalar(struct drgn_type *type); struct drgn_error *drgn_type_bit_size(struct drgn_type *type, uint64_t *ret); -/** Get the appropriate @ref drgn_object_encoding for a @ref drgn_type. */ -enum drgn_object_encoding drgn_type_object_encoding(struct drgn_type *type); - /** Initialize type-related fields in a @ref drgn_program. */ void drgn_program_init_types(struct drgn_program *prog); /** Deinitialize type-related fields in a @ref drgn_program. */ @@ -584,11 +579,11 @@ void drgn_program_deinit_types(struct drgn_program *prog); * @return @c NULL on success, &@ref drgn_not_found if the type wasn't found, * non-@c NULL on other error. */ -struct drgn_error * -drgn_program_find_type_impl(struct drgn_program *prog, - enum drgn_type_kind kind, const char *name, - size_t name_len, const char *filename, - struct drgn_qualified_type *ret); +struct drgn_error *drgn_program_find_type_impl(struct drgn_program *prog, + uint64_t kinds, const char *name, + size_t name_len, + const char *filename, + struct drgn_qualified_type *ret); /** Find a primitive type in a @ref drgn_program. */ struct drgn_error * diff --git a/libdrgn/util.h b/libdrgn/util.h index 33a61533c..ab5a1cffb 100644 --- a/libdrgn/util.h +++ b/libdrgn/util.h @@ -1,12 +1,10 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file * * Miscellanous utility functions. - * - * Several of these are taken from the Linux kernel source. */ #ifndef DRGN_UTIL_H @@ -14,6 +12,7 @@ #include #include +#include #include #include #include @@ -23,6 +22,12 @@ #define LIBDRGN_PUBLIC __attribute__((__visibility__("default"))) #endif +#if defined(__has_attribute) && __has_attribute(__fallthrough__) +#define fallthrough __attribute__((__fallthrough__)) +#else +#define fallthrough do {} while (0) +#endif + #ifdef NDEBUG #define UNREACHABLE() __builtin_unreachable() #else @@ -60,37 +65,24 @@ #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) -#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) -#define __compiletime_error(message) __attribute__((__error__(message))) -#else -#define __compiletime_error(message) -#endif -#ifdef __OPTIMIZE__ -# define __compiletime_assert(condition, msg, prefix, suffix) \ - do { \ - extern void prefix ## suffix(void) __compiletime_error(msg); \ - if (!(condition)) \ - prefix ## suffix(); \ - } while (0) -#else -# define __compiletime_assert(condition, msg, prefix, suffix) do { } while (0) -#endif -#define _compiletime_assert(condition, msg, prefix, suffix) \ - __compiletime_assert(condition, msg, prefix, suffix) -#define compiletime_assert(condition, msg) \ - _compiletime_assert(condition, msg, __compiletime_assert_, __LINE__) - -#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:(-!!(e)); })) -#define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg) +/** Return whether two types or expressions have compatible types. */ +#define types_compatible(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) -#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) - -#define container_of(ptr, type, member) ({ \ - void *__mptr = (void *)(ptr); \ - BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \ - !__same_type(*(ptr), void), \ - "pointer type mismatch in container_of()"); \ - ((type *)(__mptr - offsetof(type, member))); }) +/** + * `static_assert(assert_expression, message)` as an expression that evaluates + * to `eval_expression`. + */ +#define static_assert_expression(assert_expression, message, eval_expression) \ + _Generic(sizeof(struct { _Static_assert(assert_expression, message); int _; }),\ + default: (eval_expression)) + +#define container_of(ptr, type, member) \ +static_assert_expression( \ + types_compatible(*(ptr), ((type *)0)->member) \ + || types_compatible(*(ptr), void), \ + "pointer does not match member type", \ + (type *)((char *)(ptr) - offsetof(type, member)) \ +) static inline bool strstartswith(const char *s, const char *prefix) { @@ -191,7 +183,10 @@ static inline uint64_t uint_max(int n) * * A more natural definition would be `i == 0 ? ptr : ptr + i`, but some * versions of GCC and Clang generate an unnecessary branch or conditional move - * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97225). + * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97225). Note that in standard + * C, it is undefined behavior to cast to `uintptr_t`, do arithmetic, and cast + * back, but GCC allows this as long as the result is within the same object: + * https://gcc.gnu.org/onlinedocs/gcc/Arrays-and-pointers-implementation.html. */ #define add_to_possibly_null_pointer(ptr, i) \ ((typeof(ptr))((uintptr_t)(ptr) + (i) * sizeof(*(ptr)))) diff --git a/libdrgn/vector.c b/libdrgn/vector.c deleted file mode 100644 index ab51e9919..000000000 --- a/libdrgn/vector.c +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "vector.h" - -bool vector_do_reserve(size_t new_capacity, size_t entry_size, void **data, - size_t *capacity) -{ - size_t bytes; - void *new_data; - - if (new_capacity <= *capacity || new_capacity == 0) - return true; - if (__builtin_mul_overflow(new_capacity, entry_size, &bytes)) - return false; - new_data = realloc(*data, bytes); - if (!new_data) - return false; - *data = new_data; - *capacity = new_capacity; - return true; -} - -void vector_do_shrink_to_fit(size_t size, size_t entry_size, void **data, - size_t *capacity) -{ - if (*capacity > size) { - if (size > 0) { - /* - * We already have at least size * entry_size bytes - * allocated, so we don't need to worry about overflow. - */ - void *new_data = realloc(*data, size * entry_size); - if (new_data) { - *data = new_data; - *capacity = size; - } - } else { - free(*data); - *data = NULL; - *capacity = 0; - } - } -} - -bool vector_reserve_for_append(size_t size, size_t entry_size, void **data, - size_t *capacity) -{ - size_t new_capacity, bytes; - void *new_data; - - if (size < *capacity) - return true; - if (*capacity == 0) - new_capacity = 1; - else if (__builtin_mul_overflow(2U, *capacity, &new_capacity)) - return false; - if (__builtin_mul_overflow(new_capacity, entry_size, &bytes)) - return false; - new_data = realloc(*data, bytes); - if (!new_data) - return false; - *data = new_data; - *capacity = new_capacity; - return true; -} diff --git a/libdrgn/vector.h b/libdrgn/vector.h index 0fc2c0596..5da4fa55d 100644 --- a/libdrgn/vector.h +++ b/libdrgn/vector.h @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /** * @file @@ -16,6 +16,11 @@ #include // IWYU pragma: keep #include // IWYU pragma: keep +#include "generics.h" +#include "minmax.h" +#include "util.h" +#include "pp.h" + /** * @ingroup Internals * @@ -23,7 +28,7 @@ * * Dynamic arrays (a.k.a.\ vectors). * - * This is a basic implementation of generic, strongly-typed vectors. + * This is an implementation of generic, strongly-typed vectors. * * A vector is defined with @ref DEFINE_VECTOR(). Each generated vector * interface is prefixed with a given name; the interface documented here uses @@ -34,31 +39,14 @@ #ifdef DOXYGEN /** + * @struct vector + * * Vector instance. * * There are no requirements on how this is allocated; it may be global, on the * stack, allocated by @c malloc(), embedded in another structure, etc. */ -struct vector { - /** - * The underlying array of entries. - * - * This may be accessed directly. It may be reallocated as noted. - * - * A common pattern is using a @c vector to build an array and then - * returning the raw array. To do so, don't call @ref vector_deinit(), - * then return @c data and free it with `free()`. - */ - entry_type *data; - /** The number of entries in a @ref vector. */ - size_t size; - /** - * The number of allocated elements in @ref vector::data. - * - * This should not be modified. - */ - size_t capacity; -}; +struct vector; /** * Initialize a @ref vector. @@ -69,39 +57,170 @@ struct vector { */ void vector_init(struct vector *vector); +/** Free memory allocated by a @ref vector. */ +void vector_deinit(struct vector *vector); + +/** Return the number of entries in a @ref vector. */ +size_type vector_size(const struct vector *vector); + +/** Return whether a @ref vector is empty. */ +bool vector_empty(const struct vector *vector); + /** - * Free memory allocated by a @ref vector. + * Maximum possible number of entries in a @ref vector. * - * This frees @ref vector::data. + * Attempts to increase the size or capacity beyond this will fail. */ -void vector_deinit(struct vector *vector); +const size_type vector_max_size; + +/** + * Update the number of entries in a @ref vector. + * + * If @p size is greater than the current capacity, this increases the capacity + * to at least @p size and reallocates the entries. + * + * If @p size is greater than the current size, the entries between the old size + * and the new size are uninitialized. + * + * @return @c true on success, @c false on failure. + */ +bool vector_resize(struct vector *vector, size_t size); + +/** + * Set the size of a @ref vector to zero. + * + * This does not change the capacity or free the entries. + */ +void vector_clear(struct vector *vector); + +/** Return the number of allocated entries in a @ref vector. */ +size_type vector_capacity(const struct vector *vector); /** * Increase the capacity of a @ref vector. * - * If @p capacity is greater than the current capacity of the @ref vector, this - * reallocates @ref vector::data and increases @ref vector::capacity to at least - * @p capacity. Otherwise, it does nothing. + * If @p capacity is greater than the current capacity, this increases the + * capacity to at least @p capacity and reallocates the entries. Otherwise, it + * does nothing. * * @return @c true on success, @c false on failure. */ bool vector_reserve(struct vector *vector, size_t capacity); +/** + * Increase the capacity of a @ref vector to accomodate at least one append. + * + * If the current capacity is equal to the current size, this increases the + * capacity by at least one and reallocates the entries. Otherwise, it does + * nothing. + * + * @return @c true on success, @c false on failure. + */ +bool vector_reserve_for_append(struct vector *vector); + +/** + * Increase the capacity of a @ref vector to accomodate at least @p n appends. + * + * If the current capacity minus the current size is not at least @p n, this + * increases the capacity by at least @p n and reallocates the entries. + * Otherwise, it does nothing. + * + * @return @c true on success, @c false on failure. + */ +bool vector_reserve_for_extend(struct vector *vector, size_t n); + /** * Free unused memory in a @ref vector. * - * This may reallocate @ref vector::data and set @ref vector::capacity to @ref - * vector::size. It may also do nothing. + * This may reduce the capacity and reallocate the entries. It may also do + * nothing. */ void vector_shrink_to_fit(struct vector *vector); +/** + * Steal the array of entries from a @ref vector. + * + * This returns the internal array of entries. The vector can no longer be used + * except to be passed to @ref vector_deinit(), which will do nothing. + * + * This is undefined if the vector type was defined with a non-zero @c + * inline_size. + * + * This can be used to build an array when the size isn't known ahead of time + * but won't change after the array is built. For example: + * + * ``` + * DEFINE_VECTOR(int_vector, int); + * + * bool primes_less_than(int n, int **array_ret, size_t *size_ret) + * { + * + * _cleanup_(int_vector_deinit) struct int_vector vector = VECTOR_INIT; + * for (int i = 2; i < n; i++) { + * if (is_prime(i) && !int_vector_push(&vector, &i)) + * return false; + * } + * int_vector_shrink_to_fit(&vector); + * int_vector_steal(&vector, array_ret, size_ret); + * return true; + * } + * ``` + * + * As demonstrated here, it may be desirable to call @ref vector_shrink_to_fit() + * first. + * + * @param[out] entries_ret Returned array. This must be freed with @c free(). + * @param[out] size_ret Returned number of entries in array. May be @c NULL. + */ +void vector_steal(struct vector *vector, entry_type **entries_ret, + size_type *size_ret); + +/** + * Return the array of entries in a @ref vector. + * + * The vector may be empty, in which case this is equal to `vector_end(vector)`. + */ +entry_type *vector_begin(struct vector *vector); + +/** + * Return one past the last entry in a @ref vector. + * + * The vector may be empty, in which case this is equal to + * `vector_begin(vector)`. + */ +entry_type *vector_end(struct vector *vector); + +/** + * Return the first entry in a @ref vector. + * + * This is equivalent to `vector_at(vector, 0)`. The vector must not be empty + * (in contrast to @ref vector_begin()). + */ +entry_type *vector_first(struct vector *vector); + +/** + * Return the last entry in a @ref vector. + * + * This is equivalent to `vector_at(vector, vector_size(vector) - 1)`. The + * vector must not be empty. + */ +entry_type *vector_last(struct vector *vector); + +/** + * Return the entry at the given index in a @ref vector. + * + * @param[in] i Entry index. Must be less than the size of the vector. + */ +entry_type *vector_at(struct vector *vector, size_t i); + /** * Append to a @ref vector. * - * This increases @ref vector::size by one. It may reallocate @ref vector::data - * and change @ref vector::capacity. + * This increases vector's size by one. If the current capacity is equal to the + * current size, this increases the capacity by at least one and reallocates the + * entries. * - * @return @c true on success, @c false on failure to allocate memory. + * @return @c true on success, @c false on failure. */ bool vector_append(struct vector *vector, const entry_type *entry); @@ -111,28 +230,38 @@ bool vector_append(struct vector *vector, const entry_type *entry); * Like @ref vector_append(), but return a pointer to the new (uninitialized) * entry. * - * @return The new entry on success, @c NULL on failure to allocate memory. + * @return The new entry on success, @c NULL on failure. */ entry_type *vector_append_entry(struct vector *vector); +/** + * Append all of the entries from one vector to another. + * + * @param[in] dst Vector to append to. + * @param[in] src Source vector. This is not modified. + * @return @c true on success, @c false on failure. + */ +bool vector_extend(struct vector *dst, const struct vector *src); + /** * Remove and return the last entry in a @ref vector. * - * The vector is assumed to be non-empty. This descreases @ref vector::size by - * one. It does not reallocate @ref vector::data. + * The vector must not be empty. This decreases the size by one. It does not + * change the capacity or reallocate the entries. * * @return A pointer to the removed entry, which remains valid until another - * entry is inserted in its place or @ref vector::data is reallocated. + * entry is inserted in its place or the entries are reallocated. */ entry_type *vector_pop(struct vector *vector); #endif -bool vector_do_reserve(size_t new_capacity, size_t entry_size, void **data, - size_t *capacity); -void vector_do_shrink_to_fit(size_t size, size_t entry_size, void **data, - size_t *capacity); -bool vector_reserve_for_append(size_t size, size_t entry_size, void **data, - size_t *capacity); +/** + * Inline as many entries as possible without making the vector type larger than + * if @c inline_size was 0. + * + * This can be passed as the @c inline_size argument to @ref DEFINE_VECTOR(). + */ +#define vector_inline_minimal -1 /** * Define a vector type without defining its functions. @@ -141,16 +270,57 @@ bool vector_reserve_for_append(size_t size, size_t entry_size, void **data, * header) but the interface is defined elsewhere (e.g., a source file) with * @ref DEFINE_VECTOR_FUNCTIONS(). Otherwise, just use @ref DEFINE_VECTOR(). * - * @sa DEFINE_VECTOR() + * This takes the same arguments as @ref DEFINE_VECTOR(). */ -#define DEFINE_VECTOR_TYPE(vector, entry_type) \ -typedef typeof(entry_type) vector##_entry_type; \ - \ -struct vector { \ - vector##_entry_type *data; \ - size_t size; \ - size_t capacity; \ -}; +#define DEFINE_VECTOR_TYPE(...) \ + PP_OVERLOAD(DEFINE_VECTOR_TYPE_I, __VA_ARGS__)(__VA_ARGS__) +#define DEFINE_VECTOR_TYPE_I2(vector, entry_type) \ + DEFINE_VECTOR_TYPE_I3(vector, entry_type, 0) +#define DEFINE_VECTOR_TYPE_I3(vector, entry_type, inline_size) \ + DEFINE_VECTOR_TYPE_I4(vector, entry_type, inline_size, size_t) +#define DEFINE_VECTOR_TYPE_I4(vector, entry_type, inline_size, size_type) \ +typedef typeof(entry_type) vector##_entry_type; \ + \ +typedef typeof(size_type) vector##_size_type; \ +_Static_assert((vector##_size_type)-1 > 0, "size_type must be unsigned"); \ +_Static_assert((vector##_size_type)-1 <= SIZE_MAX, \ + "size_type must not be larger than size_t"); \ + \ +enum { vector##_inline_size_arg = (inline_size) }; \ +/* \ + * If the vector was defined with a zero inline size, then we don't want to \ + * require the complete definition of the entry type, so we do this to stub it \ + * out. \ + */ \ +typedef_if(vector##_inline_entry_type, vector##_inline_size_arg == 0, void *, \ + vector##_entry_type); \ +enum { \ + vector##_inline_size = \ + vector##_inline_size_arg == vector_inline_minimal \ + ? sizeof(void *) / sizeof(vector##_inline_entry_type) \ + : vector##_inline_size_arg, \ + /* Used to avoid a zero-length array. */ \ + vector##_inline_size_non_zero = \ + vector##_inline_size == 0 ? 1 : vector##_inline_size, \ +}; \ + \ +struct vector { \ + union { \ + vector##_entry_type *_data; \ + /* \ + * If the vector has no inline entries, then we want this to \ + * degrade to (entry_type *) instead of (entry_type [0]) so that\ + * the vector is not over-aligned to alignof(entry_type) and to \ + * avoid zero-length arrays. \ + */ \ + type_if(vector##_inline_size == 0, vector##_entry_type *, \ + vector##_inline_entry_type [vector##_inline_size_non_zero])\ + _idata; \ + }; \ + vector##_size_type _size; \ + vector##_size_type _capacity; \ +}; \ +struct DEFINE_VECTOR_needs_semicolon /** * Define the functions for a vector. @@ -167,46 +337,201 @@ struct vector { \ __attribute__((__unused__)) \ static void vector##_init(struct vector *vector) \ { \ - vector->data = NULL; \ - vector->size = vector->capacity = 0; \ + if (vector##_inline_size == 0) \ + vector->_data = NULL; \ + vector->_size = vector->_capacity = 0; \ +} \ + \ +static bool vector##_is_inline(const struct vector *vector) \ +{ \ + return vector##_inline_size > 0 && vector->_capacity == 0; \ } \ \ __attribute__((__unused__)) \ static void vector##_deinit(struct vector *vector) \ { \ - free(vector->data); \ + if (!vector##_is_inline(vector)) \ + free(vector->_data); \ +} \ + \ +__attribute__((__unused__)) \ +static vector##_size_type vector##_size(const struct vector *vector) \ +{ \ + return vector->_size; \ +} \ + \ +__attribute__((__unused__)) \ +static bool vector##_empty(const struct vector *vector) \ +{ \ + return vector->_size == 0; \ +} \ + \ +static const vector##_size_type vector##_max_size = \ + min_iconst(PTRDIFF_MAX, (vector##_size_type)-1) \ + / sizeof(vector##_entry_type); \ + \ +static vector##_size_type vector##_capacity(const struct vector *vector) \ +{ \ + if (vector##_is_inline(vector)) \ + return vector##_inline_size; \ + return vector->_capacity; \ +} \ + \ +static bool vector##_reallocate(struct vector *vector, size_t capacity) \ +{ \ + void *new_data; \ + if (vector##_is_inline(vector)) { \ + new_data = malloc(capacity * sizeof(vector##_entry_type)); \ + if (!new_data) \ + return false; \ + memcpy(new_data, vector->_idata, \ + vector##_size(vector) * sizeof(vector##_entry_type)); \ + } else { \ + new_data = realloc(vector->_data, \ + capacity * sizeof(vector##_entry_type)); \ + if (!new_data) \ + return false; \ + } \ + vector->_data = new_data; \ + vector->_capacity = capacity; \ + return true; \ +} \ + \ +static bool vector##_reserve_for_extend(struct vector *vector, size_t n) \ +{ \ + vector##_size_type size = vector##_size(vector); \ + /* \ + * Cast to size_t to avoid -Wsign-error if size_type is promoted to int.\ + */ \ + if (n <= (size_t)(vector##_capacity(vector) - size)) \ + return true; \ + if (n > (size_t)(vector##_max_size - size)) \ + return false; \ + vector##_size_type new_capacity = size + (n > size ? n : size); \ + if (new_capacity < size || new_capacity > vector##_max_size) \ + new_capacity = vector##_max_size; \ + return vector##_reallocate(vector, new_capacity); \ +} \ + \ +__attribute__((__unused__)) \ +static bool vector##_resize(struct vector *vector, size_t size) \ +{ \ + if (vector->_size < size \ + && !vector##_reserve_for_extend(vector, size - vector->_size)) \ + return false; \ + vector->_size = size; \ + return true; \ +} \ + \ +__attribute__((__unused__)) \ +static void vector##_clear(struct vector *vector) \ +{ \ + vector->_size = 0; \ } \ \ __attribute__((__unused__)) \ static bool vector##_reserve(struct vector *vector, size_t capacity) \ { \ - return vector_do_reserve(capacity, sizeof(*vector->data), \ - (void **)&vector->data, &vector->capacity); \ + if (capacity <= vector##_capacity(vector)) \ + return true; \ + if (capacity > vector##_max_size) \ + return false; \ + return vector##_reallocate(vector, capacity); \ +} \ + \ +static bool vector##_reserve_for_append(struct vector *vector) \ +{ \ + return vector##_reserve_for_extend(vector, 1); \ } \ \ __attribute__((__unused__)) \ static void vector##_shrink_to_fit(struct vector *vector) \ { \ - vector_do_shrink_to_fit(vector->size, sizeof(*vector->data), \ - (void **)&vector->data, &vector->capacity); \ + vector##_size_type size = vector##_size(vector); \ + if (vector->_capacity <= size) \ + return; \ + if (size > vector##_inline_size) { \ + vector##_reallocate(vector, size); \ + } else if (vector##_inline_size > 0) { \ + void *old_data = vector->_data; \ + memcpy(vector->_idata, old_data, \ + size * sizeof(vector##_entry_type)); \ + free(old_data); \ + vector->_capacity = 0; \ + } else { \ + free(vector->_data); \ + vector->_data = NULL; \ + vector->_capacity = 0; \ + } \ +} \ + \ +/* \ + * If the vector was defined with a non-zero inline size, make vector_steal() \ + * fail at compile time by having it take a dummy type incompatible with struct \ + * vector (but close enough to the real thing so the function body compiles). \ + */ \ +struct vector##_steal_is_undefined_for_non_zero_inline_size { \ + void *_data; \ + vector##_size_type _size; \ +}; \ +__attribute__((__unused__)) \ +static void vector##_steal(type_if(vector##_inline_size_arg == 0, \ + struct vector, \ + struct vector##_steal_is_undefined_for_non_zero_inline_size)\ + *vector, \ + vector##_entry_type **entries_ret, \ + vector##_size_type *size_ret) \ +{ \ + *entries_ret = vector->_data; \ + if (size_ret) \ + *size_ret = vector->_size; \ + vector->_data = NULL; \ +} \ + \ +static vector##_entry_type *vector##_begin(struct vector *vector) \ +{ \ + if (vector##_is_inline(vector)) \ + return vector->_idata; \ + return vector->_data; \ +} \ + \ +__attribute__((__unused__)) \ +static vector##_entry_type *vector##_end(struct vector *vector) \ +{ \ + return add_to_possibly_null_pointer(vector##_begin(vector), \ + vector##_size(vector)); \ +} \ + \ +__attribute__((__unused__)) \ +static vector##_entry_type *vector##_first(struct vector *vector) \ +{ \ + return vector##_begin(vector); \ +} \ + \ +__attribute__((__unused__)) \ +static vector##_entry_type *vector##_last(struct vector *vector) \ +{ \ + return vector##_begin(vector) + vector##_size(vector) - 1; \ +} \ + \ +__attribute__((__unused__)) \ +static vector##_entry_type *vector##_at(struct vector *vector, size_t i) \ +{ \ + return vector##_begin(vector) + i; \ } \ \ static vector##_entry_type *vector##_append_entry(struct vector *vector) \ { \ - if (!vector_reserve_for_append(vector->size, sizeof(*vector->data), \ - (void **)&vector->data, \ - &vector->capacity)) \ + if (!vector##_reserve_for_append(vector)) \ return NULL; \ - return &vector->data[vector->size++]; \ + return vector##_begin(vector) + vector->_size++; \ } \ \ __attribute__((__unused__)) \ static bool vector##_append(struct vector *vector, \ const vector##_entry_type *entry) \ { \ - vector##_entry_type *new_entry; \ - \ - new_entry = vector##_append_entry(vector); \ + vector##_entry_type *new_entry = vector##_append_entry(vector); \ if (!new_entry) \ return false; \ memcpy(new_entry, entry, sizeof(*entry)); \ @@ -214,22 +539,49 @@ static bool vector##_append(struct vector *vector, \ } \ \ __attribute__((__unused__)) \ +static bool vector##_extend(struct vector *dst, const struct vector *src) \ +{ \ + if (src->_size == 0) \ + return true; \ + if (!vector##_reserve_for_extend(dst, src->_size)) \ + return false; \ + memcpy(vector##_end(dst), vector##_begin((struct vector *)src), \ + src->_size * sizeof(vector##_entry_type)); \ + dst->_size += src->_size; \ + return true; \ +} \ + \ +__attribute__((__unused__)) \ static vector##_entry_type *vector##_pop(struct vector *vector) \ { \ - return &vector->data[--vector->size]; \ -} + return vector##_begin(vector) + --vector->_size; \ +} \ +struct DEFINE_VECTOR_needs_semicolon /** * Define a vector interface. * - * This macro defines a vector type along with its functions. + * This macro defines a vector type along with its functions. It accepts a + * variable number of arguments: + * + * ``` + * DEFINE_VECTOR(vector, entry_type); + * DEFINE_VECTOR(vector, entry_type, inline_size); + * DEFINE_VECTOR(vector, entry_type, inline_size, size_type); + * ``` * * @param[in] vector Name of the type to define. This is prefixed to all of the * types and functions defined for that type. * @param[in] entry_type Type of entries in the vector. + * @param[in] inline_size Number of entries to store directly in the vector type + * instead of as a separate allocation, or @ref vector_inline_minimal. The + * default is 0. If this is not 0, then the complete definition of @p entry_type + * must be available. + * @param[in] size_type Unsigned integer type to use to store size and capacity. + * The default is `size_t`. */ -#define DEFINE_VECTOR(vector, entry_type) \ -DEFINE_VECTOR_TYPE(vector, entry_type) \ +#define DEFINE_VECTOR(vector, ...) \ +DEFINE_VECTOR_TYPE(vector, __VA_ARGS__); \ DEFINE_VECTOR_FUNCTIONS(vector) /** @@ -239,7 +591,32 @@ DEFINE_VECTOR_FUNCTIONS(vector) * * @sa vector_init() */ -#define VECTOR_INIT { NULL } +#define VECTOR_INIT { { 0 } } + +/** + * Iterate over every entry in a @ref vector. + * + * This is roughly equivalent to + * + * ``` + * for (entry_type *it = vector_begin(vector), *end = vector_end(vector); + * it != end; it++) + * ``` + * + * Except that @p vector is only evaluated once. + * + * @param[in] vector_type Name of vector type. + * @param[out] it Name of iteration variable. + * @param[in] vector Vector to iterate over. + */ +#define vector_for_each(vector_type, it, vector) \ + for (vector_type##_entry_type *it, \ + *it##__end = ({ \ + struct vector_type *it##__vector = (vector); \ + it = vector_type##_begin(it##__vector); \ + vector_type##_end(it##__vector); \ + }); \ + it != it##__end; it++) /** @} */ diff --git a/scripts/build_dists.sh b/scripts/build_dists.sh index eb7667eab..ea2d2dc46 100755 --- a/scripts/build_dists.sh +++ b/scripts/build_dists.sh @@ -1,4 +1,6 @@ #!/bin/sh +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later set -eux @@ -7,7 +9,7 @@ set -eux SDIST=dist/drgn-"$("$PYTHON" setup.py --version)".tar.gz ${DOCKER=docker} run -it \ - --env PLAT=manylinux2010_x86_64 \ + --env PLAT=manylinux2014_x86_64 \ --env SDIST="$SDIST" \ --env OWNER="$(id -u):$(id -g)" \ --volume "$(pwd)":/io:ro \ @@ -16,5 +18,5 @@ ${DOCKER=docker} run -it \ --hostname drgn \ --rm \ --pull always \ - quay.io/pypa/manylinux2010_x86_64 \ - ./scripts/build_manylinux_in_docker.sh + quay.io/pypa/manylinux2014_x86_64 \ + ./scripts/build_manylinux_in_docker.sh "${1:-}" diff --git a/scripts/build_manylinux_in_docker.sh b/scripts/build_manylinux_in_docker.sh index 3caf12cd0..2e9f9b6a5 100755 --- a/scripts/build_manylinux_in_docker.sh +++ b/scripts/build_manylinux_in_docker.sh @@ -1,4 +1,6 @@ #!/bin/sh +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later set -eux @@ -19,9 +21,15 @@ yum install -y \ # https://github.com/pypa/manylinux/issues/731. ln -s /usr/share/aclocal/pkg.m4 /usr/local/share/aclocal/ +BUILD_ONLY_PYTHON="" +if [ -n "${1:-}" ]; then + # Translate, e.g. 3.10 -> (3, 10) + BUILD_ONLY_PYTHON="$(echo "$1" | perl -pe 's/(\d+)\.(\d+)/(\1, \2)/')" +fi + # Install a recent version of elfutils instead of whatever is in the manylinux # image. -elfutils_version=0.187 +elfutils_version=0.189 elfutils_url=https://sourceware.org/elfutils/ftp/$elfutils_version/elfutils-$elfutils_version.tar.bz2 mkdir /tmp/elfutils cd /tmp/elfutils @@ -36,29 +44,12 @@ curl -L "$elfutils_url" | tar -xj --strip-components=1 make -j$(($(nproc) + 1)) make install -libkdumpfile_version=0.4.1 +libkdumpfile_version=0.5.2 libkdumpfile_url=https://github.com/ptesarik/libkdumpfile/releases/download/v$libkdumpfile_version/libkdumpfile-$libkdumpfile_version.tar.gz mkdir /tmp/libkdumpfile cd /tmp/libkdumpfile curl -L "$libkdumpfile_url" | tar -xz --strip-components=1 -# This file is missing an include of limits.h which it accidentally gets from -# zlib.h via zconf.h, but only since zlib 1.2.7. CentOS 6 has 1.2.3. -patch -p1 << "EOF" -diff --git a/src/kdumpfile/util.c b/src/kdumpfile/util.c -index 4fb2960..14e1ce3 100644 ---- a/src/kdumpfile/util.c -+++ b/src/kdumpfile/util.c -@@ -38,6 +38,7 @@ - #include - #include - #include -+#include - - #if USE_ZLIB - # include -EOF -# z_const was added in zlib 1.2.5.2. -CPPFLAGS="-Dz_const=const" ./configure --with-lzo --with-snappy --with-zlib --without-python +./configure --with-libzstd --with-lzo2 --with-snappy --with-zlib --without-python make -j$(($(nproc) + 1)) make install @@ -68,15 +59,19 @@ mkdir /tmp/drgn cd /tmp/drgn tar -xf "/io/$SDIST" --strip-components=1 -python_supported() { - "$1" -c 'import sys; sys.exit(sys.version_info < (3, 6))' +build_for_python() { + if [ -n "$BUILD_ONLY_PYTHON" ]; then + # Build for selected Python release only + "$1" -c "import sys; sys.exit(sys.version_info[:2] != $BUILD_ONLY_PYTHON)" + else + # Build for all supported Pythons + "$1" -c 'import sys; sys.exit(sys.version_info < (3, 6))' + fi } for pybin in /opt/python/cp*/bin; do - if python_supported "$pybin/python"; then - # static_assert was added to assert.h in glibc 2.16, but CentOS - # 6 has 2.12. - CPPFLAGS="-Dstatic_assert=_Static_assert" "$pybin/pip" wheel . --no-deps -w /tmp/wheels/ + if build_for_python "$pybin/python"; then + "$pybin/pip" wheel . --no-deps -w /tmp/wheels/ fi done @@ -89,7 +84,7 @@ for wheel in /tmp/wheels/*.whl; do done for pybin in /opt/python/cp*/bin; do - if python_supported "$pybin/python"; then + if build_for_python "$pybin/python"; then "$pybin/pip" install drgn --no-index -f /tmp/manylinux_wheels/ "$pybin/drgn" --version "$pybin/python" setup.py test diff --git a/scripts/cscope.sh b/scripts/cscope.sh index ac81aff76..ed18ad227 100755 --- a/scripts/cscope.sh +++ b/scripts/cscope.sh @@ -1,4 +1,6 @@ #!/bin/bash +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later : ${PYTHON:=python3} cscope_args=(-bq -i-) diff --git a/scripts/gen_dwarf_constants.py b/scripts/gen_dwarf_constants.py new file mode 100755 index 000000000..00d71343d --- /dev/null +++ b/scripts/gen_dwarf_constants.py @@ -0,0 +1,279 @@ +#!/usr/bin/env python3 +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + + +import argparse +import keyword +import re +import sys +from typing import Dict, List, NamedTuple, Sequence, TextIO, cast + + +class DwarfConstant(NamedTuple): + name: str + value: int + + +class DwarfConstantType(NamedTuple): + name: str + constants: Sequence[DwarfConstant] + + +def parse_dwarf_constants(f: TextIO) -> Sequence[DwarfConstantType]: + types: Dict[str, Dict[str, int]] = { + type_name: {} + for type_name in ( + "DW_ACCESS", + "DW_ADDR", + "DW_AT", + "DW_ATE", + "DW_CC", + "DW_CFA", + "DW_CHILDREN", + "DW_DEFAULTED", + "DW_DS", + "DW_DSC", + "DW_EH_PE", + "DW_END", + "DW_FORM", + "DW_ID", + "DW_IDX", + "DW_INL", + "DW_LANG", + "DW_LLE", + "DW_LNCT", + "DW_LNE", + "DW_LNS", + "DW_MACINFO", + "DW_MACRO", + "DW_OP", + "DW_ORD", + "DW_RLE", + "DW_SECT", + "DW_TAG", + "DW_UT", + "DW_VIRTUALITY", + "DW_VIS", + ) + } + + for match in re.finditer( + r"^\s*#\s*define\s+(" + "|".join(types) + r")_(\w+)\s+(\S+)", + sys.stdin.read(), + flags=re.MULTILINE, + ): + type_name = match.group(1) + name = match.group(2) + value = int(match.group(3), 0) + if (type_name, name) in { + # Typos in the wild that libdwarf includes but we don't want. + ("DW_AT", "stride"), # "DWARF3 (do not use)" + ("DW_CFA", "low_user"), # "Incorrect spelling, do not use" + ("DW_TAG", "namelist_items"), # "SGI misspelling/typo" + ("DW_TAG", "template_type_param"), # "DWARF2 inconsistent" + ("DW_TAG", "template_value_param"), # "DWARF2 inconsistent" + # libdwarf probably included this one to be consistent with the + # standard DWARF template_foo_parameter names, but it's called + # DW_TAG_GNU_template_template_param everywhere else. + ("DW_TAG", "GNU_template_template_parameter"), + # This name isn't mentioned in any version of the DWARF standard. + ("DW_CFA", "extended"), + }: + continue + # Typos in libdwarf itself. + elif (type_name, name) == ("DW_CFA", "high_user"): + name = "hi_user" + elif (type_name, name) == ("DW_IDX", "hi_user"): + value = 0x3FFF + elif (type_name, name) == ("DW_LANG", "Haskel"): + name = "Haskell" + if types[type_name].setdefault(name, value) != value: + raise ValueError(f"{type_name}_{name} redefined with different value") + + result = [ + DwarfConstantType( + name=type_name, + constants=[DwarfConstant(name, value) for name, value in constants.items()], + ) + for type_name, constants in types.items() + ] + + def insert_after( + type_name: str, after_name: str, insert_constant: DwarfConstant + ) -> None: + for constant_type in result: + if constant_type.name == type_name: + break + else: + raise ValueError() + constants = cast(List[DwarfConstant], constant_type.constants) + for i, constant in enumerate(constants): + if constant.name == after_name: + break + else: + raise ValueError() + constants.insert(i + 1, insert_constant) + + insert_after("DW_EH_PE", "sdata8", DwarfConstant("signed", 0x8)) + insert_after("DW_EH_PE", "aligned", DwarfConstant("indirect", 0x80)) + + return result + + +_DWARF_CONSTANTS_WANT_STR = {"DW_TAG"} + + +def gen_dwarf_constants_h( + dwarf_constants: Sequence[DwarfConstantType], f: TextIO +) -> None: + f.write( + """\ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// SPDX-License-Identifier: LGPL-2.1-or-later +// Generated by scripts/gen_dwarf_constants.py. + +/** + * @file + * + * DWARF constant definitions. + * + * This file defines the following for each known DWARF constant type: + * + * 1. An X macro defining all of the known names and values of the type: + * `DW_FOO_DEFINITIONS`. + * 2. Enumerators defining the constants: `DW_FOO_a`, `DW_FOO_b`, etc. + * 3. For select types, a function to translate a value to its name: + * `dw_foo_str()`. + */ + +#ifndef DWARF_CONSTANTS_H +#define DWARF_CONSTANTS_H + +#define X(name, value) name = value, +""" + ) + for constant_type in dwarf_constants: + f.write( + f""" +#define {constant_type.name}_DEFINITIONS \\ +""" + ) + for i, constant in enumerate(constant_type.constants): + end = " \\" if i < len(constant_type.constants) - 1 else "" + f.write( + f"\tX({constant_type.name}_{constant.name}, 0x{constant.value:x}){end}\n" + ) + f.write(f"enum {{ {constant_type.name}_DEFINITIONS }};\n") + if constant_type.name in _DWARF_CONSTANTS_WANT_STR: + f.write( + f"""\ +#define {constant_type.name}_STR_UNKNOWN_FORMAT "{constant_type.name}_<0x%x>" +#define {constant_type.name}_STR_BUF_LEN (sizeof({constant_type.name}_STR_UNKNOWN_FORMAT) - 2 + 2 * sizeof(int)) +/** + * Get the name of a `{constant_type.name}` value. + * + * @return Static string if the value is known or @p buf if the value is + * unknown. + */ +const char *{constant_type.name.lower()}_str(int value, char buf[static {constant_type.name}_STR_BUF_LEN]); +""" + ) + + f.write( + """ +#undef X + +#endif /* DWARF_CONSTANTS_H */ +""" + ) + + +def gen_dwarf_constants_c( + dwarf_constants: Sequence[DwarfConstantType], f: TextIO +) -> None: + f.write( + """\ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// SPDX-License-Identifier: LGPL-2.1-or-later +// Generated by scripts/gen_dwarf_constants.py. + +#include + +#include "dwarf_constants.h" + +#define X(name, value) case name: return #name; +""" + ) + for constant_type in dwarf_constants: + if constant_type.name in _DWARF_CONSTANTS_WANT_STR: + f.write( + f""" +const char *{constant_type.name.lower()}_str(int value, char buf[static {constant_type.name}_STR_BUF_LEN]) +{{ + switch (value) {{ + {constant_type.name}_DEFINITIONS + default: + snprintf(buf, {constant_type.name}_STR_BUF_LEN, {constant_type.name}_STR_UNKNOWN_FORMAT, value); + return buf; + }} +}} +""" + ) + + f.write( + """ +#undef X +""" + ) + + +def gen_tests_dwarf_py(dwarf_constants: Sequence[DwarfConstantType], f: TextIO) -> None: + f.write( + """\ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later +# Generated by scripts/gen_dwarf_constants.py. + +import enum +from typing import Text +""" + ) + for constant_type in dwarf_constants: + f.write(f"\n\nclass {constant_type.name}(enum.IntEnum):\n") + for constant in constant_type.constants: + name = constant.name + if keyword.iskeyword(name): + name += "_" + f.write(f" {name} = 0x{constant.value:X}") + if name == "name": + f.write(" # type: ignore") + f.write("\n") + f.write( + f""" + @classmethod + def str(cls, value: int) -> Text: + try: + return f"{constant_type.name}_{{cls(value).name}}" + except ValueError: + return hex(value) +""" + ) + + +def main() -> None: + argparse.ArgumentParser( + description="Generate libdrgn/dwarf_constants.h, libdrgn/dwarf_constants.c, and tests/dwarf.py from libdwarf/src/lib/libdwarf/dwarf.h (read from standard input)" + ).parse_args() + + dwarf_constants = parse_dwarf_constants(sys.stdin) + with open("libdrgn/dwarf_constants.h", "w") as f: + gen_dwarf_constants_h(dwarf_constants, f) + with open("libdrgn/dwarf_constants.c", "w") as f: + gen_dwarf_constants_c(dwarf_constants, f) + with open("tests/dwarf.py", "w") as f: + gen_tests_dwarf_py(dwarf_constants, f) + + +if __name__ == "__main__": + main() diff --git a/scripts/gen_elf_compat.py b/scripts/gen_elf_compat.py new file mode 100755 index 000000000..94a780c9a --- /dev/null +++ b/scripts/gen_elf_compat.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +import argparse +import re +import subprocess + +# Macros missing from glibc 2.17, which is the oldest version we test against +# (when we build manylinux2014 wheels). +MACROS = ( + # Added in glibc 2.18. + "NT_FILE", + # Added in glibc 2.24. + "EM_RISCV", + # Added in glibc 2.27. + # We don't need all of the RISC-V relocation types, but it doesn't hurt. + "R_RISCV_NONE", + "R_RISCV_32", + "R_RISCV_64", + "R_RISCV_RELATIVE", + "R_RISCV_COPY", + "R_RISCV_JUMP_SLOT", + "R_RISCV_TLS_DTPMOD32", + "R_RISCV_TLS_DTPMOD64", + "R_RISCV_TLS_DTPREL32", + "R_RISCV_TLS_DTPREL64", + "R_RISCV_TLS_TPREL32", + "R_RISCV_TLS_TPREL64", + # Added in glibc 2.28. + "R_RISCV_BRANCH", + "R_RISCV_JAL", + "R_RISCV_CALL", + "R_RISCV_CALL_PLT", + "R_RISCV_GOT_HI20", + "R_RISCV_TLS_GOT_HI20", + "R_RISCV_TLS_GD_HI20", + "R_RISCV_PCREL_HI20", + "R_RISCV_PCREL_LO12_I", + "R_RISCV_PCREL_LO12_S", + "R_RISCV_HI20", + "R_RISCV_LO12_I", + "R_RISCV_LO12_S", + "R_RISCV_TPREL_HI20", + "R_RISCV_TPREL_LO12_I", + "R_RISCV_TPREL_LO12_S", + "R_RISCV_TPREL_ADD", + "R_RISCV_ADD8", + "R_RISCV_ADD16", + "R_RISCV_ADD32", + "R_RISCV_ADD64", + "R_RISCV_SUB8", + "R_RISCV_SUB16", + "R_RISCV_SUB32", + "R_RISCV_SUB64", + "R_RISCV_GNU_VTINHERIT", + "R_RISCV_GNU_VTENTRY", + "R_RISCV_ALIGN", + "R_RISCV_RVC_BRANCH", + "R_RISCV_RVC_JUMP", + "R_RISCV_RVC_LUI", + "R_RISCV_GPREL_I", + "R_RISCV_GPREL_S", + "R_RISCV_TPREL_I", + "R_RISCV_TPREL_S", + "R_RISCV_RELAX", + "R_RISCV_SUB6", + "R_RISCV_SET6", + "R_RISCV_SET8", + "R_RISCV_SET16", + "R_RISCV_SET32", + "R_RISCV_32_PCREL", + # Added in glibc 2.30. + "NT_ARM_PAC_MASK", +) + + +def main() -> None: + argparse.ArgumentParser( + description="Generate definitions missing from older versions of glibc for libdrgn/include/elf.h from elf.h" + ).parse_args() + + contents = subprocess.check_output( + ["gcc", "-dD", "-E", "-"], + input="#include \n", + universal_newlines=True, + ) + + macros = { + match.group(1): match.group(2) + for match in re.finditer( + r"^\s*#\s*define\s+(" + "|".join(MACROS) + r")\s+(.*)", + contents, + re.MULTILINE, + ) + } + + print("// Generated by scripts/gen_elf_compat.py.") + for macro in MACROS: + print("#ifndef", macro) + print("#define", macro, macros[macro]) + print("#endif") + + +if __name__ == "__main__": + main() diff --git a/scripts/gen_pp_cat.py b/scripts/gen_pp_cat.py index 7109f17c3..49898c15b 100755 --- a/scripts/gen_pp_cat.py +++ b/scripts/gen_pp_cat.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import argparse diff --git a/scripts/gen_tests_elf_py.py b/scripts/gen_tests_elf_py.py new file mode 100755 index 000000000..24dc67248 --- /dev/null +++ b/scripts/gen_tests_elf_py.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +import argparse +import re +import subprocess +import sys + + +def main() -> None: + argparse.ArgumentParser(description="Generate tests/elf.py from elf.h").parse_args() + + contents = subprocess.check_output( + ["gcc", "-dD", "-E", "-"], + input="#include \n", + universal_newlines=True, + ) + + enums = { + name: [] + for name in ( + "ET", + "PT", + "SHF", + "SHN", + "SHT", + "STB", + "STT", + "STV", + ) + } + for match in re.finditer( + r"^\s*#\s*define\s+(?P" + + "|".join(enums) + + r")_(?P\w+)\s+(?:(?P0x[0-9a-fA-F]+|[0-9]+)|(?:\(\s*1U?\s*<<\s*(?P[0-9]+)\s*\)))", + contents, + re.MULTILINE, + ): + enum = match.group("enum") + name = match.group("name") + if match.group("value"): + value = int(match.group("value"), 0) + else: + value = 1 << int(match.group("bitshift"), 10) + enums[enum].append((name, value)) + + f = sys.stdout + f.write( + """\ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later +# Generated by scripts/gen_tests_elf_py.py. + +import enum +from typing import Text +""" + ) + for type_name, constants in enums.items(): + assert constants + f.write(f"\n\nclass {type_name}(enum.IntEnum):\n") + for name, value in constants: + f.write(f" {name} = 0x{value:X}\n") + f.write( + f""" + @classmethod + def str(cls, value: int) -> Text: + try: + return f"{type_name}_{{cls(value).name}}" + except ValueError: + return hex(value) +""" + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_page_flag_getters.py b/scripts/generate_page_flag_getters.py index ec47d5b52..54872892b 100755 --- a/scripts/generate_page_flag_getters.py +++ b/scripts/generate_page_flag_getters.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import argparse import re diff --git a/scripts/generate_primitive_type_spellings.py b/scripts/generate_primitive_type_spellings.py index 6f620c666..8d5d2f909 100755 --- a/scripts/generate_primitive_type_spellings.py +++ b/scripts/generate_primitive_type_spellings.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import itertools import sys diff --git a/scripts/generate_test_constants.py b/scripts/generate_test_constants.py deleted file mode 100755 index 65738a02e..000000000 --- a/scripts/generate_test_constants.py +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later - -import argparse -import keyword -from pathlib import Path -import re - -ENUMS = { - "elf": ( - "ET", - "PT", - "SHN", - "SHT", - "STB", - "STT", - "STV", - ), - "dwarf": ( - "DW_AT", - "DW_ATE", - "DW_CHILDREN", - "DW_END", - "DW_FORM", - "DW_LANG", - "DW_LNE", - "DW_LNS", - "DW_OP", - "DW_TAG", - ), -} - - -VALUE_REGEX = r"(?P0x[0-9a-fA-F]+|[0-9]+)" -REGEXES = { - "elf": r"^\s*#\s*define\s+(?P" - + "|".join(ENUMS["elf"]) - + r")_(?P\w+)\s+" - + VALUE_REGEX, - "dwarf": r"^\s*(?P" - + "|".join(ENUMS["dwarf"]) - + r")_(?P\w+)\s*=\s*" - + VALUE_REGEX, -} - - -def read_header(name: str) -> str: - contents = (Path("libdrgn/include") / name).read_text() - contents = re.sub(r"/\*.*?\*/", "", contents, flags=re.DOTALL) - contents = re.sub(r"\\\n", "", contents) - return contents - - -def generate_constants(file: str) -> None: - contents = read_header(file + ".h") - - enums = {} - for match in re.finditer(REGEXES[file], contents, re.MULTILINE): - enum = match.group("enum") - name = match.group("name") - value = int(match.group("value"), 0) - try: - enums[enum].append((name, value)) - except KeyError: - enums[enum] = [(name, value)] - - print( - f"""\ -# Automatically generated from {file}.h - -import enum -from typing import Text - -""" - ) - first = True - for enum in ENUMS[file]: - assert enums[enum] - if not first: - print() - print() - first = False - print(f"class {enum}(enum.IntEnum):") - for name, value in enums[enum]: - if keyword.iskeyword(name): - name += "_" - print(f" {name} = 0x{value:X}", end="") - if name == "name": - print(" # type: ignore") - else: - print() - print() - print(" @classmethod") - print(" def str(cls, value: int) -> Text:") - print(" try:") - print(f' return f"{enum}_{{cls(value).name}}"') - print(" except ValueError:") - print(" return hex(value)") - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="generate constants for Python tests from header file" - ) - parser.add_argument("file", choices=list(ENUMS)) - args = parser.parse_args() - generate_constants(args.file) diff --git a/scripts/iwyu.py b/scripts/iwyu.py index 26e4d776d..8ca02d169 100755 --- a/scripts/iwyu.py +++ b/scripts/iwyu.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import argparse import json @@ -130,44 +130,6 @@ def gen_python_mapping_file(mapping_path): os.rename(mapping_path + ".tmp", mapping_path) -def iwyu_associated_header(path): - with open(path, "r") as f: - match = re.search( - r'^\s*#\s*include\s+"([^"]+)"\s+//\s+IWYU\s+pragma:\s+associated', - f.read(), - re.M, - ) - if match: - return os.path.join(os.path.dirname(path), match.group(1)) - if path.endswith(".c"): - return path[:-2] + ".h" - return None - - -def ignore_line(path, state, line): - # include-what-you-use/include-what-you-use#969: iwyu recommends bogus - # forward declarations for the anonymous unions generated by - # BINARY_OP_SIGNED_2C. - if line.endswith("::;"): - return True - - # include-what-you-use/include-what-you-use#971: drgn.h "exports" a forward - # declaration of several opaque types, but iwyu doesn't have such a notion. - if re.fullmatch( - r"struct drgn_(language|platform|program|register|stack_trace|symbol);", line - ): - paths = [path] - associated_header = iwyu_associated_header(path) - if associated_header is not None: - paths.append(associated_header) - for path in paths: - with open(path, "r") as f: - if re.search(r'^#include "(drgn.h|drgnpy.h)"', f.read(), re.M): - return True - - return False - - def main(): parser = argparse.ArgumentParser(description="run include-what-you-use on drgn") parser.add_argument( @@ -247,11 +209,7 @@ def main(): else: header = None lines.clear() - elif ( - line - and state != "include_list" - and not ignore_line(path, state, line) - ): + elif line and state != "include_list": if header is not None: print("\n" + header) header = None diff --git a/scripts/test_cityhash.c b/scripts/test_cityhash.c index a23dba958..1c5caa0be 100644 --- a/scripts/test_cityhash.c +++ b/scripts/test_cityhash.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later #include #include diff --git a/setup.py b/setup.py index 2c0703ad1..e0e106171 100755 --- a/setup.py +++ b/setup.py @@ -1,16 +1,9 @@ #!/usr/bin/env python3 # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later - -# setuptools must be imported before distutils (see pypa/setuptools#2230). -import setuptools # isort: skip # noqa: F401 +# SPDX-License-Identifier: LGPL-2.1-or-later import contextlib -from distutils import log -from distutils.command.build import build as _build -from distutils.dir_util import mkpath -from distutils.errors import DistutilsError -from distutils.file_util import copy_file +import logging import os import os.path from pathlib import Path @@ -18,7 +11,6 @@ import shlex import subprocess import sys -import tempfile from setuptools import Command, find_packages, setup from setuptools.command.build_ext import build_ext as _build_ext @@ -26,7 +18,23 @@ from setuptools.command.sdist import sdist as _sdist from setuptools.extension import Extension +# setuptools must be imported before distutils (see pypa/setuptools#2230), so +# make sure to keep these fallbacks after the other setuptools imports. +try: + # This was added in setuptools 62.4.0 (released June 13th, 2022). + from setuptools.command.build import build as _build +except ImportError: + from distutils.command.build import build as _build +try: + # This was added in setuptools 59.0.0 (released November 12th, 2021). + from setuptools.errors import BaseError +except ImportError: + from distutils.errors import DistutilsError as BaseError + from util import nproc, out_of_date +from vmtest.config import KERNEL_FLAVORS, SUPPORTED_KERNEL_VERSIONS + +logger = logging.getLogger(__name__) class build(_build): @@ -60,7 +68,7 @@ def _run_autoreconf(self): raise def _run_configure(self): - mkpath(self.build_temp) + self.mkpath(self.build_temp) makefile = os.path.join(self.build_temp, "Makefile") if not os.path.exists(makefile): args = [ @@ -99,11 +107,11 @@ def run(self): self.make() so = os.path.join(self.build_temp, ".libs/_drgn.so") if self.inplace: - copy_file(so, self.get_ext_fullpath("_drgn"), update=True) + self.copy_file(so, self.get_ext_fullpath("_drgn")) old_inplace, self.inplace = self.inplace, 0 build_path = self.get_ext_fullpath("_drgn") - mkpath(os.path.dirname(build_path)) - copy_file(so, build_path, update=True) + self.mkpath(os.path.dirname(build_path)) + self.copy_file(so, build_path) self.inplace = old_inplace @@ -131,33 +139,12 @@ def make_release_tree(self, base_dir, files): class test(Command): description = "run unit tests after in-place build" - KERNELS = [ - "6.0", - "5.19", - "5.18", - "5.17", - "5.16", - "5.15", - "5.14", - "5.13", - "5.12", - "5.11", - "5.10", - "5.4", - "4.19", - "4.14", - "4.9", - "4.4", - ] - - KERNEL_FLAVORS = ["default", "alternative", "tiny"] - user_options = [ ( "kernel", "K", "run Linux kernel tests in a virtual machine on all supported kernels " - f"({', '.join(KERNELS)})", + f"({', '.join(SUPPORTED_KERNEL_VERSIONS)})", ), ( "all-kernel-flavors", @@ -187,9 +174,11 @@ def initialize_options(self): def finalize_options(self): self.kernels = [kernel for kernel in self.extra_kernels.split(",") if kernel] if self.kernel: - flavors = test.KERNEL_FLAVORS if self.all_kernel_flavors else [""] + flavors = KERNEL_FLAVORS if self.all_kernel_flavors else [""] self.kernels.extend( - kernel + ".*" + flavor for kernel in test.KERNELS for flavor in flavors + kernel + ".*" + flavor + for kernel in SUPPORTED_KERNEL_VERSIONS + for flavor in flavors ) if self.vmtest_dir is None: build_base = self.get_finalized_command("build").build_base @@ -204,60 +193,21 @@ def _run_local(self): test = unittest.main(module=None, argv=argv, exit=False) return test.result.wasSuccessful() - def _build_kmod(self, kernel_dir, kmod): - kernel_build_dir = kernel_dir / "build" - # External modules can't do out-of-tree builds for some reason, so copy - # the source files to a temporary directory and build the module there, - # then move it to the final location. - kmod_source_dir = Path("tests/linux_kernel/kmod") - source_files = ("drgn_test.c", "Makefile") - if out_of_date( - kmod, *[kmod_source_dir / filename for filename in source_files] - ): - with tempfile.TemporaryDirectory(dir=kmod.parent) as tmp_name: - tmp_dir = Path(tmp_name) - # Make sure that header files have the same paths as in the - # original kernel build. - debug_prefix_map = [ - f"{kernel_build_dir.resolve()}=.", - f"{tmp_dir.resolve()}=./drgn_test", - ] - cflags = " ".join( - ["-fdebug-prefix-map=" + map for map in debug_prefix_map] - ) - for filename in source_files: - copy_file(kmod_source_dir / filename, tmp_dir / filename) - if ( - subprocess.call( - [ - "make", - "-C", - kernel_build_dir, - f"M={tmp_dir.resolve()}", - "KAFLAGS=" + cflags, - "KCFLAGS=" + cflags, - "-j", - str(nproc()), - ] - ) - != 0 - ): - return False - (tmp_dir / "drgn_test.ko").rename(kmod) - return True - - def _run_vm(self, kernel_dir, kernel_release): + def _run_vm(self, kernel): + from vmtest.kmod import build_kmod import vmtest.vm - self.announce(f"running tests in VM on Linux {kernel_release}", log.INFO) + logger.info("running tests in VM on Linux %s", kernel.release) - kmod = kernel_dir.parent / f"drgn_test-{kernel_release}.ko" - if not self._build_kmod(kernel_dir, kmod): + try: + kmod = build_kmod(Path(self.vmtest_dir), kernel) + except subprocess.CalledProcessError: return False command = rf""" set -e +export PYTHON={shlex.quote(sys.executable)} export DRGN_TEST_KMOD={shlex.quote(str(kmod))} if [ -e /proc/vmcore ]; then "$PYTHON" -Bm unittest discover -t . -s tests/linux_kernel/vmcore {"-v" if self.verbose else ""} @@ -265,25 +215,26 @@ def _run_vm(self, kernel_dir, kernel_release): insmod "$DRGN_TEST_KMOD" DRGN_RUN_LINUX_KERNEL_TESTS=1 "$PYTHON" -Bm \ unittest discover -t . -s tests/linux_kernel {"-v" if self.verbose else ""} - "$PYTHON" vmtest/enter_kdump.py + "$PYTHON" -Bm vmtest.enter_kdump # We should crash and not reach this. exit 1 fi """ try: returncode = vmtest.vm.run_in_vm( - command, Path(kernel_dir), Path(self.vmtest_dir) + command, kernel, Path("/"), Path(self.vmtest_dir) ) - except vmtest.vm.LostVMError as e: - self.announce(f"error on Linux {kernel_release}: {e}", log.ERROR) + except vmtest.vm.LostVMError: + logger.exception("error on Linux %s", kernel.release) return False - self.announce( - f"Tests in VM on Linux {kernel_release} returned {returncode}", log.INFO - ) + logger.info("Tests in VM on Linux %s returned %d", kernel.release, returncode) return returncode == 0 def run(self): - from vmtest.download import download_kernels_in_thread + import urllib.error + + from vmtest.config import ARCHITECTURES, Kernel, local_kernel + from vmtest.download import DownloadCompiler, DownloadKernel, download_in_thread if os.getenv("GITHUB_ACTIONS") == "true": @@ -303,51 +254,68 @@ def github_workflow_group(title): # Start downloads ASAP so that they're hopefully done by the time we # need them. - with download_kernels_in_thread( - Path(self.vmtest_dir), "x86_64", self.kernels - ) as kernel_downloads: - if self.kernels: - self.announce("downloading kernels in the background", log.INFO) - - with github_workflow_group("Build extension"): - self.run_command("egg_info") - self.reinitialize_command("build_ext", inplace=1) - self.run_command("build_ext") - - passed = [] - failed = [] - - with github_workflow_group("Run unit tests"): - if self.kernels: - self.announce("running tests locally", log.INFO) - if self._run_local(): - passed.append("local") - else: - failed.append("local") - + try: + to_download = [] if self.kernels: - for kernel in kernel_downloads: - kernel_release = kernel.name - if kernel_release.startswith("kernel-"): - kernel_release = kernel_release[len("kernel-") :] - + to_download.append(DownloadCompiler(ARCHITECTURES["x86_64"])) + for pattern in self.kernels: + if not pattern.startswith(".") and not pattern.startswith("/"): + to_download.append( + DownloadKernel(ARCHITECTURES["x86_64"], pattern) + ) + with download_in_thread(Path(self.vmtest_dir), to_download) as downloads: + downloads_it = iter(downloads) + + if to_download: + logger.info("downloading kernels in the background") + + with github_workflow_group("Build extension"): + self.run_command("egg_info") + self.reinitialize_command("build_ext", inplace=1) + self.run_command("build_ext") + + passed = [] + failed = [] + + with github_workflow_group("Run unit tests"): + if self.kernels: + logger.info("running tests locally") + if self._run_local(): + passed.append("local") + else: + failed.append("local") + + for pattern in self.kernels: + if pattern.startswith(".") or pattern.startswith("/"): + kernel = local_kernel(ARCHITECTURES["x86_64"], Path(pattern)) + else: + while True: + kernel = next(downloads_it) + if isinstance(kernel, Kernel): + break with github_workflow_group( - f"Run integration tests on Linux {kernel_release}" + f"Run integration tests on Linux {kernel.release}" ): - if self._run_vm(kernel, kernel_release): - passed.append(kernel_release) + if self._run_vm(kernel): + passed.append(kernel.release) else: - failed.append(kernel_release) - - if passed: - self.announce(f'Passed: {", ".join(passed)}', log.INFO) - if failed: - self.announce(f'Failed: {", ".join(failed)}', log.ERROR) + failed.append(kernel.release) + + if passed: + logger.info("Passed: %s", ", ".join(passed)) + if failed: + logger.error("Failed: %s", ", ".join(failed)) + except urllib.error.HTTPError as e: + if e.code == 403: + print(e, file=sys.stderr) + print("Headers:", e.headers, file=sys.stderr) + print("Body:", e.read().decode(), file=sys.stderr) + raise if failed: - raise DistutilsError("some tests failed") + raise BaseError("some tests failed") else: - self.announce("all tests passed", log.INFO) + logger.info("all tests passed") def get_version(): @@ -367,7 +335,12 @@ def get_version(): # If this is a git repository, use a git-describe(1)-esque local version. # Otherwise, get the local version saved in the sdist. - if os.path.exists(".git"): + if os.path.exists(".git") and ( + subprocess.call( + ["git", "--git-dir=.git", "rev-parse"], stderr=subprocess.DEVNULL + ) + == 0 + ): # Read the Docs modifies the working tree (namely, docs/conf.py). We # don't want the documentation to display a dirty version, so ignore # modifications for RTD builds. @@ -389,7 +362,7 @@ def get_version(): ) ) except subprocess.CalledProcessError: - log.warn("warning: v%s tag not found", public_version) + logger.warning("warning: v%s tag not found", public_version) else: if count == 0: local_version = "+dirty" if dirty else "" @@ -403,7 +376,7 @@ def get_version(): else: if version_py is None: # This isn't a proper sdist (maybe a git archive). - log.warn("warning: drgn/internal/version.py not found") + logger.warning("warning: drgn/internal/version.py not found") else: # The saved version must start with the public version. match = re.search( @@ -414,7 +387,7 @@ def get_version(): if match: local_version = match.group(1) else: - log.warn("warning: drgn/internal/version.py is invalid") + logger.warning("warning: drgn/internal/version.py is invalid") version = public_version + local_version # Update version.py if necessary. @@ -444,7 +417,7 @@ def get_version(): "sdist": sdist, "test": test, }, - entry_points={"console_scripts": ["drgn=drgn.internal.cli:main"]}, + entry_points={"console_scripts": ["drgn=drgn.cli:_main"]}, python_requires=">=3.6", author="Omar Sandoval", author_email="osandov@osandov.com", @@ -456,12 +429,12 @@ def get_version(): "Bug Tracker": "https://github.com/osandov/drgn/issues", "Documentation": "https://drgn.readthedocs.io", }, - license="GPL-3.0-or-later", + license="LGPL-2.1-or-later", classifiers=[ "Development Status :: 3 - Alpha", "Environment :: Console", "Intended Audience :: Developers", - "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", + "License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3", "Topic :: Software Development :: Debuggers", diff --git a/tests/__init__.py b/tests/__init__.py index 8fa472bca..810bd8f33 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import functools from typing import Any, NamedTuple, Optional @@ -158,18 +158,21 @@ def _identical(a, b): ), ): return False - exc_a = exc_b = False - try: - value_a = a.value_() - except Exception: - exc_a = True - try: - value_b = b.value_() - except Exception: - exc_b = True - if exc_a != exc_b: - return False - return exc_a or _identical(value_a, value_b) + if a.address_ is None: + exc_a = exc_b = False + try: + value_a = a.value_() + except Exception: + exc_a = True + try: + value_b = b.value_() + except Exception: + exc_b = True + if exc_a != exc_b: + return False + return exc_a or _identical(value_a, value_b) + else: + return True elif isinstance(a, Type) and isinstance(b, Type): if a.qualifiers != b.qualifiers: return False diff --git a/tests/assembler.py b/tests/assembler.py index 94540e180..c1ac523e4 100644 --- a/tests/assembler.py +++ b/tests/assembler.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from collections import namedtuple diff --git a/tests/dwarf.py b/tests/dwarf.py index 521ce40d4..4c3cbdce6 100644 --- a/tests/dwarf.py +++ b/tests/dwarf.py @@ -1,21 +1,50 @@ -# Automatically generated from dwarf.h +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later +# Generated by scripts/gen_dwarf_constants.py. import enum from typing import Text +class DW_ACCESS(enum.IntEnum): + public = 0x1 + protected = 0x2 + private = 0x3 + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_ACCESS_{cls(value).name}" + except ValueError: + return hex(value) + + +class DW_ADDR(enum.IntEnum): + none = 0x0 + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_ADDR_{cls(value).name}" + except ValueError: + return hex(value) + + class DW_AT(enum.IntEnum): sibling = 0x1 location = 0x2 name = 0x3 # type: ignore ordering = 0x9 + subscr_data = 0xA byte_size = 0xB bit_offset = 0xC bit_size = 0xD + element_list = 0xF stmt_list = 0x10 low_pc = 0x11 high_pc = 0x12 language = 0x13 + member = 0x14 discr = 0x15 discr_value = 0x16 visibility = 0x17 @@ -34,6 +63,7 @@ class DW_AT(enum.IntEnum): return_addr = 0x2A start_scope = 0x2C bit_stride = 0x2E + stride_size = 0x2E upper_bound = 0x2F abstract_origin = 0x31 accessibility = 0x32 @@ -103,6 +133,7 @@ class DW_AT(enum.IntEnum): str_offsets_base = 0x72 addr_base = 0x73 rnglists_base = 0x74 + dwo_id = 0x75 dwo_name = 0x76 reference = 0x77 rvalue_reference = 0x78 @@ -126,6 +157,10 @@ class DW_AT(enum.IntEnum): deleted = 0x8A defaulted = 0x8B loclists_base = 0x8C + ghs_namespace_alias = 0x806 + ghs_using_namespace = 0x807 + ghs_using_declaration = 0x808 + HP_block_index = 0x2000 lo_user = 0x2000 MIPS_fde = 0x2001 MIPS_loop_begin = 0x2002 @@ -144,6 +179,40 @@ class DW_AT(enum.IntEnum): MIPS_allocatable_dopetype = 0x200F MIPS_assumed_shape_dopetype = 0x2010 MIPS_assumed_size = 0x2011 + HP_unmodifiable = 0x2001 + HP_prologue = 0x2005 + HP_epilogue = 0x2008 + HP_actuals_stmt_list = 0x2010 + HP_proc_per_section = 0x2011 + HP_raw_data_ptr = 0x2012 + HP_pass_by_reference = 0x2013 + HP_opt_level = 0x2014 + HP_prof_version_id = 0x2015 + HP_opt_flags = 0x2016 + HP_cold_region_low_pc = 0x2017 + HP_cold_region_high_pc = 0x2018 + HP_all_variables_modifiable = 0x2019 + HP_linkage_name = 0x201A + HP_prof_flags = 0x201B + HP_unit_name = 0x201F + HP_unit_size = 0x2020 + HP_widened_byte_size = 0x2021 + HP_definition_points = 0x2022 + HP_default_location = 0x2023 + HP_is_result_param = 0x2029 + CPQ_discontig_ranges = 0x2001 + CPQ_semantic_events = 0x2002 + CPQ_split_lifetimes_var = 0x2003 + CPQ_split_lifetimes_rtn = 0x2004 + CPQ_prologue_length = 0x2005 + ghs_mangled = 0x2007 + ghs_rsm = 0x2083 + ghs_frsm = 0x2085 + ghs_frames = 0x2086 + ghs_rso = 0x2087 + ghs_subcpu = 0x2092 + ghs_lbrace_line = 0x2093 + INTEL_other_endian = 0x2026 sf_names = 0x2101 src_info = 0x2102 mac_info = 0x2103 @@ -168,8 +237,6 @@ class DW_AT(enum.IntEnum): GNU_all_tail_call_sites = 0x2116 GNU_all_call_sites = 0x2117 GNU_all_source_call_sites = 0x2118 - GNU_locviews = 0x2137 - GNU_entry_view = 0x2138 GNU_macros = 0x2119 GNU_deleted = 0x211A GNU_dwo_name = 0x2130 @@ -178,9 +245,115 @@ class DW_AT(enum.IntEnum): GNU_addr_base = 0x2133 GNU_pubnames = 0x2134 GNU_pubtypes = 0x2135 + GNU_discriminator = 0x2136 + GNU_locviews = 0x2137 + GNU_entry_view = 0x2138 + GNU_bias = 0x2305 + SUN_template = 0x2201 + VMS_rtnbeg_pd_address = 0x2201 + SUN_alignment = 0x2202 + SUN_vtable = 0x2203 + SUN_count_guarantee = 0x2204 + SUN_command_line = 0x2205 + SUN_vbase = 0x2206 + SUN_compile_options = 0x2207 + SUN_language = 0x2208 + SUN_browser_file = 0x2209 + SUN_vtable_abi = 0x2210 + SUN_func_offsets = 0x2211 + SUN_cf_kind = 0x2212 + SUN_vtable_index = 0x2213 + SUN_omp_tpriv_addr = 0x2214 + SUN_omp_child_func = 0x2215 + SUN_func_offset = 0x2216 + SUN_memop_type_ref = 0x2217 + SUN_profile_id = 0x2218 + SUN_memop_signature = 0x2219 + SUN_obj_dir = 0x2220 + SUN_obj_file = 0x2221 + SUN_original_name = 0x2222 + SUN_hwcprof_signature = 0x2223 + SUN_amd64_parmdump = 0x2224 + SUN_part_link_name = 0x2225 + SUN_link_name = 0x2226 + SUN_pass_with_const = 0x2227 + SUN_return_with_const = 0x2228 + SUN_import_by_name = 0x2229 + SUN_f90_pointer = 0x222A + SUN_pass_by_ref = 0x222B + SUN_f90_allocatable = 0x222C + SUN_f90_assumed_shape_array = 0x222D + SUN_c_vla = 0x222E + SUN_return_value_ptr = 0x2230 + SUN_dtor_start = 0x2231 + SUN_dtor_length = 0x2232 + SUN_dtor_state_initial = 0x2233 + SUN_dtor_state_final = 0x2234 + SUN_dtor_state_deltas = 0x2235 + SUN_import_by_lname = 0x2236 + SUN_f90_use_only = 0x2237 + SUN_namelist_spec = 0x2238 + SUN_is_omp_child_func = 0x2239 + SUN_fortran_main_alias = 0x223A + SUN_fortran_based = 0x223B + ALTIUM_loclist = 0x2300 + use_GNAT_descriptive_type = 0x2301 + GNAT_descriptive_type = 0x2302 GNU_numerator = 0x2303 GNU_denominator = 0x2304 - GNU_bias = 0x2305 + go_kind = 0x2900 + go_key = 0x2901 + go_elem = 0x2902 + go_embedded_field = 0x2903 + go_runtime_type = 0x2904 + upc_threads_scaled = 0x3210 + IBM_wsa_addr = 0x393E + IBM_home_location = 0x393F + IBM_alt_srcview = 0x3940 + PGI_lbase = 0x3A00 + PGI_soffset = 0x3A01 + PGI_lstride = 0x3A02 + BORLAND_property_read = 0x3B11 + BORLAND_property_write = 0x3B12 + BORLAND_property_implements = 0x3B13 + BORLAND_property_index = 0x3B14 + BORLAND_property_default = 0x3B15 + BORLAND_Delphi_unit = 0x3B20 + BORLAND_Delphi_class = 0x3B21 + BORLAND_Delphi_record = 0x3B22 + BORLAND_Delphi_metaclass = 0x3B23 + BORLAND_Delphi_constructor = 0x3B24 + BORLAND_Delphi_destructor = 0x3B25 + BORLAND_Delphi_anonymous_method = 0x3B26 + BORLAND_Delphi_interface = 0x3B27 + BORLAND_Delphi_ABI = 0x3B28 + BORLAND_Delphi_frameptr = 0x3B30 + BORLAND_closure = 0x3B31 + LLVM_include_path = 0x3E00 + LLVM_config_macros = 0x3E01 + LLVM_sysroot = 0x3E02 + LLVM_tag_offset = 0x3E03 + LLVM_apinotes = 0x3E07 + LLVM_active_lane = 0x3E08 + LLVM_augmentation = 0x3E09 + LLVM_lanes = 0x3E0A + LLVM_lane_pc = 0x3E0B + LLVM_vector_size = 0x3E0C + APPLE_optimized = 0x3FE1 + APPLE_flags = 0x3FE2 + APPLE_isa = 0x3FE3 + APPLE_block = 0x3FE4 + APPLE_major_runtime_vers = 0x3FE5 + APPLE_runtime_class = 0x3FE6 + APPLE_omit_frame_ptr = 0x3FE7 + APPLE_property_name = 0x3FE8 + APPLE_property_getter = 0x3FE9 + APPLE_property_setter = 0x3FEA + APPLE_property_attribute = 0x3FEB + APPLE_objc_complete_type = 0x3FEC + APPLE_property = 0x3FED + APPLE_objc_direct = 0x3FEE + APPLE_sdk = 0x3FEF hi_user = 0x3FFF @classmethod @@ -192,7 +365,6 @@ def str(cls, value: int) -> Text: class DW_ATE(enum.IntEnum): - void = 0x0 address = 0x1 boolean = 0x2 complex_float = 0x3 @@ -211,7 +383,27 @@ class DW_ATE(enum.IntEnum): UTF = 0x10 UCS = 0x11 ASCII = 0x12 + ALTIUM_fract = 0x80 lo_user = 0x80 + ALTIUM_accum = 0x81 + HP_float80 = 0x80 + HP_complex_float80 = 0x81 + HP_float128 = 0x82 + HP_complex_float128 = 0x83 + HP_floathpintel = 0x84 + HP_imaginary_float80 = 0x85 + HP_imaginary_float128 = 0x86 + HP_VAX_float = 0x88 + HP_VAX_float_d = 0x89 + HP_packed_decimal = 0x8A + HP_zoned_decimal = 0x8B + HP_edited = 0x8C + HP_signed_fixed = 0x8D + HP_unsigned_fixed = 0x8E + HP_VAX_complex_float = 0x8F + HP_VAX_complex_float_d = 0x90 + SUN_interval_float = 0x91 + SUN_imaginary_float = 0x92 hi_user = 0xFF @classmethod @@ -222,6 +414,95 @@ def str(cls, value: int) -> Text: return hex(value) +class DW_CC(enum.IntEnum): + normal = 0x1 + program = 0x2 + nocall = 0x3 + pass_by_reference = 0x4 + pass_by_value = 0x5 + lo_user = 0x40 + GNU_renesas_sh = 0x40 + GNU_borland_fastcall_i386 = 0x41 + ALTIUM_interrupt = 0x65 + ALTIUM_near_system_stack = 0x66 + ALTIUM_near_user_stack = 0x67 + ALTIUM_huge_user_stack = 0x68 + GNU_BORLAND_safecall = 0xB0 + GNU_BORLAND_stdcall = 0xB1 + GNU_BORLAND_pascal = 0xB2 + GNU_BORLAND_msfastcall = 0xB3 + GNU_BORLAND_msreturn = 0xB4 + GNU_BORLAND_thiscall = 0xB5 + GNU_BORLAND_fastcall = 0xB6 + LLVM_vectorcall = 0xC0 + LLVM_Win64 = 0xC1 + LLVM_X86_64SysV = 0xC2 + LLVM_AAPCS = 0xC3 + LLVM_AAPCS_VFP = 0xC4 + LLVM_IntelOclBicc = 0xC5 + LLVM_SpirFunction = 0xC6 + LLVM_OpenCLKernel = 0xC7 + LLVM_Swift = 0xC8 + LLVM_PreserveMost = 0xC9 + LLVM_PreserveAll = 0xCA + LLVM_X86RegCall = 0xCB + GDB_IBM_OpenCL = 0xFF + hi_user = 0xFF + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_CC_{cls(value).name}" + except ValueError: + return hex(value) + + +class DW_CFA(enum.IntEnum): + advance_loc = 0x40 + offset = 0x80 + restore = 0xC0 + nop = 0x0 + set_loc = 0x1 + advance_loc1 = 0x2 + advance_loc2 = 0x3 + advance_loc4 = 0x4 + offset_extended = 0x5 + restore_extended = 0x6 + undefined = 0x7 + same_value = 0x8 + register = 0x9 + remember_state = 0xA + restore_state = 0xB + def_cfa = 0xC + def_cfa_register = 0xD + def_cfa_offset = 0xE + def_cfa_expression = 0xF + expression = 0x10 + offset_extended_sf = 0x11 + def_cfa_sf = 0x12 + def_cfa_offset_sf = 0x13 + val_offset = 0x14 + val_offset_sf = 0x15 + val_expression = 0x16 + lo_user = 0x1C + MIPS_advance_loc8 = 0x1D + GNU_window_save = 0x2D + AARCH64_negate_ra_state = 0x2D + GNU_args_size = 0x2E + GNU_negative_offset_extended = 0x2F + LLVM_def_aspace_cfa = 0x30 + LLVM_def_aspace_cfa_sf = 0x31 + METAWARE_info = 0x34 + hi_user = 0x3F + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_CFA_{cls(value).name}" + except ValueError: + return hex(value) + + class DW_CHILDREN(enum.IntEnum): no = 0x0 yes = 0x1 @@ -234,6 +515,73 @@ def str(cls, value: int) -> Text: return hex(value) +class DW_DEFAULTED(enum.IntEnum): + no = 0x0 + in_class = 0x1 + out_of_class = 0x2 + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_DEFAULTED_{cls(value).name}" + except ValueError: + return hex(value) + + +class DW_DS(enum.IntEnum): + unsigned = 0x1 + leading_overpunch = 0x2 + trailing_overpunch = 0x3 + leading_separate = 0x4 + trailing_separate = 0x5 + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_DS_{cls(value).name}" + except ValueError: + return hex(value) + + +class DW_DSC(enum.IntEnum): + label = 0x0 + range = 0x1 + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_DSC_{cls(value).name}" + except ValueError: + return hex(value) + + +class DW_EH_PE(enum.IntEnum): + absptr = 0x0 + uleb128 = 0x1 + udata2 = 0x2 + udata4 = 0x3 + udata8 = 0x4 + sleb128 = 0x9 + sdata2 = 0xA + sdata4 = 0xB + sdata8 = 0xC + signed = 0x8 + pcrel = 0x10 + textrel = 0x20 + datarel = 0x30 + funcrel = 0x40 + aligned = 0x50 + indirect = 0x80 + omit = 0xFF + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_EH_PE_{cls(value).name}" + except ValueError: + return hex(value) + + class DW_END(enum.IntEnum): default = 0x0 big = 0x1 @@ -297,6 +645,7 @@ class DW_FORM(enum.IntEnum): GNU_str_index = 0x1F02 GNU_ref_alt = 0x1F20 GNU_strp_alt = 0x1F21 + LLVM_addrx_offset = 0x2001 @classmethod def str(cls, value: int) -> Text: @@ -306,6 +655,53 @@ def str(cls, value: int) -> Text: return hex(value) +class DW_ID(enum.IntEnum): + case_sensitive = 0x0 + up_case = 0x1 + down_case = 0x2 + case_insensitive = 0x3 + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_ID_{cls(value).name}" + except ValueError: + return hex(value) + + +class DW_IDX(enum.IntEnum): + compile_unit = 0x1 + type_unit = 0x2 + die_offset = 0x3 + parent = 0x4 + type_hash = 0x5 + GNU_internal = 0x2000 + lo_user = 0x2000 + GNU_external = 0x2001 + hi_user = 0x3FFF + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_IDX_{cls(value).name}" + except ValueError: + return hex(value) + + +class DW_INL(enum.IntEnum): + not_inlined = 0x0 + inlined = 0x1 + declared_not_inlined = 0x2 + declared_inlined = 0x3 + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_INL_{cls(value).name}" + except ValueError: + return hex(value) + + class DW_LANG(enum.IntEnum): C89 = 0x1 C = 0x2 @@ -346,6 +742,11 @@ class DW_LANG(enum.IntEnum): BLISS = 0x25 lo_user = 0x8000 Mips_Assembler = 0x8001 + Upc = 0x8765 + GOOGLE_RenderScript = 0x8001 + ALTIUM_Assembler = 0x9101 + BORLAND_Delphi = 0xB000 + SUN_Assembler = 0x9001 hi_user = 0xFFFF @classmethod @@ -356,11 +757,63 @@ def str(cls, value: int) -> Text: return hex(value) +class DW_LLE(enum.IntEnum): + end_of_list = 0x0 + base_addressx = 0x1 + startx_endx = 0x2 + startx_length = 0x3 + offset_pair = 0x4 + default_location = 0x5 + base_address = 0x6 + start_end = 0x7 + start_length = 0x8 + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_LLE_{cls(value).name}" + except ValueError: + return hex(value) + + +class DW_LNCT(enum.IntEnum): + path = 0x1 + directory_index = 0x2 + timestamp = 0x3 + size = 0x4 + MD5 = 0x5 + GNU_subprogram_name = 0x6 + GNU_decl_file = 0x7 + GNU_decl_line = 0x8 + lo_user = 0x2000 + LLVM_source = 0x2001 + LLVM_is_MD5 = 0x2002 + hi_user = 0x3FFF + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_LNCT_{cls(value).name}" + except ValueError: + return hex(value) + + class DW_LNE(enum.IntEnum): end_sequence = 0x1 set_address = 0x2 define_file = 0x3 set_discriminator = 0x4 + HP_negate_is_UV_update = 0x11 + HP_push_context = 0x12 + HP_pop_context = 0x13 + HP_set_file_line_column = 0x14 + HP_set_routine_name = 0x15 + HP_set_sequence = 0x16 + HP_negate_post_semantics = 0x17 + HP_negate_function_exit = 0x18 + HP_negate_front_end_logical = 0x19 + HP_define_proc = 0x20 + HP_source_file_correlation = 0x80 lo_user = 0x80 hi_user = 0xFF @@ -385,6 +838,10 @@ class DW_LNS(enum.IntEnum): set_prologue_end = 0xA set_epilogue_begin = 0xB set_isa = 0xC + set_address_from_logical = 0xD + set_subprogram = 0xD + inlined_call = 0xE + pop_context = 0xF @classmethod def str(cls, value: int) -> Text: @@ -394,6 +851,45 @@ def str(cls, value: int) -> Text: return hex(value) +class DW_MACINFO(enum.IntEnum): + define = 0x1 + undef = 0x2 + start_file = 0x3 + end_file = 0x4 + vendor_ext = 0xFF + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_MACINFO_{cls(value).name}" + except ValueError: + return hex(value) + + +class DW_MACRO(enum.IntEnum): + define = 0x1 + undef = 0x2 + start_file = 0x3 + end_file = 0x4 + define_strp = 0x5 + undef_strp = 0x6 + import_ = 0x7 + define_sup = 0x8 + undef_sup = 0x9 + import_sup = 0xA + define_strx = 0xB + undef_strx = 0xC + lo_user = 0xE0 + hi_user = 0xFF + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_MACRO_{cls(value).name}" + except ValueError: + return hex(value) + + class DW_OP(enum.IntEnum): addr = 0x3 deref = 0x6 @@ -560,7 +1056,31 @@ class DW_OP(enum.IntEnum): convert = 0xA8 reinterpret = 0xA9 GNU_push_tls_address = 0xE0 + WASM_location = 0xED + WASM_location_int = 0xEE + lo_user = 0xE0 + LLVM_form_aspace_address = 0xE1 + LLVM_push_lane = 0xE2 + LLVM_offset = 0xE3 + LLVM_offset_uconst = 0xE4 + LLVM_bit_offset = 0xE5 + LLVM_call_frame_entry_reg = 0xE6 + LLVM_undefined = 0xE7 + LLVM_aspace_bregx = 0xE8 + LLVM_aspace_implicit_pointer = 0xE9 + LLVM_piece_end = 0xEA + LLVM_extend = 0xEB + LLVM_select_bit_piece = 0xEC + HP_unknown = 0xE0 + HP_is_value = 0xE1 + HP_fltconst4 = 0xE2 + HP_fltconst8 = 0xE3 + HP_mod_range = 0xE4 + HP_unmod_range = 0xE5 + HP_tls = 0xE6 + INTEL_bit_piece = 0xE8 GNU_uninit = 0xF0 + APPLE_uninit = 0xF0 GNU_encoded_addr = 0xF1 GNU_implicit_pointer = 0xF2 GNU_entry_value = 0xF3 @@ -573,7 +1093,7 @@ class DW_OP(enum.IntEnum): GNU_addr_index = 0xFB GNU_const_index = 0xFC GNU_variable_value = 0xFD - lo_user = 0xE0 + PGI_omp_thread_num = 0xF8 hi_user = 0xFF @classmethod @@ -584,6 +1104,54 @@ def str(cls, value: int) -> Text: return hex(value) +class DW_ORD(enum.IntEnum): + row_major = 0x0 + col_major = 0x1 + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_ORD_{cls(value).name}" + except ValueError: + return hex(value) + + +class DW_RLE(enum.IntEnum): + end_of_list = 0x0 + base_addressx = 0x1 + startx_endx = 0x2 + startx_length = 0x3 + offset_pair = 0x4 + base_address = 0x5 + start_end = 0x6 + start_length = 0x7 + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_RLE_{cls(value).name}" + except ValueError: + return hex(value) + + +class DW_SECT(enum.IntEnum): + INFO = 0x1 + TYPES = 0x2 + ABBREV = 0x3 + LINE = 0x4 + LOCLISTS = 0x5 + STR_OFFSETS = 0x6 + MACRO = 0x7 + RNGLISTS = 0x8 + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_SECT_{cls(value).name}" + except ValueError: + return hex(value) + + class DW_TAG(enum.IntEnum): array_type = 0x1 class_type = 0x2 @@ -640,6 +1208,7 @@ class DW_TAG(enum.IntEnum): unspecified_type = 0x3B partial_unit = 0x3C imported_unit = 0x3D + mutable_type = 0x3E condition = 0x3F shared_type = 0x40 type_unit = 0x41 @@ -655,6 +1224,7 @@ class DW_TAG(enum.IntEnum): immutable_type = 0x4B lo_user = 0x4080 MIPS_loop = 0x4081 + HP_array_descriptor = 0x4090 format_label = 0x4101 function_template = 0x4102 class_template = 0x4103 @@ -665,6 +1235,39 @@ class DW_TAG(enum.IntEnum): GNU_formal_parameter_pack = 0x4108 GNU_call_site = 0x4109 GNU_call_site_parameter = 0x410A + SUN_function_template = 0x4201 + SUN_class_template = 0x4202 + SUN_struct_template = 0x4203 + SUN_union_template = 0x4204 + SUN_indirect_inheritance = 0x4205 + SUN_codeflags = 0x4206 + SUN_memop_info = 0x4207 + SUN_omp_child_func = 0x4208 + SUN_rtti_descriptor = 0x4209 + SUN_dtor_info = 0x420A + SUN_dtor = 0x420B + SUN_f90_interface = 0x420C + SUN_fortran_vax_structure = 0x420D + SUN_hi = 0x42FF + ALTIUM_circ_type = 0x5101 + ALTIUM_mwa_circ_type = 0x5102 + ALTIUM_rev_carry_type = 0x5103 + ALTIUM_rom = 0x5111 + LLVM_annotation = 0x6000 + ghs_namespace = 0x8004 + ghs_using_namespace = 0x8005 + ghs_using_declaration = 0x8006 + ghs_template_templ_param = 0x8007 + upc_shared_type = 0x8765 + upc_strict_type = 0x8766 + upc_relaxed_type = 0x8767 + PGI_kanji_type = 0xA000 + PGI_interface_block = 0xA020 + BORLAND_property = 0xB000 + BORLAND_Delphi_string = 0xB001 + BORLAND_Delphi_dynamic_array = 0xB002 + BORLAND_Delphi_set = 0xB003 + BORLAND_Delphi_variant = 0xB004 hi_user = 0xFFFF @classmethod @@ -673,3 +1276,47 @@ def str(cls, value: int) -> Text: return f"DW_TAG_{cls(value).name}" except ValueError: return hex(value) + + +class DW_UT(enum.IntEnum): + compile = 0x1 + type = 0x2 + partial = 0x3 + skeleton = 0x4 + split_compile = 0x5 + split_type = 0x6 + lo_user = 0x80 + hi_user = 0xFF + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_UT_{cls(value).name}" + except ValueError: + return hex(value) + + +class DW_VIRTUALITY(enum.IntEnum): + none = 0x0 + virtual = 0x1 + pure_virtual = 0x2 + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_VIRTUALITY_{cls(value).name}" + except ValueError: + return hex(value) + + +class DW_VIS(enum.IntEnum): + local = 0x1 + exported = 0x2 + qualified = 0x3 + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"DW_VIS_{cls(value).name}" + except ValueError: + return hex(value) diff --git a/tests/dwarfwriter.py b/tests/dwarfwriter.py index 33c4e2a2d..619a53a72 100644 --- a/tests/dwarfwriter.py +++ b/tests/dwarfwriter.py @@ -1,17 +1,19 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later +from collections import OrderedDict import os.path from typing import Any, NamedTuple, Optional, Sequence, Union +import zlib from tests.assembler import _append_sleb128, _append_uleb128 -from tests.dwarf import DW_AT, DW_FORM, DW_TAG +from tests.dwarf import DW_AT, DW_FORM, DW_LNCT, DW_TAG, DW_UT from tests.elf import ET, SHT from tests.elfwriter import ElfSection, create_elf_file class DwarfAttrib(NamedTuple): - name: str + name: DW_AT form: DW_FORM value: Any @@ -24,11 +26,17 @@ class DwarfDie(NamedTuple): tag: DW_TAG attribs: Sequence[DwarfAttrib] children: Sequence[Union["DwarfDie", DwarfLabel]] = () + + +class DwarfUnit(NamedTuple): + type: DW_UT + die: DwarfDie + dwo_id: Optional[int] = None type_signature: Optional[int] = None type_offset: Optional[str] = None -def _compile_debug_abbrev(unit_dies, use_dw_form_indirect): +def _compile_debug_abbrev(units, use_dw_form_indirect): buf = bytearray() code = 1 @@ -51,13 +59,13 @@ def aux(die): for child in die.children: aux(child) - for die in unit_dies: - aux(die) + for unit in units: + aux(unit.die) buf.append(0) return buf -def _compile_debug_info(unit_dies, little_endian, bits, use_dw_form_indirect): +def _compile_debug_info(units, little_endian, bits, version, use_dw_form_indirect): byteorder = "little" if little_endian else "big" all_labels = set() labels = {} @@ -130,25 +138,37 @@ def aux(buf, die, depth): debug_info = bytearray() debug_types = bytearray() - for die in unit_dies: + for unit in units: labels.clear() relocations.clear() - buf = debug_info if die.tag == DW_TAG.compile_unit else debug_types + decl_file = 1 + if version == 4 and unit.type in (DW_UT.type, DW_UT.split_type): + buf = debug_types + else: + buf = debug_info orig_len = len(buf) buf.extend(b"\0\0\0\0") # unit_length - buf.extend((4).to_bytes(2, byteorder)) # version + buf.extend(version.to_bytes(2, byteorder)) # version + if version >= 5: + buf.append(unit.type) # unit_type + buf.append(bits // 8) # address_size buf.extend((0).to_bytes(4, byteorder)) # debug_abbrev_offset - buf.append(bits // 8) # address_size + if version < 5: + buf.append(bits // 8) # address_size - if die.tag == DW_TAG.type_unit: - buf.extend(die.type_signature.to_bytes(8, byteorder)) - relocations.append((len(buf), die.type_offset)) + if version >= 5 and unit.type in (DW_UT.skeleton, DW_UT.split_compile): + buf.extend(unit.dwo_id.to_bytes(8, byteorder)) # dwo_id + else: + assert unit.dwo_id is None + if unit.type in (DW_UT.type, DW_UT.split_type): + buf.extend(unit.type_signature.to_bytes(8, byteorder)) # type_signature + relocations.append((len(buf), unit.type_offset)) buf.extend(b"\0\0\0\0") # type_offset else: - assert die.type_signature is None - assert die.type_offset is None + assert unit.type_signature is None + assert unit.type_offset is None - aux(buf, die, 0) + aux(buf, unit.die, 0) unit_length = len(buf) - orig_len - 4 buf[orig_len : orig_len + 4] = unit_length.to_bytes(4, byteorder) @@ -159,72 +179,119 @@ def aux(buf, die, depth): return debug_info, debug_types -def _compile_debug_line(unit_dies, little_endian): - buf = bytearray() +def _compile_debug_line(units, little_endian, bits, version): byteorder = "little" if little_endian else "big" - buf.extend(b"\0\0\0\0") # unit_length - buf.extend((4).to_bytes(2, byteorder)) # version - buf.extend(b"\0\0\0\0") # header_length - buf.append(1) # minimum_instruction_length - buf.append(1) # maximum_operations_per_instruction - buf.append(1) # default_is_stmt - buf.append(1) # line_base - buf.append(1) # line_range - buf.append(1) # opcode_base - # Don't need standard_opcode_length - - def compile_include_directories(die): - if isinstance(die, DwarfLabel): - return - for attrib in die.attribs: - if attrib.name != DW_AT.decl_file: - continue - dirname = os.path.dirname(attrib.value) - if dirname: - buf.extend(dirname.encode("ascii")) - buf.append(0) - if die.children: + if not units: + units = [DwarfUnit(DW_UT.compile, DwarfDie(DW_TAG.compile_unit, []))] + + buf = bytearray() + for unit in units: + unit.die.attribs.append( + DwarfAttrib(DW_AT.stmt_list, DW_FORM.sec_offset, len(buf)) + ) + if unit.type in (DW_UT.compile, DW_UT.partial, DW_UT.skeleton): + unit.die.attribs.append(DwarfAttrib(DW_AT.name, DW_FORM.string, "main.c")) + unit.die.attribs.append( + DwarfAttrib(DW_AT.comp_dir, DW_FORM.string, "/usr/src") + ) + + unit_length_start = len(buf) + buf.extend(b"\0\0\0\0") # unit_length + unit_length_end = len(buf) + buf.extend(version.to_bytes(2, byteorder)) # version + if version >= 5: + buf.append(bits // 8) # address_size + buf.append(0) # segment_selector_size + header_length_start = len(buf) + buf.extend(b"\0\0\0\0") # header_length + header_length_end = len(buf) + buf.append(1) # minimum_instruction_length + buf.append(1) # maximum_operations_per_instruction + buf.append(1) # default_is_stmt + buf.append(1) # line_base + buf.append(1) # line_range + buf.append(1) # opcode_base + # Don't need standard_opcode_lengths + if version >= 5: + buf.append(1) # directory_entry_format_count + # directory_entry_format + _append_uleb128(buf, DW_LNCT.path) + _append_uleb128(buf, DW_FORM.string) + + directories = OrderedDict([("/usr/src", 0)]) + + def collect_directories(die): + if isinstance(die, DwarfLabel): + return + for attrib in die.attribs: + if attrib.name != DW_AT.decl_file: + continue + dirname = os.path.dirname(attrib.value) + if dirname: + directories.setdefault(dirname, len(directories)) for child in die.children: - compile_include_directories(child) + collect_directories(child) - for die in unit_dies: - compile_include_directories(die) - buf.append(0) + collect_directories(unit.die) - decl_file = 1 - directory = 1 + if version >= 5: + _append_uleb128(buf, len(directories)) # directories_count - def compile_file_names(die): - if isinstance(die, DwarfLabel): - return - nonlocal decl_file, directory - for attrib in die.attribs: - if attrib.name != DW_AT.decl_file: - continue - dirname, basename = os.path.split(attrib.value) - buf.extend(basename.encode("ascii")) + # directories (or include_directories in version <= 4) + for directory, index in directories.items(): + if index > 0 or version >= 5: + buf.extend(directory.encode("ascii")) + buf.append(0) + if version < 5: buf.append(0) - # directory index - if dirname: - _append_uleb128(buf, directory) - directory += 1 - else: - _append_uleb128(buf, 0) - _append_uleb128(buf, 0) # mtime - _append_uleb128(buf, 0) # size - if die.children: + + if version >= 5: + buf.append(2) # file_name_entry_format_count + # file_name_entry_format + _append_uleb128(buf, DW_LNCT.path) + _append_uleb128(buf, DW_FORM.string) + _append_uleb128(buf, DW_LNCT.directory_index) + _append_uleb128(buf, DW_FORM.udata) + + file_names = [("main.c", 0)] + + def collect_file_names(die): + if isinstance(die, DwarfLabel): + return + for attrib in die.attribs: + if attrib.name != DW_AT.decl_file: + continue + dirname, basename = os.path.split(attrib.value) + directory_index = directories[dirname] if dirname else 0 + file_names.append((basename, directory_index)) for child in die.children: - compile_file_names(child) + collect_file_names(child) - for die in unit_dies: - compile_file_names(die) - buf.append(0) + collect_file_names(unit.die) + + if version >= 5: + _append_uleb128(buf, len(file_names)) # file_names_count - unit_length = len(buf) - 4 - buf[:4] = unit_length.to_bytes(4, byteorder) - header_length = unit_length - 6 - buf[6:10] = header_length.to_bytes(4, byteorder) + # file_names + for path, directory_index in file_names[0 if version >= 5 else 1 :]: + # path + buf.extend(path.encode("ascii")) + buf.append(0) + _append_uleb128(buf, directory_index) # directory_index + if version < 5: + _append_uleb128(buf, 0) # mtime + _append_uleb128(buf, 0) # size + + if version < 5: + buf.append(0) + + buf[unit_length_start:unit_length_end] = (len(buf) - unit_length_end).to_bytes( + unit_length_end - unit_length_start, byteorder + ) + buf[header_length_start:header_length_end] = ( + len(buf) - header_length_end + ).to_bytes(header_length_end - header_length_start, byteorder) return buf @@ -232,60 +299,99 @@ def compile_file_names(die): def dwarf_sections( - dies, little_endian=True, bits=64, *, lang=None, use_dw_form_indirect=False + units_or_dies, + little_endian=True, + bits=64, + *, + version=4, + lang=None, + use_dw_form_indirect=False, + compress=None, + split=None, ): - if isinstance(dies, DwarfDie): - dies = (dies,) - assert all(isinstance(die, (DwarfDie, DwarfLabel)) for die in dies) + assert compress in (None, "zlib-gnu", "zlib-gabi") + assert split in (None, "dwo") - if any(isinstance(die, DwarfDie) and die.tag in _UNIT_TAGS for die in dies): - assert all(isinstance(die, DwarfLabel) or die.tag in _UNIT_TAGS for die in dies) - unit_dies = dies + if isinstance(units_or_dies, (DwarfDie, DwarfUnit)): + units_or_dies = (units_or_dies,) + if not units_or_dies or isinstance(units_or_dies[0], DwarfUnit): + units = units_or_dies else: - unit_dies = (DwarfDie(DW_TAG.compile_unit, (), dies),) + assert all(isinstance(die, (DwarfDie, DwarfLabel)) for die in units_or_dies) + assert all( + not isinstance(die, DwarfDie) or die.tag not in _UNIT_TAGS + for die in units_or_dies + ) + units = ( + DwarfUnit(DW_UT.compile, DwarfDie(DW_TAG.compile_unit, (), units_or_dies)), + ) + assert all(isinstance(unit, DwarfUnit) for unit in units) + assert all(unit.die.tag in _UNIT_TAGS for unit in units) - unit_attribs = [DwarfAttrib(DW_AT.stmt_list, DW_FORM.sec_offset, 0)] + unit_attribs = [] if lang is not None: unit_attribs.append(DwarfAttrib(DW_AT.language, DW_FORM.data1, lang)) - cu_attribs = unit_attribs + [ - DwarfAttrib(DW_AT.comp_dir, DW_FORM.string, "/usr/src") - ] - unit_dies = [ - die._replace( - attribs=list(die.attribs) - + (cu_attribs if die.tag == DW_TAG.compile_unit else unit_attribs) + units = [ + unit._replace( + die=unit.die._replace(attribs=list(unit.die.attribs) + unit_attribs) ) - for die in unit_dies + for unit in units ] + # TODO: line number information for a split file is in the skeleton file. + # We don't have any test cases yet that use line number information from a + # split file, but when we do, we'll have to add a way to include the split + # file's line number information in the skeleton file. + if not split: + debug_line = _compile_debug_line(units, little_endian, bits, version) + debug_info, debug_types = _compile_debug_info( - unit_dies, little_endian, bits, use_dw_form_indirect + units, little_endian, bits, version, use_dw_form_indirect ) - sections = [ - ElfSection( - name=".debug_abbrev", + def debug_section(name, data): + assert name.startswith(".debug") + if compress == "zlib-gnu": + name = ".z" + name[1:] + compressed_data = bytearray(b"ZLIB") + compressed_data.extend(len(data).to_bytes(8, "big")) + compressed_data.extend(zlib.compress(data)) + data = compressed_data + if split: + name += ".dwo" + return ElfSection( + name=name, sh_type=SHT.PROGBITS, - data=_compile_debug_abbrev(unit_dies, use_dw_form_indirect), - ), - ElfSection(name=".debug_info", sh_type=SHT.PROGBITS, data=debug_info), - ElfSection( - name=".debug_line", - sh_type=SHT.PROGBITS, - data=_compile_debug_line(unit_dies, little_endian), + data=data, + compressed=(compress == "zlib-gabi"), + ) + return name + + sections = [ + debug_section( + ".debug_abbrev", _compile_debug_abbrev(units, use_dw_form_indirect) ), - ElfSection(name=".debug_str", sh_type=SHT.PROGBITS, data=b"\0"), + debug_section(".debug_info", debug_info), + debug_section(".debug_str", b"\0"), ] + if not split: + sections.append(debug_section(".debug_line", debug_line)) if debug_types: - sections.append( - ElfSection(name=".debug_types", sh_type=SHT.PROGBITS, data=debug_types) - ) + sections.append(debug_section(".debug_types", debug_types)) return sections def compile_dwarf( - dies, little_endian=True, bits=64, *, lang=None, use_dw_form_indirect=False + dies, + little_endian=True, + bits=64, + *, + version=4, + lang=None, + use_dw_form_indirect=False, + compress=None, + split=None, ): return create_elf_file( ET.EXEC, @@ -293,8 +399,11 @@ def compile_dwarf( dies, little_endian=little_endian, bits=bits, + version=version, lang=lang, use_dw_form_indirect=use_dw_form_indirect, + compress=compress, + split=split, ), little_endian=little_endian, bits=bits, diff --git a/tests/elf.py b/tests/elf.py index 0c82c7a37..636e93eae 100644 --- a/tests/elf.py +++ b/tests/elf.py @@ -1,4 +1,6 @@ -# Automatically generated from elf.h +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later +# Generated by scripts/gen_tests_elf_py.py. import enum from typing import Text @@ -61,6 +63,48 @@ def str(cls, value: int) -> Text: return hex(value) +class SHF(enum.IntEnum): + WRITE = 0x1 + ALLOC = 0x2 + EXECINSTR = 0x4 + MERGE = 0x10 + STRINGS = 0x20 + INFO_LINK = 0x40 + LINK_ORDER = 0x80 + OS_NONCONFORMING = 0x100 + GROUP = 0x200 + TLS = 0x400 + COMPRESSED = 0x800 + MASKOS = 0xFF00000 + MASKPROC = 0xF0000000 + GNU_RETAIN = 0x200000 + ORDERED = 0x40000000 + EXCLUDE = 0x80000000 + MIPS_GPREL = 0x10000000 + MIPS_MERGE = 0x20000000 + MIPS_ADDR = 0x40000000 + MIPS_STRINGS = 0x80000000 + MIPS_NOSTRIP = 0x8000000 + MIPS_LOCAL = 0x4000000 + MIPS_NAMES = 0x2000000 + MIPS_NODUPE = 0x1000000 + PARISC_SHORT = 0x20000000 + PARISC_HUGE = 0x40000000 + PARISC_SBP = 0x80000000 + ALPHA_GPREL = 0x10000000 + ARM_ENTRYSECT = 0x10000000 + ARM_COMDEF = 0x80000000 + IA_64_SHORT = 0x10000000 + IA_64_NORECOV = 0x20000000 + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"SHF_{cls(value).name}" + except ValueError: + return hex(value) + + class SHN(enum.IntEnum): UNDEF = 0x0 LORESERVE = 0xFF00 @@ -108,7 +152,8 @@ class SHT(enum.IntEnum): PREINIT_ARRAY = 0x10 GROUP = 0x11 SYMTAB_SHNDX = 0x12 - NUM = 0x13 + RELR = 0x13 + NUM = 0x14 LOOS = 0x60000000 GNU_ATTRIBUTES = 0x6FFFFFF5 GNU_HASH = 0x6FFFFFF6 diff --git a/tests/elfwriter.py b/tests/elfwriter.py index 32e9ef1b0..c8f80dc4a 100644 --- a/tests/elfwriter.py +++ b/tests/elfwriter.py @@ -1,10 +1,11 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import struct from typing import List, NamedTuple, Optional, Sequence +import zlib -from tests.elf import ET, PT, SHN, SHT, STB, STT, STV +from tests.elf import ET, PT, SHF, SHN, SHT, STB, STT, STV class ElfSection: @@ -21,14 +22,16 @@ def __init__( sh_link: int = 0, sh_info: int = 0, sh_entsize: int = 0, + compressed=False, ): self.data = data self.name = name self.sh_type = sh_type + self.sh_flags = SHF.COMPRESSED if compressed else 0 self.p_type = p_type self.vaddr = vaddr self.paddr = paddr - self.memsz = len(self.data) if memsz is None else memsz + self.memsz = memsz self.p_align = p_align self.sh_link = sh_link self.sh_info = sh_info @@ -36,6 +39,7 @@ def __init__( assert (self.name is not None) or (self.p_type is not None) assert (self.name is None) == (self.sh_type is None) + assert self.p_type is None or not compressed class ElfSymbol(NamedTuple): @@ -122,12 +126,14 @@ def create_elf_file( ehdr_struct = struct.Struct(endian + "16BHHIQQQIHHHHHH") shdr_struct = struct.Struct(endian + "IIQQQQIIQQ") phdr_struct = struct.Struct(endian + "IIQQQQQQ") + chdr_struct = struct.Struct(endian + "IIQQ") e_machine = 62 if little_endian else 43 # EM_X86_64 or EM_SPARCV9 else: assert bits == 32 ehdr_struct = struct.Struct(endian + "16BHHIIIIIHHHHHH") shdr_struct = struct.Struct(endian + "10I") phdr_struct = struct.Struct(endian + "8I") + chdr_struct = struct.Struct(endian + "III") e_machine = 3 if little_endian else 8 # EM_386 or EM_MIPS sections = list(sections) @@ -189,6 +195,15 @@ def create_elf_file( shdr_offset += shdr_struct.size for section in sections: + ch_addralign = 1 if section.p_type is None else bits // 8 + memsz = len(section.data) if section.memsz is None else section.memsz + if section.sh_flags & SHF.COMPRESSED: + sh_addralign = bits // 8 + compressed_data = zlib.compress(section.data) + sh_size = chdr_struct.size + len(compressed_data) + else: + sh_addralign = ch_addralign + sh_size = memsz if section.p_align: padding = section.vaddr % section.p_align - len(buf) % section.p_align buf.extend(bytes(padding)) @@ -198,13 +213,13 @@ def create_elf_file( shdr_offset, shstrtab.index(section.name.encode()), # sh_name section.sh_type, # sh_type - 0, # sh_flags + section.sh_flags, # sh_flags section.vaddr, # sh_addr len(buf), # sh_offset - section.memsz, # sh_size + sh_size, # sh_size section.sh_link, # sh_link section.sh_info, # sh_info - 1 if section.p_type is None else bits // 8, # sh_addralign + sh_addralign, # sh_addralign section.sh_entsize, # sh_entsize ) shdr_offset += shdr_struct.size @@ -220,7 +235,7 @@ def create_elf_file( section.vaddr, # p_vaddr section.paddr, # p_paddr len(section.data), # p_filesz - section.memsz, # p_memsz + memsz, # p_memsz section.p_align, # p_align ) else: @@ -232,11 +247,32 @@ def create_elf_file( section.vaddr, # p_vaddr section.paddr, # p_paddr len(section.data), # p_filesz - section.memsz, # p_memsz + memsz, # p_memsz flags, # p_flags section.p_align, # p_align ) phdr_offset += phdr_struct.size - buf.extend(section.data) + if section.sh_flags & SHF.COMPRESSED: + ELFCOMPRESS_ZLIB = 1 + if bits == 64: + buf.extend( + chdr_struct.pack( + ELFCOMPRESS_ZLIB, # ch_type + 0, # ch_reserved + memsz, # ch_size + ch_addralign, # ch_addralign + ) + ) + else: + buf.extend( + chdr_struct.pack( + ELFCOMPRESS_ZLIB, # ch_type + memsz, # ch_size + ch_addralign, # ch_addralign + ) + ) + buf.extend(compressed_data) + else: + buf.extend(section.data) return buf diff --git a/tests/helpers/common/test_format.py b/tests/helpers/common/test_format.py index b196f8a76..d57421531 100644 --- a/tests/helpers/common/test_format.py +++ b/tests/helpers/common/test_format.py @@ -1,8 +1,12 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from drgn import Program, TypeEnumerator -from drgn.helpers.common.format import decode_enum_type_flags, decode_flags +from drgn.helpers.common.format import ( + decode_enum_type_flags, + decode_flags, + number_in_binary_units, +) from tests import MOCK_PLATFORM, TestCase @@ -73,3 +77,50 @@ def test_decode_enum_type_flags_incomplete(self): 2, Program().enum_type(None), ) + + +class TestNumberInBinaryUnits(TestCase): + def test_zero(self): + self.assertEqual(number_in_binary_units(0), "0") + + def test_small(self): + self.assertEqual(number_in_binary_units(100), "100") + self.assertEqual(number_in_binary_units(1023), "1023") + + def test_small_negative(self): + self.assertEqual(number_in_binary_units(-100), "-100") + self.assertEqual(number_in_binary_units(-1023), "-1023") + + def test_integer(self): + self.assertEqual(number_in_binary_units(1024), "1K") + self.assertEqual(number_in_binary_units(1024**2), "1M") + self.assertEqual(number_in_binary_units(1024**3), "1G") + self.assertEqual(number_in_binary_units(1024**4), "1T") + self.assertEqual(number_in_binary_units(1024**5), "1P") + self.assertEqual(number_in_binary_units(1024**6), "1E") + self.assertEqual(number_in_binary_units(1024**7), "1Z") + self.assertEqual(number_in_binary_units(1024**8), "1Y") + + def test_negative_integer(self): + self.assertEqual(number_in_binary_units(-1024), "-1K") + self.assertEqual(number_in_binary_units(-(1024**2)), "-1M") + self.assertEqual(number_in_binary_units(-(1024**3)), "-1G") + self.assertEqual(number_in_binary_units(-(1024**4)), "-1T") + self.assertEqual(number_in_binary_units(-(1024**5)), "-1P") + self.assertEqual(number_in_binary_units(-(1024**6)), "-1E") + self.assertEqual(number_in_binary_units(-(1024**7)), "-1Z") + self.assertEqual(number_in_binary_units(-(1024**8)), "-1Y") + + def test_almost_integer(self): + self.assertEqual(number_in_binary_units(1025), "1.0K") + self.assertEqual(number_in_binary_units(1024**4 + 1), "1.0T") + + def test_precision(self): + n = 1088 + self.assertEqual(number_in_binary_units(n, precision=0), "1K") + self.assertEqual(number_in_binary_units(n, precision=1), "1.1K") + self.assertEqual(number_in_binary_units(n, precision=2), "1.06K") + + def test_huge(self): + self.assertEqual(number_in_binary_units(1024**8 * 1.5), "1.5Y") + self.assertEqual(number_in_binary_units(1024**10), "1048576Y") diff --git a/tests/libdrgn.py b/tests/libdrgn.py index 160780a24..4c95ae9d6 100644 --- a/tests/libdrgn.py +++ b/tests/libdrgn.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import ctypes import enum @@ -192,6 +192,7 @@ class C_TOKEN(enum.IntEnum): DOT = auto() NUMBER = auto() IDENTIFIER = auto() + TEMPLATE_ARGUMENTS = auto() class Token: diff --git a/tests/linux_kernel/__init__.py b/tests/linux_kernel/__init__.py index 54a238d46..7833a9234 100644 --- a/tests/linux_kernel/__init__.py +++ b/tests/linux_kernel/__init__.py @@ -1,21 +1,23 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import contextlib import ctypes import errno import os from pathlib import Path -import platform import re import signal import socket +import sys import time +import traceback from typing import NamedTuple import unittest import drgn from tests import TestCase +from util import NORMALIZED_MACHINE_NAME, SYS class LinuxKernelTestCase(TestCase): @@ -89,12 +91,31 @@ def setUpClass(cls): "DRGN_TEST_KMOD" in os.environ, "test requires drgn_test Linux kernel module" ) +HAVE_FULL_MM_SUPPORT = NORMALIZED_MACHINE_NAME in ("aarch64", "s390x", "x86_64") + skip_unless_have_full_mm_support = unittest.skipUnless( - platform.machine() == "x86_64", - f"mm support is not implemented for {platform.machine()}", + HAVE_FULL_MM_SUPPORT, + f"mm support is not implemented for {NORMALIZED_MACHINE_NAME}", +) + +skip_unless_have_stack_tracing = unittest.skipUnless( + NORMALIZED_MACHINE_NAME in {"aarch64", "ppc64", "s390x", "x86_64"}, + f"stack tracing is not implemented for {NORMALIZED_MACHINE_NAME}", ) +# PRNG used by the test kernel module. +def prng32(seed): + seed = seed.encode("ascii") + assert len(seed) == 4 + x = int.from_bytes(seed, "big") + while True: + x ^= (x << 13) & 0xFFFFFFFF + x ^= x >> 17 + x ^= (x << 5) & 0xFFFFFFFF + yield x + + def wait_until(fn, *args, **kwds): TIMEOUT = 5 deadline = time.monotonic() + TIMEOUT @@ -109,31 +130,51 @@ def wait_until(fn, *args, **kwds): sleep *= 2 -def fork_and_pause(fn=None): - pid = os.fork() - if pid == 0: - if fn: - fn() - try: - while True: - signal.pause() - finally: - os._exit(1) - return pid - - def proc_state(pid): with open(f"/proc/{pid}/status", "r") as f: return re.search(r"State:\s*(\S)", f.read(), re.M).group(1) -# Return whether a process is blocked and fully scheduled out. The process -# state is updated while the process is still running, so use this instead of -# proc_state(pid) != "R" to avoid races. This is not accurate if pid is the -# calling thread. -def proc_blocked(pid): +_sigwait_syscall_number_strs = { + str(SYS[name]) + for name in ("rt_sigtimedwait", "rt_sigtimedwait_time64") + if name in SYS +} + + +# Return whether a process is blocked in sigwait(). +def proc_in_sigwait(pid): + if proc_state(pid) != "S": + return False with open(f"/proc/{pid}/syscall", "r") as f: - return f.read() != "running\n" + return f.read().partition(" ")[0] in _sigwait_syscall_number_strs + + +# Context manager that: +# 1. Forks a process that blocks in sigwait() forever, optionally calling a +# function beforehand. +# 2. Waits for the process to be in sigwait(). +# 3. Returns the PID from __enter__(). +# 4. Kills the process in __exit__(). +@contextlib.contextmanager +def fork_and_sigwait(fn=None): + pid = os.fork() + try: + if pid == 0: + try: + if fn: + fn() + while True: + signal.sigwait(()) + finally: + traceback.print_exc() + sys.stderr.flush() + os._exit(1) + wait_until(proc_in_sigwait, pid) + yield pid + finally: + os.kill(pid, signal.SIGKILL) + os.waitpid(pid, 0) def smp_enabled(): diff --git a/tests/linux_kernel/bpf.py b/tests/linux_kernel/bpf.py index 1e3cd7172..5d8570739 100644 --- a/tests/linux_kernel/bpf.py +++ b/tests/linux_kernel/bpf.py @@ -1,13 +1,12 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import array import ctypes -import platform -import re from typing import NamedTuple from tests.linux_kernel import _check_ctypes_syscall, _syscall +from util import SYS # enum bpf_cmd BPF_MAP_CREATE = 0 @@ -247,37 +246,10 @@ class _bpf_attr(ctypes.Union): ) -_machine = platform.machine() try: - if _machine.startswith("aarch64") or _machine.startswith("arm64"): - _SYS_bpf = 280 - elif _machine.startswith("arm"): - _SYS_bpf = 386 - elif re.fullmatch(r"i.86", _machine): - _SYS_bpf = 357 - elif _machine.startswith("parisc"): - _SYS_bpf = 341 - elif _machine.startswith("ppc"): - _SYS_bpf = 361 - elif _machine.startswith("s390"): - _SYS_bpf = 351 - elif _machine.startswith("sh"): - _SYS_bpf = 375 - elif _machine.startswith("sparc"): - _SYS_bpf = 349 - else: - _SYS_bpf = { - "alpha": 515, - "ia64": 317, - "m68k": 354, - "microblaze": 387, - "x86_64": 321, - "xtensa": 340, - }[_machine] + _SYS_bpf = ctypes.c_long(SYS["bpf"]) except KeyError: _SYS_bpf = None -else: - _SYS_bpf = ctypes.c_long(_SYS_bpf) _sizeof_bpf_attr = ctypes.c_uint(ctypes.sizeof(_bpf_attr)) diff --git a/tests/linux_kernel/helpers/test_bitops.py b/tests/linux_kernel/helpers/test_bitops.py index ec510de6e..28af08de0 100644 --- a/tests/linux_kernel/helpers/test_bitops.py +++ b/tests/linux_kernel/helpers/test_bitops.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from drgn import Object from drgn.helpers.linux.bitops import for_each_clear_bit, for_each_set_bit, test_bit diff --git a/tests/linux_kernel/helpers/test_block.py b/tests/linux_kernel/helpers/test_block.py index 7fe40c017..74435a7d8 100644 --- a/tests/linux_kernel/helpers/test_block.py +++ b/tests/linux_kernel/helpers/test_block.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import errno from fcntl import ioctl diff --git a/tests/linux_kernel/helpers/test_boot.py b/tests/linux_kernel/helpers/test_boot.py index 9a35a4291..580f54efb 100644 --- a/tests/linux_kernel/helpers/test_boot.py +++ b/tests/linux_kernel/helpers/test_boot.py @@ -1,16 +1,16 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later -import platform import re import unittest from drgn.helpers.linux.boot import pgtable_l5_enabled from tests.linux_kernel import LinuxKernelTestCase +from util import NORMALIZED_MACHINE_NAME class TestBoot(LinuxKernelTestCase): - @unittest.skipUnless(platform.machine() == "x86_64", "machine is not x86_64") + @unittest.skipUnless(NORMALIZED_MACHINE_NAME == "x86_64", "machine is not x86_64") def test_pgtable_l5_enabled(self): with open("/proc/cpuinfo", "r") as f: self.assertEqual( diff --git a/tests/linux_kernel/helpers/test_bpf.py b/tests/linux_kernel/helpers/test_bpf.py index 4b8d109a8..146812e4c 100644 --- a/tests/linux_kernel/helpers/test_bpf.py +++ b/tests/linux_kernel/helpers/test_bpf.py @@ -1,9 +1,8 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import errno import os -import platform import sys import unittest @@ -35,6 +34,7 @@ bpf_prog_load, ) from tests.linux_kernel.helpers.test_cgroup import tmp_cgroups +from util import NORMALIZED_MACHINE_NAME class TestBpf(LinuxKernelTestCase): @@ -51,7 +51,7 @@ def setUpClass(cls): super().setUpClass() if _SYS_bpf is None: raise unittest.SkipTest( - f"bpf syscall number is not known on {platform.machine()}" + f"bpf syscall number is not known on {NORMALIZED_MACHINE_NAME}" ) try: os.close(bpf_map_create(BPF_MAP_TYPE_HASH, 8, 8, 8)) diff --git a/tests/linux_kernel/helpers/test_cgroup.py b/tests/linux_kernel/helpers/test_cgroup.py index c934166b4..6a99351fd 100644 --- a/tests/linux_kernel/helpers/test_cgroup.py +++ b/tests/linux_kernel/helpers/test_cgroup.py @@ -1,14 +1,13 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import contextlib import os from pathlib import Path -import signal import tempfile import unittest -from drgn import NULL +from drgn import NULL, cast from drgn.helpers.linux.cgroup import ( cgroup_get_from_path, cgroup_name, @@ -16,9 +15,16 @@ cgroup_path, css_for_each_child, css_for_each_descendant_pre, + sock_cgroup_ptr, ) +from drgn.helpers.linux.fs import fget from drgn.helpers.linux.pid import find_task -from tests.linux_kernel import LinuxKernelTestCase, fork_and_pause, iter_mounts +from tests.linux_kernel import ( + LinuxKernelTestCase, + create_socket, + fork_and_sigwait, + iter_mounts, +) @contextlib.contextmanager @@ -56,8 +62,7 @@ def setUpClass(cls): cls.root_cgroup = cls.prog["cgrp_dfl_root"].cgrp.address_of_() - pid = fork_and_pause() - try: + with fork_and_sigwait() as pid: task = find_task(cls.prog, pid) cls.parent_cgroup_name = os.fsencode(parent_cgroup_dir.name) @@ -73,9 +78,6 @@ def setUpClass(cls): (child_cgroup_dir / "cgroup.procs").write_text(str(pid)) cls.child_cgroup = task.cgroups.dfl_cgrp.read_() - finally: - os.kill(pid, signal.SIGKILL) - os.waitpid(pid, 0) except BaseException: for cleanup in reversed(cls.__cleanups): cleanup[0](*cleanup[1:]) @@ -117,6 +119,13 @@ def test_cgroup_get_from_path(self): NULL(self.prog, "struct cgroup *"), ) + def test_cgroup_socket(self): + with create_socket() as sock: + task = find_task(self.prog, os.getpid()) + file = fget(task, sock.fileno()) + sk = cast("struct socket *", file.private_data).sk + self.assertEqual(sock_cgroup_ptr(sk.sk_cgrp_data), task.cgroups.dfl_cgrp) + @staticmethod def _cgroup_iter_paths(fn, cgroup): return [cgroup_path(css.cgroup) for css in fn(cgroup.self.address_of_())] diff --git a/tests/linux_kernel/helpers/test_common.py b/tests/linux_kernel/helpers/test_common.py index a29715380..87c51725d 100644 --- a/tests/linux_kernel/helpers/test_common.py +++ b/tests/linux_kernel/helpers/test_common.py @@ -1,27 +1,35 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later -from drgn.helpers.common import identify_address +from contextlib import redirect_stdout +import io + +from drgn.helpers.common.memory import identify_address +from drgn.helpers.common.stack import print_annotated_stack from drgn.helpers.linux.mm import pfn_to_virt from tests.linux_kernel import ( + HAVE_FULL_MM_SUPPORT, LinuxKernelTestCase, + fork_and_sigwait, skip_unless_have_full_mm_support, + skip_unless_have_stack_tracing, skip_unless_have_test_kmod, ) class TestIdentifyAddress(LinuxKernelTestCase): + @skip_unless_have_test_kmod def test_identify_symbol(self): - symbol = self.prog.symbol("__schedule") + symbol = self.prog.symbol("drgn_test_function") - self.assertIn( + self.assertEqual( identify_address(self.prog, symbol.address), - ("symbol: __sched_text_start+0x0", "function symbol: __schedule+0x0"), + "function symbol: drgn_test_function+0x0", ) self.assertEqual( identify_address(self.prog, symbol.address + 1), - "function symbol: __schedule+0x1", + "function symbol: drgn_test_function+0x1", ) @skip_unless_have_full_mm_support @@ -38,13 +46,37 @@ def test_identify_slab_cache(self): for obj in objects: self.assertEqual( identify_address(obj), - f"slab object: drgn_test_{size}", + f"slab object: drgn_test_{size}+0x0", ) + @skip_unless_have_full_mm_support + @skip_unless_have_test_kmod def test_identify_unrecognized(self): start_addr = (pfn_to_virt(self.prog["min_low_pfn"])).value_() end_addr = (pfn_to_virt(self.prog["max_pfn"]) + self.prog["PAGE_SIZE"]).value_() - self.assertIsNone(identify_address(self.prog, start_addr - 1)) + # On s390x, the start address is 0, and identify_address() doesn't + # allow a negative address. + if start_addr > 0: + self.assertIsNone(identify_address(self.prog, start_addr - 1)) self.assertIsNone(identify_address(self.prog, end_addr)) self.assertIsNone(identify_address(self.prog, self.prog["drgn_test_va"])) + + +class TestPrintAnnotatedStack(LinuxKernelTestCase): + @skip_unless_have_stack_tracing + @skip_unless_have_test_kmod + def test_print_annotated_stack(self): + with fork_and_sigwait() as pid: + trace = self.prog.stack_trace(pid) + + f = io.StringIO() + with redirect_stdout(f): + print_annotated_stack(trace) + + printed_trace = f.getvalue() + + if HAVE_FULL_MM_SUPPORT and not self.prog["drgn_test_slob"]: + self.assertIn("slab object: task_struct", printed_trace) + self.assertIn("[function symbol: schedule", printed_trace) + self.assertIn("schedule at ", printed_trace) diff --git a/tests/linux_kernel/helpers/test_cpumask.py b/tests/linux_kernel/helpers/test_cpumask.py index 494dee5a5..ff7fe2b48 100644 --- a/tests/linux_kernel/helpers/test_cpumask.py +++ b/tests/linux_kernel/helpers/test_cpumask.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from pathlib import Path diff --git a/tests/linux_kernel/helpers/test_fs.py b/tests/linux_kernel/helpers/test_fs.py index b338bbba5..3e54a0861 100644 --- a/tests/linux_kernel/helpers/test_fs.py +++ b/tests/linux_kernel/helpers/test_fs.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import os import os.path diff --git a/tests/linux_kernel/helpers/test_idr.py b/tests/linux_kernel/helpers/test_idr.py new file mode 100644 index 000000000..dd0345687 --- /dev/null +++ b/tests/linux_kernel/helpers/test_idr.py @@ -0,0 +1,71 @@ +# Copyright (c) 2023, Oracle and/or its affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +from drgn import NULL, Object +from drgn.helpers.linux.idr import idr_find, idr_for_each +from tests.linux_kernel import LinuxKernelTestCase, skip_unless_have_test_kmod + + +@skip_unless_have_test_kmod +class TestIDR(LinuxKernelTestCase): + def test_idr_find_empty(self): + root = self.prog["drgn_test_idr_empty"].address_of_() + self.assertIdentical(idr_find(root, 0), NULL(self.prog, "void *")) + self.assertIdentical(idr_find(root, 100), NULL(self.prog, "void *")) + + def test_idr_for_each_empty(self): + root = self.prog["drgn_test_idr_empty"].address_of_() + self.assertIdentical(list(idr_for_each(root)), []) + + def test_idr_find_one(self): + root = self.prog["drgn_test_idr_one"].address_of_() + self.assertIdentical(idr_find(root, 0), NULL(self.prog, "void *")) + self.assertIdentical(idr_find(root, 65), NULL(self.prog, "void *")) + self.assertIdentical(idr_find(root, 66), Object(self.prog, "void *", 0xDEADB00)) + self.assertIdentical(idr_find(root, 67), NULL(self.prog, "void *")) + self.assertIdentical(idr_find(root, 100), NULL(self.prog, "void *")) + self.assertIdentical(idr_find(root, 256 + 66), NULL(self.prog, "void *")) + self.assertIdentical(idr_find(root, 2**24 + 66), NULL(self.prog, "void *")) + self.assertIdentical(idr_find(root, 2**56 + 66), NULL(self.prog, "void *")) + + def test_idr_for_each_one(self): + root = self.prog["drgn_test_idr_one"].address_of_() + self.assertIdentical( + list(idr_for_each(root)), + [(66, Object(self.prog, "void *", 0xDEADB00))], + ) + + def test_idr_lookup_one_at_zero(self): + root = self.prog["drgn_test_idr_one_at_zero"].address_of_() + self.assertIdentical(idr_find(root, 0), Object(self.prog, "void *", 0x1234)) + self.assertIdentical(idr_find(root, 1), NULL(self.prog, "void *")) + self.assertIdentical(idr_find(root, 100), NULL(self.prog, "void *")) + + def test_idr_for_each_one_at_zero(self): + root = self.prog["drgn_test_idr_one_at_zero"].address_of_() + self.assertIdentical( + list(idr_for_each(root)), [(0, Object(self.prog, "void *", 0x1234))] + ) + + def test_idr_find_sparse(self): + root = self.prog["drgn_test_idr_sparse"].address_of_() + self.assertIdentical(idr_find(root, 0), NULL(self.prog, "void *")) + self.assertIdentical(idr_find(root, 1), Object(self.prog, "void *", 0x1234)) + self.assertIdentical(idr_find(root, 2), NULL(self.prog, "void *")) + self.assertIdentical(idr_find(root, 0x40), NULL(self.prog, "void *")) + self.assertIdentical(idr_find(root, 0x70), NULL(self.prog, "void *")) + self.assertIdentical(idr_find(root, 0x7F), NULL(self.prog, "void *")) + self.assertIdentical(idr_find(root, 0x80), Object(self.prog, "void *", 0x5678)) + self.assertIdentical(idr_find(root, 0xEE), Object(self.prog, "void *", 0x9ABC)) + self.assertIdentical(idr_find(root, 0xEF), NULL(self.prog, "void *")) + + def test_idr_for_each_sparse(self): + root = self.prog["drgn_test_idr_sparse"].address_of_() + self.assertIdentical( + list(idr_for_each(root)), + [ + (1, Object(self.prog, "void *", 0x1234)), + (0x80, Object(self.prog, "void *", 0x5678)), + (0xEE, Object(self.prog, "void *", 0x9ABC)), + ], + ) diff --git a/tests/linux_kernel/helpers/test_kconfig.py b/tests/linux_kernel/helpers/test_kconfig.py index 0441f023b..6dded6411 100644 --- a/tests/linux_kernel/helpers/test_kconfig.py +++ b/tests/linux_kernel/helpers/test_kconfig.py @@ -1,7 +1,8 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later -import os.path +import gzip +import re from drgn.helpers.linux.kconfig import get_kconfig from tests.linux_kernel import LinuxKernelTestCase @@ -9,7 +10,13 @@ class TestKconfig(LinuxKernelTestCase): def test_get_kconfig(self): - if not os.path.isfile("/proc/config.gz"): + expected = {} + try: + with gzip.open("/proc/config.gz", "rt") as f: + for line in f: + match = re.match(r"(\w+)=(.*)", line) + if match: + expected[match.group(1)] = match.group(2) + except FileNotFoundError: self.skipTest("kernel not built with CONFIG_IKCONFIG_PROC") - m = get_kconfig(self.prog) - self.assertIn(m["CONFIG_IKCONFIG"], {"y", "m"}) + self.assertEqual(get_kconfig(self.prog), expected) diff --git a/tests/linux_kernel/helpers/test_kernfs.py b/tests/linux_kernel/helpers/test_kernfs.py index 691b2c49f..dfa8b4d15 100644 --- a/tests/linux_kernel/helpers/test_kernfs.py +++ b/tests/linux_kernel/helpers/test_kernfs.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import os diff --git a/tests/linux_kernel/helpers/test_list.py b/tests/linux_kernel/helpers/test_list.py index 6ed65cf98..3605bf377 100644 --- a/tests/linux_kernel/helpers/test_list.py +++ b/tests/linux_kernel/helpers/test_list.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import collections @@ -9,6 +9,7 @@ hlist_empty, hlist_for_each, hlist_for_each_entry, + list_count_nodes, list_empty, list_first_entry, list_first_entry_or_null, @@ -56,6 +57,11 @@ def test_list_is_singular(self): self.assertFalse(list_is_singular(self.full)) self.assertTrue(list_is_singular(self.singular)) + def test_list_count_nodes(self): + self.assertEqual(list_count_nodes(self.empty), 0) + self.assertEqual(list_count_nodes(self.full), self.num_entries) + self.assertEqual(list_count_nodes(self.singular), 1) + def test_list_first_entry(self): self.assertEqual( list_first_entry(self.full, "struct drgn_test_list_entry", "node"), diff --git a/tests/linux_kernel/helpers/test_llist.py b/tests/linux_kernel/helpers/test_llist.py index d4dc10e50..04f40fd00 100644 --- a/tests/linux_kernel/helpers/test_llist.py +++ b/tests/linux_kernel/helpers/test_llist.py @@ -1,5 +1,5 @@ # Copyright (c) 2022, Oracle and/or its affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from drgn import NULL from drgn.helpers.linux.llist import ( diff --git a/tests/linux_kernel/helpers/test_mm.py b/tests/linux_kernel/helpers/test_mm.py index 4e48dfbb0..79e5633e8 100644 --- a/tests/linux_kernel/helpers/test_mm.py +++ b/tests/linux_kernel/helpers/test_mm.py @@ -1,12 +1,12 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import contextlib import ctypes import mmap import os -import platform import struct +import sys import tempfile import unittest @@ -27,6 +27,9 @@ compound_order, decode_page_flags, environ, + follow_page, + follow_pfn, + follow_phys, page_size, page_to_pfn, page_to_phys, @@ -35,17 +38,22 @@ pfn_to_virt, phys_to_page, phys_to_virt, + totalram_pages, virt_to_page, virt_to_pfn, virt_to_phys, + vmalloc_to_page, + vmalloc_to_pfn, ) from drgn.helpers.linux.pid import find_task from tests.linux_kernel import ( LinuxKernelTestCase, mlock, + prng32, skip_unless_have_full_mm_support, skip_unless_have_test_kmod, ) +from util import NORMALIZED_MACHINE_NAME class TestMm(LinuxKernelTestCase): @@ -142,28 +150,24 @@ def test_decode_page_flags(self): page = pfn_to_page(self.prog, pfns[0]) self.assertIn("PG_swapbacked", decode_page_flags(page)) - @skip_unless_have_full_mm_support @skip_unless_have_test_kmod def test_PFN_PHYS(self): self.assertEqual( PFN_PHYS(self.prog["drgn_test_pfn"]), self.prog["drgn_test_pa"] ) - @skip_unless_have_full_mm_support @skip_unless_have_test_kmod def test_PHYS_PFN(self): self.assertEqual( PHYS_PFN(self.prog["drgn_test_pa"]), self.prog["drgn_test_pfn"] ) - @skip_unless_have_full_mm_support @skip_unless_have_test_kmod def test_page_to_pfn(self): self.assertEqual( page_to_pfn(self.prog["drgn_test_page"]), self.prog["drgn_test_pfn"] ) - @skip_unless_have_full_mm_support @skip_unless_have_test_kmod def test_page_to_phys(self): self.assertEqual( @@ -177,7 +181,6 @@ def test_page_to_virt(self): page_to_virt(self.prog["drgn_test_page"]), self.prog["drgn_test_va"] ) - @skip_unless_have_full_mm_support @skip_unless_have_test_kmod def test_pfn_to_page(self): self.assertEqual( @@ -191,7 +194,6 @@ def test_pfn_to_virt(self): pfn_to_virt(self.prog["drgn_test_pfn"]), self.prog["drgn_test_va"] ) - @skip_unless_have_full_mm_support @skip_unless_have_test_kmod def test_phys_to_page(self): self.assertEqual( @@ -226,13 +228,59 @@ def test_virt_to_phys(self): virt_to_phys(self.prog["drgn_test_va"]), self.prog["drgn_test_pa"] ) + @skip_unless_have_test_kmod def test_read_physical(self): - with self._pages() as (map, _, pfns): - for i, pfn in enumerate(pfns): - self.assertEqual( - self.prog.read(pfn * mmap.PAGESIZE, mmap.PAGESIZE, True), - map[i * mmap.PAGESIZE : (i + 1) * mmap.PAGESIZE], - ) + expected = bytearray() + for x in prng32("PAGE"): + expected.extend(x.to_bytes(4, sys.byteorder)) + if len(expected) >= mmap.PAGESIZE: + break + self.assertEqual( + self.prog.read(self.prog["drgn_test_pa"], mmap.PAGESIZE, True), expected + ) + + @skip_unless_have_full_mm_support + @skip_unless_have_test_kmod + def test_follow_phys(self): + self.assertEqual( + follow_phys(self.prog["init_mm"].address_of_(), self.prog["drgn_test_va"]), + self.prog["drgn_test_pa"], + ) + + @skip_unless_have_full_mm_support + @skip_unless_have_test_kmod + def test_follow_page(self): + self.assertEqual( + follow_page(self.prog["init_mm"].address_of_(), self.prog["drgn_test_va"]), + self.prog["drgn_test_page"], + ) + + @skip_unless_have_full_mm_support + @skip_unless_have_test_kmod + def test_follow_pfn(self): + self.assertEqual( + follow_pfn(self.prog["init_mm"].address_of_(), self.prog["drgn_test_va"]), + self.prog["drgn_test_pfn"], + ) + task = find_task(self.prog, os.getpid()) + with self._pages() as (map, address, pfns): + self.assertEqual(follow_pfn(task.mm, address), pfns[0]) + + @skip_unless_have_full_mm_support + @skip_unless_have_test_kmod + def test_vmalloc_to_page(self): + self.assertEqual( + vmalloc_to_page(self.prog["drgn_test_vmalloc_va"]), + self.prog["drgn_test_vmalloc_page"], + ) + + @skip_unless_have_full_mm_support + @skip_unless_have_test_kmod + def test_vmalloc_to_pfn(self): + self.assertEqual( + vmalloc_to_pfn(self.prog["drgn_test_vmalloc_va"]), + self.prog["drgn_test_vmalloc_pfn"], + ) @skip_unless_have_full_mm_support def test_access_process_vm(self): @@ -268,7 +316,7 @@ def test_access_remote_vm_init_mm(self): data, ) - @unittest.skipUnless(platform.machine() == "x86_64", "machine is not x86_64") + @unittest.skipUnless(NORMALIZED_MACHINE_NAME == "x86_64", "machine is not x86_64") def test_non_canonical_x86_64(self): task = find_task(self.prog, os.getpid()) data = b"hello, world" @@ -291,3 +339,14 @@ def test_environ(self): proc_environ = f.read().split(b"\0")[:-1] task = find_task(self.prog, os.getpid()) self.assertEqual(environ(task), proc_environ) + + def test_totalram_pages(self): + with open("/proc/meminfo") as f: + lines = f.read().splitlines() + line = [line for line in lines if line.startswith("MemTotal:")][0] + parts = line.split() + self.assertEqual(parts[2], "kB") + + proc_totalram = 1024 * int(parts[1]) + page_size = self.prog["PAGE_SIZE"].value_() + self.assertEqual(totalram_pages(self.prog) * page_size, proc_totalram) diff --git a/tests/linux_kernel/helpers/test_net.py b/tests/linux_kernel/helpers/test_net.py index 2be433d99..47785db60 100644 --- a/tests/linux_kernel/helpers/test_net.py +++ b/tests/linux_kernel/helpers/test_net.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import os import socket @@ -17,10 +17,16 @@ netdev_for_each_tx_queue, netdev_get_by_index, netdev_get_by_name, + netdev_priv, sk_fullsock, + skb_shinfo, ) from drgn.helpers.linux.pid import find_task -from tests.linux_kernel import LinuxKernelTestCase, create_socket +from tests.linux_kernel import ( + LinuxKernelTestCase, + create_socket, + skip_unless_have_test_kmod, +) class TestNet(LinuxKernelTestCase): @@ -48,6 +54,19 @@ def test_netdev_get_by_name(self): netdev = netdev_get_by_name(self.net, name) self.assertEqual(netdev.ifindex, index) + @skip_unless_have_test_kmod + def test_netdev_get_by_name_init_net(self): + self.assertEqual( + netdev_get_by_name(self.prog, "lo"), self.prog["drgn_test_netdev"] + ) + + @skip_unless_have_test_kmod + def test_netdev_priv(self): + self.assertEqual( + netdev_priv(self.prog["drgn_test_netdev"]), + self.prog["drgn_test_netdev_priv"], + ) + def test_for_each_net(self): self.assertIn(self.prog["init_net"].address_of_(), for_each_net(self.prog)) @@ -96,3 +115,9 @@ def test_SOCK_INODE(self): sock = SOCKET_I(fget(self.task, skt.fileno()).f_inode) inode = SOCK_INODE(sock) self.assertEqual(inode.i_mode & _S_IFMT, _S_IFSOCK) + + @skip_unless_have_test_kmod + def test_skb_shinfo(self): + self.assertEqual( + skb_shinfo(self.prog["drgn_test_skb"]), self.prog["drgn_test_skb_shinfo"] + ) diff --git a/tests/linux_kernel/helpers/test_nodemask.py b/tests/linux_kernel/helpers/test_nodemask.py index 5224b4098..c7d37f667 100644 --- a/tests/linux_kernel/helpers/test_nodemask.py +++ b/tests/linux_kernel/helpers/test_nodemask.py @@ -1,5 +1,5 @@ # Copyright (c) ByteDance, Inc. and its affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from pathlib import Path import unittest diff --git a/tests/linux_kernel/helpers/test_percpu.py b/tests/linux_kernel/helpers/test_percpu.py index cdd94215e..c0a343262 100644 --- a/tests/linux_kernel/helpers/test_percpu.py +++ b/tests/linux_kernel/helpers/test_percpu.py @@ -1,10 +1,11 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from drgn.helpers.linux.cpumask import for_each_possible_cpu from drgn.helpers.linux.percpu import per_cpu, per_cpu_ptr from tests.linux_kernel import ( LinuxKernelTestCase, + prng32, skip_unless_have_test_kmod, smp_enabled, ) @@ -25,18 +26,14 @@ def test_per_cpu(self): @skip_unless_have_test_kmod def test_per_cpu_module_static(self): - expected = prime = self.prog["drgn_test_percpu_static_prime"] - for cpu in for_each_possible_cpu(self.prog): - expected *= prime + for cpu, expected in zip(for_each_possible_cpu(self.prog), prng32("PCPU")): self.assertEqual( per_cpu(self.prog["drgn_test_percpu_static"], cpu), expected ) @skip_unless_have_test_kmod def test_per_cpu_module_dynamic(self): - expected = prime = self.prog["drgn_test_percpu_dynamic_prime"] - for cpu in for_each_possible_cpu(self.prog): - expected *= prime + for cpu, expected in zip(for_each_possible_cpu(self.prog), prng32("pcpu")): self.assertEqual( per_cpu_ptr(self.prog["drgn_test_percpu_dynamic"], cpu)[0], expected ) diff --git a/tests/linux_kernel/helpers/test_pid.py b/tests/linux_kernel/helpers/test_pid.py index cc7ea8542..8b2600dbb 100644 --- a/tests/linux_kernel/helpers/test_pid.py +++ b/tests/linux_kernel/helpers/test_pid.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from multiprocessing import Barrier, Process import os diff --git a/tests/linux_kernel/helpers/test_printk.py b/tests/linux_kernel/helpers/test_printk.py index 81e35fd05..cf5505db5 100644 --- a/tests/linux_kernel/helpers/test_printk.py +++ b/tests/linux_kernel/helpers/test_printk.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import errno import os diff --git a/tests/linux_kernel/helpers/test_radixtree.py b/tests/linux_kernel/helpers/test_radixtree.py new file mode 100644 index 000000000..81e6fd052 --- /dev/null +++ b/tests/linux_kernel/helpers/test_radixtree.py @@ -0,0 +1,122 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +from drgn import NULL, Object +from drgn.helpers.linux.radixtree import radix_tree_for_each, radix_tree_lookup +from tests.linux_kernel import LinuxKernelTestCase, skip_unless_have_test_kmod + + +@skip_unless_have_test_kmod +class TestRadixTree(LinuxKernelTestCase): + def test_radix_tree_lookup_empty(self): + root = self.prog["drgn_test_radix_tree_empty"].address_of_() + self.assertIdentical(radix_tree_lookup(root, 0), NULL(self.prog, "void *")) + self.assertIdentical(radix_tree_lookup(root, 100000), NULL(self.prog, "void *")) + + def test_radix_tree_for_each_empty(self): + root = self.prog["drgn_test_radix_tree_empty"].address_of_() + self.assertIdentical(list(radix_tree_for_each(root)), []) + + def test_radix_tree_lookup_one(self): + root = self.prog["drgn_test_radix_tree_one"].address_of_() + self.assertIdentical(radix_tree_lookup(root, 0), NULL(self.prog, "void *")) + self.assertIdentical(radix_tree_lookup(root, 665), NULL(self.prog, "void *")) + self.assertIdentical( + radix_tree_lookup(root, 666), Object(self.prog, "void *", 0xDEADB00) + ) + self.assertIdentical(radix_tree_lookup(root, 667), NULL(self.prog, "void *")) + self.assertIdentical(radix_tree_lookup(root, 100000), NULL(self.prog, "void *")) + + def test_radix_tree_for_each_one(self): + root = self.prog["drgn_test_radix_tree_one"].address_of_() + self.assertIdentical( + list(radix_tree_for_each(root)), + [(666, Object(self.prog, "void *", 0xDEADB00))], + ) + + def test_radix_tree_lookup_one_at_zero(self): + root = self.prog["drgn_test_radix_tree_one_at_zero"].address_of_() + self.assertIdentical( + radix_tree_lookup(root, 0), Object(self.prog, "void *", 0x1234) + ) + self.assertIdentical(radix_tree_lookup(root, 1), NULL(self.prog, "void *")) + self.assertIdentical(radix_tree_lookup(root, 100000), NULL(self.prog, "void *")) + + def test_radix_tree_for_each_one_at_zero(self): + root = self.prog["drgn_test_radix_tree_one_at_zero"].address_of_() + self.assertIdentical( + list(radix_tree_for_each(root)), [(0, Object(self.prog, "void *", 0x1234))] + ) + + def test_radix_tree_lookup_sparse(self): + root = self.prog["drgn_test_radix_tree_sparse"].address_of_() + self.assertIdentical(radix_tree_lookup(root, 0), NULL(self.prog, "void *")) + self.assertIdentical( + radix_tree_lookup(root, 1), Object(self.prog, "void *", 0x1234) + ) + self.assertIdentical(radix_tree_lookup(root, 2), NULL(self.prog, "void *")) + self.assertIdentical( + radix_tree_lookup(root, 0x40000000), NULL(self.prog, "void *") + ) + self.assertIdentical( + radix_tree_lookup(root, 0x80000000), NULL(self.prog, "void *") + ) + self.assertIdentical( + radix_tree_lookup(root, 0x80800000), NULL(self.prog, "void *") + ) + self.assertIdentical( + radix_tree_lookup(root, 0x80808000), NULL(self.prog, "void *") + ) + self.assertIdentical( + radix_tree_lookup(root, 0x80808080), Object(self.prog, "void *", 0x5678) + ) + self.assertIdentical( + radix_tree_lookup(root, 0xFFFFFFFE), NULL(self.prog, "void *") + ) + self.assertIdentical( + radix_tree_lookup(root, 0xFFFFFFFF), Object(self.prog, "void *", 0x9ABC) + ) + + def test_radix_tree_for_each_sparse(self): + root = self.prog["drgn_test_radix_tree_sparse"].address_of_() + self.assertIdentical( + list(radix_tree_for_each(root)), + [ + (1, Object(self.prog, "void *", 0x1234)), + (0x80808080, Object(self.prog, "void *", 0x5678)), + (0xFFFFFFFF, Object(self.prog, "void *", 0x9ABC)), + ], + ) + + def test_radix_tree_lookup_multi_index(self): + try: + root = self.prog["drgn_test_radix_tree_multi_order"].address_of_() + except KeyError: + # Radix tree multi-order support only exists between Linux kernel + # commits e61452365372 ("radix_tree: add support for multi-order + # entries") (in v4.6) and 3a08cd52c37c ("radix tree: Remove + # multiorder support") (in v4.20), and only if + # CONFIG_RADIX_TREE_MULTIORDER=y. + self.skipTest("kernel does not have multi-order radix trees") + self.assertIdentical( + radix_tree_lookup(root, 0x80807FFF), NULL(self.prog, "void *") + ) + for index in range(0x80808000, 0x80808200): + with self.subTest(index=index): + self.assertIdentical( + radix_tree_lookup(root, index), Object(self.prog, "void *", 0x1234) + ) + self.assertIdentical( + radix_tree_lookup(root, 0x80808200), NULL(self.prog, "void *") + ) + + def test_radix_tree_for_each_multi_index(self): + try: + root = self.prog["drgn_test_radix_tree_multi_order"].address_of_() + except KeyError: + # See test_radix_tree_lookup_multi_index(). + self.skipTest("kernel does not have multi-order radix trees") + self.assertIdentical( + list(radix_tree_for_each(root)), + [(0x80808000, Object(self.prog, "void *", 0x1234))], + ) diff --git a/tests/linux_kernel/helpers/test_rbtree.py b/tests/linux_kernel/helpers/test_rbtree.py index 8e5be9f57..8127b6a12 100644 --- a/tests/linux_kernel/helpers/test_rbtree.py +++ b/tests/linux_kernel/helpers/test_rbtree.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import collections diff --git a/tests/linux_kernel/helpers/test_sched.py b/tests/linux_kernel/helpers/test_sched.py index c77f347d4..ebbd75c70 100644 --- a/tests/linux_kernel/helpers/test_sched.py +++ b/tests/linux_kernel/helpers/test_sched.py @@ -1,15 +1,21 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import os import signal from drgn.helpers.linux.cpumask import for_each_possible_cpu from drgn.helpers.linux.pid import find_task -from drgn.helpers.linux.sched import idle_task, task_state_to_char +from drgn.helpers.linux.sched import ( + cpu_curr, + idle_task, + loadavg, + task_cpu, + task_state_to_char, +) from tests.linux_kernel import ( LinuxKernelTestCase, - fork_and_pause, + fork_and_sigwait, proc_state, smp_enabled, wait_until, @@ -17,25 +23,38 @@ class TestSched(LinuxKernelTestCase): + def test_task_cpu(self): + cpu = os.cpu_count() - 1 + with fork_and_sigwait(lambda: os.sched_setaffinity(0, (cpu,))) as pid: + self.assertEqual(task_cpu(find_task(self.prog, pid)), cpu) + def test_task_state_to_char(self): task = find_task(self.prog, os.getpid()) self.assertEqual(task_state_to_char(task), "R") - pid = fork_and_pause() - task = find_task(self.prog, pid) + with fork_and_sigwait() as pid: + task = find_task(self.prog, pid) - wait_until(lambda: proc_state(pid) == "S") - self.assertEqual(task_state_to_char(task), "S") + wait_until(lambda: proc_state(pid) == "S") + self.assertEqual(task_state_to_char(task), "S") - os.kill(pid, signal.SIGSTOP) - wait_until(lambda: proc_state(pid) == "T") - self.assertEqual(task_state_to_char(task), "T") + os.kill(pid, signal.SIGSTOP) + wait_until(lambda: proc_state(pid) == "T") + self.assertEqual(task_state_to_char(task), "T") - os.kill(pid, signal.SIGKILL) - wait_until(lambda: proc_state(pid) == "Z") - self.assertEqual(task_state_to_char(task), "Z") + os.kill(pid, signal.SIGKILL) + wait_until(lambda: proc_state(pid) == "Z") + self.assertEqual(task_state_to_char(task), "Z") - os.waitpid(pid, 0) + def test_cpu_curr(self): + task = find_task(self.prog, os.getpid()) + cpu = os.cpu_count() - 1 + old_affinity = os.sched_getaffinity(0) + os.sched_setaffinity(0, (cpu,)) + try: + self.assertEqual(cpu_curr(self.prog, cpu), task) + finally: + os.sched_setaffinity(0, old_affinity) def test_idle_task(self): if smp_enabled(): @@ -45,3 +64,8 @@ def test_idle_task(self): ) else: self.assertEqual(idle_task(self.prog, 0).comm.string_(), b"swapper") + + def test_loadavg(self): + values = loadavg(self.prog) + self.assertEqual(len(values), 3) + self.assertTrue(all(v >= 0.0 for v in values)) diff --git a/tests/linux_kernel/helpers/test_slab.py b/tests/linux_kernel/helpers/test_slab.py index 0f2e11029..6658e795a 100644 --- a/tests/linux_kernel/helpers/test_slab.py +++ b/tests/linux_kernel/helpers/test_slab.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from collections import defaultdict from pathlib import Path @@ -10,8 +10,10 @@ find_containing_slab_cache, find_slab_cache, for_each_slab_cache, + get_slab_cache_aliases, slab_cache_for_each_allocated_object, slab_cache_is_merged, + slab_object_info, ) from tests.linux_kernel import ( LinuxKernelTestCase, @@ -19,6 +21,8 @@ skip_unless_have_test_kmod, ) +SLAB_SYSFS_PATH = Path("/sys/kernel/slab") + def get_proc_slabinfo_names(): with open("/proc/slabinfo", "rb") as f: @@ -44,11 +48,10 @@ def fallback_slab_cache_names(prog): class TestSlab(LinuxKernelTestCase): def _slab_cache_aliases(self): - slab_path = Path("/sys/kernel/slab") - if not slab_path.exists(): - self.skipTest(f"{slab_path} does not exist") + if not SLAB_SYSFS_PATH.exists(): + self.skipTest(f"{str(SLAB_SYSFS_PATH)} does not exist") aliases = defaultdict(list) - for child in slab_path.iterdir(): + for child in SLAB_SYSFS_PATH.iterdir(): if not child.name.startswith(":"): aliases[child.stat().st_ino].append(child.name) return aliases @@ -75,6 +78,39 @@ def test_slab_cache_is_merged_true(self): self.fail("couldn't find slab cache") self.assertTrue(slab_cache_is_merged(slab_cache)) + def test_get_slab_cache_aliases(self): + if not SLAB_SYSFS_PATH.exists(): + # A SLOB or SLAB kernel, or one without SYSFS. Test that the + # helper fails as expected. + self.assertRaisesRegex( + LookupError, "CONFIG_SYSFS", get_slab_cache_aliases, self.prog + ) + return + # Otherwise, the helper should work, test functionality. + alias_to_name = get_slab_cache_aliases(self.prog) + for aliases in self._slab_cache_aliases().values(): + # Alias groups of size 1 are either non-mergeable slabs, or + # mergeable slabs which haven't actually been merged. Either way, + # they should not be present in the dictionary. + if len(aliases) == 1: + self.assertNotIn(aliases[0], alias_to_name) + continue + + # Find out which cache in the group is target -- it won't be + # included in the alias dict. + for alias in aliases: + if alias not in alias_to_name: + target_alias = alias + aliases.remove(alias) + break + else: + self.fail("could not find target slab cache name") + + # All aliases should map to the same name + for alias in aliases: + self.assertEqual(alias_to_name[alias], target_alias) + self.assertNotIn(target_alias, alias_to_name) + def test_for_each_slab_cache(self): try: slab_cache_names = get_proc_slabinfo_names() @@ -107,14 +143,12 @@ def test_slab_cache_for_each_allocated_object(self): cache = self.prog[f"drgn_test_{size}_kmem_cache"] objects = self.prog[f"drgn_test_{size}_slab_objects"] if self.prog["drgn_test_slob"]: - self.assertRaisesRegex( - ValueError, - "SLOB is not supported", - next, - slab_cache_for_each_allocated_object( - cache, f"struct drgn_test_{size}_slab_object" - ), - ) + with self.assertRaisesRegex(ValueError, "SLOB is not supported"): + next( + slab_cache_for_each_allocated_object( + cache, f"struct drgn_test_{size}_slab_object" + ) + ) else: self.assertEqual( sorted( @@ -126,6 +160,26 @@ def test_slab_cache_for_each_allocated_object(self): list(objects), ) + @skip_unless_have_full_mm_support + @skip_unless_have_test_kmod + def test_slab_object_info(self): + for size in ("small", "big"): + with self.subTest(size=size): + cache = self.prog[f"drgn_test_{size}_kmem_cache"] + objects = self.prog[f"drgn_test_{size}_slab_objects"] + if self.prog["drgn_test_slob"]: + self.assertIsNone(slab_object_info(objects[0])) + else: + info = slab_object_info(objects[0]) + self.assertEqual(info.slab_cache, cache) + self.assertEqual(info.address, objects[0].value_()) + self.assertTrue(info.allocated) + + info = slab_object_info(objects[0].value.address_of_()) + self.assertEqual(info.slab_cache, cache) + self.assertEqual(info.address, objects[0].value_()) + self.assertTrue(info.allocated) + @skip_unless_have_full_mm_support @skip_unless_have_test_kmod def test_find_containing_slab_cache(self): diff --git a/tests/linux_kernel/helpers/test_tc.py b/tests/linux_kernel/helpers/test_tc.py index cd875a52a..0b54a0727 100644 --- a/tests/linux_kernel/helpers/test_tc.py +++ b/tests/linux_kernel/helpers/test_tc.py @@ -1,5 +1,5 @@ # Copyright (c) ByteDance, Inc. and its affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import os import random diff --git a/tests/linux_kernel/helpers/test_tcp.py b/tests/linux_kernel/helpers/test_tcp.py index 8b3e6641c..cfd8c554e 100644 --- a/tests/linux_kernel/helpers/test_tcp.py +++ b/tests/linux_kernel/helpers/test_tcp.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import os import socket diff --git a/tests/linux_kernel/helpers/test_user.py b/tests/linux_kernel/helpers/test_user.py index 2065d059d..afaf60555 100644 --- a/tests/linux_kernel/helpers/test_user.py +++ b/tests/linux_kernel/helpers/test_user.py @@ -1,17 +1,12 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later +import contextlib import functools import os -import signal from drgn.helpers.linux.user import find_user, for_each_user -from tests.linux_kernel import ( - LinuxKernelTestCase, - fork_and_pause, - proc_state, - wait_until, -) +from tests.linux_kernel import LinuxKernelTestCase, fork_and_sigwait class TestUser(LinuxKernelTestCase): @@ -20,23 +15,13 @@ class TestUser(LinuxKernelTestCase): def test_find_user(self): for uid in self.UIDS: - pid = fork_and_pause(functools.partial(os.setuid, uid)) - try: - wait_until(lambda: proc_state(pid) == "S") + with fork_and_sigwait(functools.partial(os.setuid, uid)): found_uid = find_user(self.prog, uid).uid.val.value_() - finally: - os.kill(pid, signal.SIGKILL) self.assertEqual(found_uid, uid) def test_for_each_user(self): - pids = [] - try: + with contextlib.ExitStack() as stack: for uid in self.UIDS: - pid = fork_and_pause(functools.partial(os.setuid, uid)) - wait_until(lambda: proc_state(pid) == "S") - pids.append(pid) + stack.enter_context(fork_and_sigwait(functools.partial(os.setuid, uid))) found_uids = {user.uid.val.value_() for user in for_each_user(self.prog)} - finally: - for pid in pids: - os.kill(pid, signal.SIGKILL) self.assertTrue(self.UIDS.issubset(found_uids)) diff --git a/tests/linux_kernel/helpers/test_xarray.py b/tests/linux_kernel/helpers/test_xarray.py new file mode 100644 index 000000000..b396e1f5c --- /dev/null +++ b/tests/linux_kernel/helpers/test_xarray.py @@ -0,0 +1,153 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +import unittest + +from drgn import NULL, Object +from drgn.helpers.linux.xarray import ( + xa_for_each, + xa_is_value, + xa_is_zero, + xa_load, + xa_to_value, +) +from tests.linux_kernel import LinuxKernelTestCase, skip_unless_have_test_kmod + + +@skip_unless_have_test_kmod +class TestXArray(LinuxKernelTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + if not cls.prog["drgn_test_have_xarray"]: + raise unittest.SkipTest("kernel does not have XArray") + + def test_xa_is_zero(self): + self.assertTrue(xa_is_zero(self.prog["drgn_test_xa_zero_entry"])) + self.assertFalse(xa_is_zero(NULL(self.prog, "void *"))) + + def test_xa_load_empty(self): + xa = self.prog["drgn_test_xarray_empty"].address_of_() + self.assertIdentical(xa_load(xa, 0), NULL(self.prog, "void *")) + self.assertIdentical(xa_load(xa, 100000), NULL(self.prog, "void *")) + + def test_xa_for_each_empty(self): + xa = self.prog["drgn_test_xarray_empty"].address_of_() + self.assertIdentical(list(xa_for_each(xa)), []) + + def test_xa_load_one(self): + xa = self.prog["drgn_test_xarray_one"].address_of_() + self.assertIdentical(xa_load(xa, 0), NULL(self.prog, "void *")) + self.assertIdentical(xa_load(xa, 665), NULL(self.prog, "void *")) + self.assertIdentical(xa_load(xa, 666), Object(self.prog, "void *", 0xDEADB00)) + self.assertIdentical(xa_load(xa, 667), NULL(self.prog, "void *")) + self.assertIdentical(xa_load(xa, 100000), NULL(self.prog, "void *")) + + def test_xa_for_each_one(self): + xa = self.prog["drgn_test_xarray_one"].address_of_() + self.assertIdentical( + list(xa_for_each(xa)), [(666, Object(self.prog, "void *", 0xDEADB00))] + ) + + def test_xa_load_one_at_zero(self): + xa = self.prog["drgn_test_xarray_one_at_zero"].address_of_() + self.assertIdentical(xa_load(xa, 0), Object(self.prog, "void *", 0x1234)) + self.assertIdentical(xa_load(xa, 1), NULL(self.prog, "void *")) + self.assertIdentical(xa_load(xa, 100000), NULL(self.prog, "void *")) + + def test_xa_for_each_one_at_zero(self): + xa = self.prog["drgn_test_xarray_one_at_zero"].address_of_() + self.assertIdentical( + list(xa_for_each(xa)), [(0, Object(self.prog, "void *", 0x1234))] + ) + + def test_xa_load_sparse(self): + xa = self.prog["drgn_test_xarray_sparse"].address_of_() + self.assertIdentical(xa_load(xa, 0), NULL(self.prog, "void *")) + self.assertIdentical(xa_load(xa, 1), Object(self.prog, "void *", 0x1234)) + self.assertIdentical(xa_load(xa, 2), NULL(self.prog, "void *")) + self.assertIdentical(xa_load(xa, 0x40000000), NULL(self.prog, "void *")) + self.assertIdentical(xa_load(xa, 0x80000000), NULL(self.prog, "void *")) + self.assertIdentical(xa_load(xa, 0x80800000), NULL(self.prog, "void *")) + self.assertIdentical(xa_load(xa, 0x80808000), NULL(self.prog, "void *")) + self.assertIdentical( + xa_load(xa, 0x80808080), Object(self.prog, "void *", 0x5678) + ) + self.assertIdentical(xa_load(xa, 0xFFFFFFFE), NULL(self.prog, "void *")) + self.assertIdentical( + xa_load(xa, 0xFFFFFFFF), Object(self.prog, "void *", 0x9ABC) + ) + + def test_xa_for_each_sparse(self): + xa = self.prog["drgn_test_xarray_sparse"].address_of_() + self.assertIdentical( + list(xa_for_each(xa)), + [ + (1, Object(self.prog, "void *", 0x1234)), + (0x80808080, Object(self.prog, "void *", 0x5678)), + (0xFFFFFFFF, Object(self.prog, "void *", 0x9ABC)), + ], + ) + + def test_xa_load_multi_index(self): + xa = self.prog["drgn_test_xarray_multi_index"].address_of_() + self.assertIdentical(xa_load(xa, 0x80807FFF), NULL(self.prog, "void *")) + for index in range(0x80808000, 0x80808200): + with self.subTest(index=index): + self.assertIdentical( + xa_load(xa, index), Object(self.prog, "void *", 0x1234) + ) + self.assertIdentical(xa_load(xa, 0x80808200), NULL(self.prog, "void *")) + + def test_xa_for_each_multi_index(self): + xa = self.prog["drgn_test_xarray_multi_index"].address_of_() + self.assertIdentical( + list(xa_for_each(xa)), [(0x80808000, Object(self.prog, "void *", 0x1234))] + ) + + def test_xa_load_zero_entry(self): + xa = self.prog["drgn_test_xarray_zero_entry"].address_of_() + self.assertIdentical(xa_load(xa, 0), NULL(self.prog, "void *")) + self.assertIdentical(xa_load(xa, 666), NULL(self.prog, "void *")) + self.assertTrue(xa_is_zero(xa_load(xa, 666, advanced=True))) + self.assertIdentical(xa_load(xa, 2), NULL(self.prog, "void *")) + + def test_xa_for_each_zero_entry(self): + xa = self.prog["drgn_test_xarray_zero_entry"].address_of_() + self.assertIdentical(list(xa_for_each(xa)), []) + + entries = list(xa_for_each(xa, advanced=True)) + self.assertEqual(len(entries), 1) + self.assertEqual(entries[0][0], 666) + self.assertTrue(xa_is_zero(entries[0][1])) + + def test_xa_load_zero_entry_at_zero(self): + xa = self.prog["drgn_test_xarray_zero_entry_at_zero"].address_of_() + self.assertIdentical(xa_load(xa, 0), NULL(self.prog, "void *")) + self.assertTrue(xa_is_zero(xa_load(xa, 0, advanced=True))) + self.assertIdentical(xa_load(xa, 1), NULL(self.prog, "void *")) + + def test_xa_for_each_zero_entry_at_zero(self): + xa = self.prog["drgn_test_xarray_zero_entry_at_zero"].address_of_() + self.assertIdentical(list(xa_for_each(xa)), []) + + entries = list(xa_for_each(xa, advanced=True)) + self.assertEqual(len(entries), 1) + self.assertEqual(entries[0][0], 0) + self.assertTrue(xa_is_zero(entries[0][1])) + + def test_xa_is_value(self): + self.assertTrue( + xa_is_value(xa_load(self.prog["drgn_test_xarray_value"].address_of_(), 0)) + ) + self.assertFalse( + xa_is_value( + xa_load(self.prog["drgn_test_xarray_one_at_zero"].address_of_(), 0) + ) + ) + + def test_xa_to_value(self): + self.assertIdentical( + xa_to_value(xa_load(self.prog["drgn_test_xarray_value"].address_of_(), 0)), + Object(self.prog, "unsigned long", 1337), + ) diff --git a/tests/linux_kernel/kmod/drgn_test.c b/tests/linux_kernel/kmod/drgn_test.c index 7228208ce..81d9567b3 100644 --- a/tests/linux_kernel/kmod/drgn_test.c +++ b/tests/linux_kernel/kmod/drgn_test.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: GPL-2.0-or-later // Linux kernel module for testing drgn helpers and kernel support. For now, // this is all in one file for simplicity and to keep the compilation fast @@ -8,15 +8,46 @@ // This is intended to be used with drgn's vmtest framework, but in theory it // can be used with any kernel that has debug info enabled (at your own risk). +#include #include #include +#include +#include #include #include #include #include +#include +#include #include #include +#include #include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) +#define HAVE_XARRAY 1 +#include +#else +#define HAVE_XARRAY 0 +#endif + +// Convert a 4-character string to a seed for drgn_test_prng32(). +static inline u32 drgn_test_prng32_seed(const char *s) +{ + BUG_ON(strlen(s) != 4); + return ((u32)s[0] << 24) | ((u32)s[1] << 16) | ((u32)s[2] << 8) | (u32)s[3]; +} + +// x must not be 0; the return value is never 0. +static u32 drgn_test_prng32(u32 x) +{ + // Xorshift RNG with a period of 2^32 - 1. + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + return x; +} // list @@ -119,9 +150,15 @@ phys_addr_t drgn_test_pa; unsigned long drgn_test_pfn; struct page *drgn_test_page; struct page *drgn_test_compound_page; +void *drgn_test_vmalloc_va; +unsigned long drgn_test_vmalloc_pfn; +struct page *drgn_test_vmalloc_page; static int drgn_test_mm_init(void) { + u32 fill; + size_t i; + drgn_test_page = alloc_page(GFP_KERNEL); if (!drgn_test_page) return -ENOMEM; @@ -129,43 +166,79 @@ static int drgn_test_mm_init(void) if (!drgn_test_compound_page) return -ENOMEM; drgn_test_va = page_address(drgn_test_page); + // Fill the page with a PRNG sequence. + fill = drgn_test_prng32_seed("PAGE"); + for (i = 0; i < PAGE_SIZE / sizeof(fill); i++) { + fill = drgn_test_prng32(fill); + ((u32 *)drgn_test_va)[i] = fill; + } drgn_test_pa = virt_to_phys(drgn_test_va); drgn_test_pfn = PHYS_PFN(drgn_test_pa); + drgn_test_vmalloc_va = vmalloc(PAGE_SIZE); + if (!drgn_test_vmalloc_va) + return -ENOMEM; + drgn_test_vmalloc_pfn = vmalloc_to_pfn(drgn_test_vmalloc_va); + drgn_test_vmalloc_page = vmalloc_to_page(drgn_test_vmalloc_va); return 0; } static void drgn_test_mm_exit(void) { + vfree(drgn_test_vmalloc_va); if (drgn_test_compound_page) - __free_pages(drgn_test_page, 1); + __free_pages(drgn_test_compound_page, 1); if (drgn_test_page) __free_pages(drgn_test_page, 0); } +// net + +struct net_device *drgn_test_netdev; +void *drgn_test_netdev_priv; +struct sk_buff *drgn_test_skb; +struct skb_shared_info *drgn_test_skb_shinfo; + +static int drgn_test_net_init(void) +{ + drgn_test_netdev = dev_get_by_name(&init_net, "lo"); + if (!drgn_test_netdev) + return -ENODEV; + // The loopback device doesn't actually have private data, but we just + // need to compare the pointer. + drgn_test_netdev_priv = netdev_priv(drgn_test_netdev); + drgn_test_skb = alloc_skb(64, GFP_KERNEL); + if (!drgn_test_skb) + return -ENOMEM; + drgn_test_skb_shinfo = skb_shinfo(drgn_test_skb); + return 0; +} + +static void drgn_test_net_exit(void) +{ + kfree_skb(drgn_test_skb); + dev_put(drgn_test_netdev); +} // percpu -DEFINE_PER_CPU(unsigned int, drgn_test_percpu_static); -const unsigned int drgn_test_percpu_static_prime = 0xa45dcfc3U; -unsigned int __percpu *drgn_test_percpu_dynamic; -const unsigned int drgn_test_percpu_dynamic_prime = 0x6d80a613U; +DEFINE_PER_CPU(u32, drgn_test_percpu_static); +u32 __percpu *drgn_test_percpu_dynamic; static int drgn_test_percpu_init(void) { int cpu; - unsigned int static_prime = drgn_test_percpu_static_prime; - unsigned int dynamic_prime = drgn_test_percpu_dynamic_prime; + u32 static_seed = drgn_test_prng32_seed("PCPU"); + u32 dynamic_seed = drgn_test_prng32_seed("pcpu"); - drgn_test_percpu_dynamic = alloc_percpu(unsigned int); + drgn_test_percpu_dynamic = alloc_percpu(u32); if (!drgn_test_percpu_dynamic) return -ENOMEM; - // Initialize the per-cpu variables to powers of a random prime number - // which are extremely unlikely to appear anywhere else. + // Initialize the per-cpu variables with a PRNG sequence. for_each_possible_cpu(cpu) { - static_prime *= drgn_test_percpu_static_prime; - per_cpu(drgn_test_percpu_static, cpu) = static_prime; - dynamic_prime *= drgn_test_percpu_dynamic_prime; - *per_cpu_ptr(drgn_test_percpu_dynamic, cpu) = dynamic_prime; + static_seed = drgn_test_prng32(static_seed); + per_cpu(drgn_test_percpu_static, cpu) = static_seed; + dynamic_seed = drgn_test_prng32(dynamic_seed); + *per_cpu_ptr(drgn_test_percpu_dynamic, cpu) = dynamic_seed; } return 0; } @@ -385,6 +458,395 @@ static int drgn_test_slab_init(void) return 0; } +// kthread for stack trace + +static struct task_struct *drgn_test_kthread; +// Completion indicating that the kthread has set up its stack frames and is +// ready to be parked. +static DECLARE_COMPLETION(drgn_test_kthread_ready); +struct pt_regs drgn_test_kthread_pt_regs; +static inline void drgn_test_get_pt_regs(struct pt_regs *regs) +{ +#if defined(__aarch64__) + // Copied from crash_setup_regs() in arch/arm64/include/asm/kexec.h as + // of Linux v6.1. + u64 tmp1, tmp2; + + __asm__ __volatile__ ( + "stp x0, x1, [%2, #16 * 0]\n" + "stp x2, x3, [%2, #16 * 1]\n" + "stp x4, x5, [%2, #16 * 2]\n" + "stp x6, x7, [%2, #16 * 3]\n" + "stp x8, x9, [%2, #16 * 4]\n" + "stp x10, x11, [%2, #16 * 5]\n" + "stp x12, x13, [%2, #16 * 6]\n" + "stp x14, x15, [%2, #16 * 7]\n" + "stp x16, x17, [%2, #16 * 8]\n" + "stp x18, x19, [%2, #16 * 9]\n" + "stp x20, x21, [%2, #16 * 10]\n" + "stp x22, x23, [%2, #16 * 11]\n" + "stp x24, x25, [%2, #16 * 12]\n" + "stp x26, x27, [%2, #16 * 13]\n" + "stp x28, x29, [%2, #16 * 14]\n" + "mov %0, sp\n" + "stp x30, %0, [%2, #16 * 15]\n" + + "/* faked current PSTATE */\n" + "mrs %0, CurrentEL\n" + "mrs %1, SPSEL\n" + "orr %0, %0, %1\n" + "mrs %1, DAIF\n" + "orr %0, %0, %1\n" + "mrs %1, NZCV\n" + "orr %0, %0, %1\n" + /* pc */ + "adr %1, 1f\n" + "1:\n" + "stp %1, %0, [%2, #16 * 16]\n" + : "=&r" (tmp1), "=&r" (tmp2) + : "r" (regs) + : "memory" + ); +#elif defined(__powerpc64__) + unsigned long link; + unsigned long ccr; + + asm volatile("std 0,%0" : "=m"(regs->gpr[0])); + asm volatile("std 1,%0" : "=m"(regs->gpr[1])); + asm volatile("std 2,%0" : "=m"(regs->gpr[2])); + asm volatile("std 3,%0" : "=m"(regs->gpr[3])); + asm volatile("std 4,%0" : "=m"(regs->gpr[4])); + asm volatile("std 5,%0" : "=m"(regs->gpr[5])); + asm volatile("std 6,%0" : "=m"(regs->gpr[6])); + asm volatile("std 7,%0" : "=m"(regs->gpr[7])); + asm volatile("std 8,%0" : "=m"(regs->gpr[8])); + asm volatile("std 9,%0" : "=m"(regs->gpr[9])); + asm volatile("std 10,%0" : "=m"(regs->gpr[10])); + asm volatile("std 11,%0" : "=m"(regs->gpr[11])); + asm volatile("std 12,%0" : "=m"(regs->gpr[12])); + asm volatile("std 13,%0" : "=m"(regs->gpr[13])); + asm volatile("std 14,%0" : "=m"(regs->gpr[14])); + asm volatile("std 15,%0" : "=m"(regs->gpr[15])); + asm volatile("std 16,%0" : "=m"(regs->gpr[16])); + asm volatile("std 17,%0" : "=m"(regs->gpr[17])); + asm volatile("std 18,%0" : "=m"(regs->gpr[18])); + asm volatile("std 19,%0" : "=m"(regs->gpr[19])); + asm volatile("std 20,%0" : "=m"(regs->gpr[20])); + asm volatile("std 21,%0" : "=m"(regs->gpr[21])); + asm volatile("std 22,%0" : "=m"(regs->gpr[22])); + asm volatile("std 23,%0" : "=m"(regs->gpr[23])); + asm volatile("std 24,%0" : "=m"(regs->gpr[24])); + asm volatile("std 25,%0" : "=m"(regs->gpr[25])); + asm volatile("std 26,%0" : "=m"(regs->gpr[26])); + asm volatile("std 27,%0" : "=m"(regs->gpr[27])); + asm volatile("std 28,%0" : "=m"(regs->gpr[28])); + asm volatile("std 29,%0" : "=m"(regs->gpr[29])); + asm volatile("std 30,%0" : "=m"(regs->gpr[30])); + asm volatile("std 31,%0" : "=m"(regs->gpr[31])); + asm volatile("mflr %0" : "=r"(link)); + asm volatile("std %1,%0" : "=m"(regs->link) : "r"(link)); + asm volatile("mfcr %0" : "=r"(ccr)); + asm volatile("std %1,%0" : "=m"(regs->ccr) : "r"(ccr)); + regs->nip = _THIS_IP_; +#elif defined(__s390x__) + regs->psw.mask = __extract_psw(); + regs->psw.addr = _THIS_IP_; + asm volatile("stmg 0,15,%0\n" : "=S" (regs->gprs) : : "memory"); +#elif defined(__x86_64__) + // Copied from crash_setup_regs() in arch/x86/include/asm/kexec.h as of + // Linux v6.1. + asm volatile("movq %%rbx,%0" : "=m"(regs->bx)); + asm volatile("movq %%rcx,%0" : "=m"(regs->cx)); + asm volatile("movq %%rdx,%0" : "=m"(regs->dx)); + asm volatile("movq %%rsi,%0" : "=m"(regs->si)); + asm volatile("movq %%rdi,%0" : "=m"(regs->di)); + asm volatile("movq %%rbp,%0" : "=m"(regs->bp)); + asm volatile("movq %%rax,%0" : "=m"(regs->ax)); + asm volatile("movq %%rsp,%0" : "=m"(regs->sp)); + asm volatile("movq %%r8,%0" : "=m"(regs->r8)); + asm volatile("movq %%r9,%0" : "=m"(regs->r9)); + asm volatile("movq %%r10,%0" : "=m"(regs->r10)); + asm volatile("movq %%r11,%0" : "=m"(regs->r11)); + asm volatile("movq %%r12,%0" : "=m"(regs->r12)); + asm volatile("movq %%r13,%0" : "=m"(regs->r13)); + asm volatile("movq %%r14,%0" : "=m"(regs->r14)); + asm volatile("movq %%r15,%0" : "=m"(regs->r15)); + asm volatile("movl %%ss, %%eax;" :"=a"(regs->ss)); + asm volatile("movl %%cs, %%eax;" :"=a"(regs->cs)); + asm volatile("pushfq; popq %0" :"=m"(regs->flags)); + regs->ip = _THIS_IP_; +#endif +} + + __attribute__((__optimize__("O0"))) +static void drgn_test_kthread_fn3(void) +{ + // Create some local variables for the test cases to use. Use volatile + // to make doubly sure that they aren't optimized out. + volatile int a, b, c; + a = 1; + b = 2; + c = 3; + + complete(&drgn_test_kthread_ready); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (kthread_should_stop()) { + __set_current_state(TASK_RUNNING); + break; + } + if (kthread_should_park()) { + __set_current_state(TASK_RUNNING); + drgn_test_get_pt_regs(&drgn_test_kthread_pt_regs); + kthread_parkme(); + continue; + } + schedule(); + __set_current_state(TASK_RUNNING); + } +} + + __attribute__((__optimize__("O0"))) +static void drgn_test_kthread_fn2(void) +{ + drgn_test_kthread_fn3(); +} + + __attribute__((__optimize__("O0"))) +static int drgn_test_kthread_fn(void *arg) +{ + drgn_test_kthread_fn2(); + return 0; +} + +static void drgn_test_stack_trace_exit(void) +{ + if (drgn_test_kthread) { + kthread_stop(drgn_test_kthread); + drgn_test_kthread = NULL; + } +} + +static int drgn_test_stack_trace_init(void) +{ + drgn_test_kthread = kthread_create(drgn_test_kthread_fn, NULL, + "drgn_test_kthread"); + if (!drgn_test_kthread) + return -1; + wake_up_process(drgn_test_kthread); + wait_for_completion(&drgn_test_kthread_ready); + return kthread_park(drgn_test_kthread); +} + +// radixtree + +RADIX_TREE(drgn_test_radix_tree_empty, GFP_KERNEL); +RADIX_TREE(drgn_test_radix_tree_one, GFP_KERNEL); +RADIX_TREE(drgn_test_radix_tree_one_at_zero, GFP_KERNEL); +RADIX_TREE(drgn_test_radix_tree_sparse, GFP_KERNEL); +#ifdef CONFIG_RADIX_TREE_MULTIORDER +RADIX_TREE(drgn_test_radix_tree_multi_order, GFP_KERNEL); +#endif + +static int drgn_test_radix_tree_init(void) +{ + int ret; + + ret = radix_tree_insert(&drgn_test_radix_tree_one, 666, + (void *)0xdeadb00); + if (ret) + return ret; + + ret = radix_tree_insert(&drgn_test_radix_tree_one_at_zero, 0, + (void *)0x1234); + if (ret) + return ret; + + ret = radix_tree_insert(&drgn_test_radix_tree_sparse, 1, + (void *)0x1234); + if (ret) + return ret; + + ret = radix_tree_insert(&drgn_test_radix_tree_sparse, 0x80808080, + (void *)0x5678); + if (ret) + return ret; + + ret = radix_tree_insert(&drgn_test_radix_tree_sparse, 0xffffffff, + (void *)0x9abc); + if (ret) + return ret; + +#ifdef CONFIG_RADIX_TREE_MULTIORDER + ret = __radix_tree_insert(&drgn_test_radix_tree_multi_order, 0x80808000, + 9, (void *)0x1234); + if (ret) + return ret; +#endif + + return 0; +} + +static void drgn_test_radix_tree_destroy(struct radix_tree_root *root) +{ + struct radix_tree_iter iter; + void __rcu **slot; + + radix_tree_for_each_slot(slot, root, &iter, 0) + radix_tree_delete(root, iter.index); +} + +static void drgn_test_radix_tree_exit(void) +{ + drgn_test_radix_tree_destroy(&drgn_test_radix_tree_one); + drgn_test_radix_tree_destroy(&drgn_test_radix_tree_one_at_zero); + drgn_test_radix_tree_destroy(&drgn_test_radix_tree_sparse); +#ifdef CONFIG_RADIX_TREE_MULTIORDER + drgn_test_radix_tree_destroy(&drgn_test_radix_tree_multi_order); +#endif +} + +// xarray +const int drgn_test_have_xarray = HAVE_XARRAY; +#if HAVE_XARRAY +DEFINE_XARRAY(drgn_test_xarray_empty); +DEFINE_XARRAY(drgn_test_xarray_one); +DEFINE_XARRAY(drgn_test_xarray_one_at_zero); +DEFINE_XARRAY(drgn_test_xarray_sparse); +DEFINE_XARRAY(drgn_test_xarray_multi_index); +DEFINE_XARRAY(drgn_test_xarray_zero_entry); +DEFINE_XARRAY(drgn_test_xarray_zero_entry_at_zero); +DEFINE_XARRAY(drgn_test_xarray_value); +void *drgn_test_xa_zero_entry; + +static int drgn_test_xa_store_order(struct xarray *xa, unsigned long index, + unsigned order, void *entry, gfp_t gfp) +{ + XA_STATE_ORDER(xas, xa, index, order); + + do { + xas_lock(&xas); + xas_store(&xas, entry); + xas_unlock(&xas); + } while (xas_nomem(&xas, gfp)); + return xas_error(&xas); +} +#endif + +static int drgn_test_xarray_init(void) +{ +#if HAVE_XARRAY + void *entry; + int ret; + + drgn_test_xa_zero_entry = XA_ZERO_ENTRY; + + entry = xa_store(&drgn_test_xarray_one, 666, (void *)0xdeadb00, + GFP_KERNEL); + if (xa_is_err(entry)) + return xa_err(entry); + + entry = xa_store(&drgn_test_xarray_one_at_zero, 0, (void *)0x1234, + GFP_KERNEL); + if (xa_is_err(entry)) + return xa_err(entry); + + entry = xa_store(&drgn_test_xarray_sparse, 1, (void *)0x1234, + GFP_KERNEL); + if (xa_is_err(entry)) + return xa_err(entry); + entry = xa_store(&drgn_test_xarray_sparse, 0x80808080, (void *)0x5678, + GFP_KERNEL); + if (xa_is_err(entry)) + return xa_err(entry); + entry = xa_store(&drgn_test_xarray_sparse, 0xffffffffUL, (void *)0x9abc, + GFP_KERNEL); + if (xa_is_err(entry)) + return xa_err(entry); + + ret = drgn_test_xa_store_order(&drgn_test_xarray_multi_index, + 0x80808000, 9, (void *)0x1234, + GFP_KERNEL); + if (ret) + return ret; + + ret = xa_reserve(&drgn_test_xarray_zero_entry, 666, GFP_KERNEL); + if (ret) + return ret; + + ret = xa_reserve(&drgn_test_xarray_zero_entry_at_zero, 0, GFP_KERNEL); + if (ret) + return ret; + + entry = xa_store(&drgn_test_xarray_value, 0, xa_mk_value(1337), + GFP_KERNEL); + if (xa_is_err(entry)) + return xa_err(entry); + +#endif + + return 0; +} + +static void drgn_test_xarray_exit(void) +{ +#if HAVE_XARRAY + xa_destroy(&drgn_test_xarray_one); + xa_destroy(&drgn_test_xarray_one_at_zero); + xa_destroy(&drgn_test_xarray_sparse); + xa_destroy(&drgn_test_xarray_multi_index); + xa_destroy(&drgn_test_xarray_zero_entry); + xa_destroy(&drgn_test_xarray_zero_entry_at_zero); + xa_destroy(&drgn_test_xarray_value); +#endif +} + +// idr + +DEFINE_IDR(drgn_test_idr_empty); +DEFINE_IDR(drgn_test_idr_one); +DEFINE_IDR(drgn_test_idr_one_at_zero); +DEFINE_IDR(drgn_test_idr_sparse); + +static int drgn_test_idr_init(void) +{ + int ret; + + ret = idr_alloc(&drgn_test_idr_one, (void *)0xdeadb00, 66, 67, + GFP_KERNEL); + if (ret < 0) + return ret; + + ret = idr_alloc(&drgn_test_idr_one_at_zero, (void *)0x1234, 0, 1, + GFP_KERNEL); + if (ret < 0) + return ret; + + ret = idr_alloc(&drgn_test_idr_sparse, (void *)0x1234, 1, 2, + GFP_KERNEL); + if (ret < 0) + return ret; + + ret = idr_alloc(&drgn_test_idr_sparse, (void *)0x5678, 0x80, 0x81, + GFP_KERNEL); + if (ret < 0) + return ret; + + ret = idr_alloc(&drgn_test_idr_sparse, (void *)0x9abc, 0xee, 0xef, + GFP_KERNEL); + if (ret < 0) + return ret; + + return 0; +} + +static void drgn_test_idr_exit(void) +{ + idr_destroy(&drgn_test_idr_one); + idr_destroy(&drgn_test_idr_one_at_zero); + idr_destroy(&drgn_test_idr_sparse); +} + // Dummy function symbol. int drgn_test_function(int x) { @@ -396,6 +858,11 @@ static void drgn_test_exit(void) drgn_test_slab_exit(); drgn_test_percpu_exit(); drgn_test_mm_exit(); + drgn_test_net_exit(); + drgn_test_stack_trace_exit(); + drgn_test_radix_tree_exit(); + drgn_test_xarray_exit(); + drgn_test_idr_exit(); } static int __init drgn_test_init(void) @@ -405,6 +872,9 @@ static int __init drgn_test_init(void) drgn_test_list_init(); drgn_test_llist_init(); ret = drgn_test_mm_init(); + if (ret) + goto out; + ret = drgn_test_net_init(); if (ret) goto out; ret = drgn_test_percpu_init(); @@ -412,6 +882,18 @@ static int __init drgn_test_init(void) goto out; drgn_test_rbtree_init(); ret = drgn_test_slab_init(); + if (ret) + goto out; + ret = drgn_test_stack_trace_init(); + if (ret) + goto out; + ret = drgn_test_radix_tree_init(); + if (ret) + goto out; + ret = drgn_test_xarray_init(); + if (ret) + goto out; + ret = drgn_test_idr_init(); out: if (ret) drgn_test_exit(); diff --git a/tests/linux_kernel/test_debug_info.py b/tests/linux_kernel/test_debug_info.py index 0b637c533..c3bbcad34 100644 --- a/tests/linux_kernel/test_debug_info.py +++ b/tests/linux_kernel/test_debug_info.py @@ -1,11 +1,11 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import os from pathlib import Path import unittest -from drgn import Program +from drgn import Object, Program from tests.linux_kernel import LinuxKernelTestCase, setenv, skip_unless_have_test_kmod KALLSYMS_PATH = Path("/proc/kallsyms") @@ -47,3 +47,15 @@ def test_module_debug_info_use_proc_and_sys(self): def test_module_debug_info_use_core_dump(self): self._test_module_debug_info(False) + + +class TestLinuxKernelObjectFinder(LinuxKernelTestCase): + def test_jiffies(self): + self.assertIdentical( + self.prog["jiffies"], + Object( + self.prog, + "volatile unsigned long", + address=self.prog.symbol("jiffies").address, + ), + ) diff --git a/tests/linux_kernel/test_special_objects.py b/tests/linux_kernel/test_special_objects.py new file mode 100644 index 000000000..4e282fd82 --- /dev/null +++ b/tests/linux_kernel/test_special_objects.py @@ -0,0 +1,43 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +import os + +import drgn +from tests.linux_kernel import LinuxKernelTestCase + + +class TestUts(LinuxKernelTestCase): + def test_uts_release(self): + self.assertEqual( + self.prog["UTS_RELEASE"].string_().decode(), os.uname().release + ) + + def test_uts_release_no_debug_info(self): + prog = drgn.Program() + prog.set_kernel() + self.assertEqual(prog["UTS_RELEASE"].string_().decode(), os.uname().release) + + +class TestVmcoreinfo(LinuxKernelTestCase): + def test_vmcoreinfo(self): + vmcoreinfo_data = dict( + line.split("=", 1) + for line in self.prog["VMCOREINFO"].string_().decode().strip().split("\n") + ) + self.assertEqual( + int(vmcoreinfo_data["SYMBOL(init_uts_ns)"], 16), + self.prog.symbol("init_uts_ns").address, + ) + + def test_vmcoreinfo_no_debug_info(self): + prog = drgn.Program() + prog.set_kernel() + vmcoreinfo_data = dict( + line.split("=", 1) + for line in prog["VMCOREINFO"].string_().decode().strip().split("\n") + ) + self.assertEqual( + vmcoreinfo_data["OSRELEASE"], + os.uname().release, + ) diff --git a/tests/linux_kernel/test_stack_trace.py b/tests/linux_kernel/test_stack_trace.py index 7e6bb52f4..620ff5870 100644 --- a/tests/linux_kernel/test_stack_trace.py +++ b/tests/linux_kernel/test_stack_trace.py @@ -1,34 +1,37 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import os -import signal +import unittest -from drgn import Object, Program, cast -from drgn.helpers.linux.pid import find_task +from drgn import Object, Program from tests import assertReprPrettyEqualsStr from tests.linux_kernel import ( LinuxKernelTestCase, - fork_and_pause, - proc_blocked, + fork_and_sigwait, setenv, - wait_until, + skip_unless_have_stack_tracing, + skip_unless_have_test_kmod, ) +from util import NORMALIZED_MACHINE_NAME +@skip_unless_have_stack_tracing class TestStackTrace(LinuxKernelTestCase): - def _assert_trace_paused(self, trace): - for frame in trace: - if "pause" in frame.name or "poll" in frame.name: - return - self.fail(f"pause frame not found in {str(trace)!r}") + def _test_drgn_test_kthread_trace(self, trace): + for i, frame in enumerate(trace): + if frame.name == "drgn_test_kthread_fn3": + break + else: + self.fail("Couldn't find drgn_test_kthread_fn3 frame") + self.assertEqual(trace[i + 1].name, "drgn_test_kthread_fn2") + self.assertEqual(trace[i + 2].name, "drgn_test_kthread_fn") + @skip_unless_have_test_kmod def test_by_task_struct(self): - pid = fork_and_pause() - wait_until(proc_blocked, pid) - self._assert_trace_paused(self.prog.stack_trace(find_task(self.prog, pid))) - os.kill(pid, signal.SIGKILL) - os.waitpid(pid, 0) + self._test_drgn_test_kthread_trace( + self.prog.stack_trace(self.prog["drgn_test_kthread"]) + ) def _test_by_pid(self, orc): old_orc = int(os.environ.get("DRGN_PREFER_ORC_UNWINDER", "0")) != 0 @@ -39,59 +42,68 @@ def _test_by_pid(self, orc): prog = Program() prog.set_kernel() self._load_debug_info(prog) - pid = fork_and_pause() - wait_until(proc_blocked, pid) - self._assert_trace_paused(prog.stack_trace(pid)) - os.kill(pid, signal.SIGKILL) - os.waitpid(pid, 0) + self._test_drgn_test_kthread_trace( + prog.stack_trace(prog["drgn_test_kthread"].pid) + ) + @skip_unless_have_test_kmod def test_by_pid_dwarf(self): self._test_by_pid(False) + @unittest.skipUnless( + NORMALIZED_MACHINE_NAME == "x86_64", + f"{NORMALIZED_MACHINE_NAME} does not use ORC", + ) + @skip_unless_have_test_kmod def test_by_pid_orc(self): self._test_by_pid(True) + @skip_unless_have_test_kmod + def test_by_pt_regs(self): + pt_regs = self.prog["drgn_test_kthread_pt_regs"] + self._test_drgn_test_kthread_trace(self.prog.stack_trace(pt_regs)) + self._test_drgn_test_kthread_trace(self.prog.stack_trace(pt_regs.address_of_())) + + @skip_unless_have_test_kmod def test_local_variable(self): - pid = fork_and_pause() - wait_until(proc_blocked, pid) - for frame in self.prog.stack_trace(pid): - if frame.name in ("context_switch", "__schedule"): - try: - prev = frame["prev"] - except KeyError: - continue - if not prev.absent_: - self.assertEqual(prev.pid, pid) - break + for frame in self.prog.stack_trace(self.prog["drgn_test_kthread"]): + if frame.name == "drgn_test_kthread_fn3": + break else: - self.skipTest("prev not found in context_switch or __schedule") - os.kill(pid, signal.SIGKILL) - os.waitpid(pid, 0) + self.fail("Couldn't find drgn_test_kthread_fn3 frame") + self.assertEqual(frame["a"], 1) + self.assertEqual(frame["b"], 2) + self.assertEqual(frame["c"], 3) - def test_pt_regs(self): - # This won't unwind anything useful, but at least make sure it accepts - # a struct pt_regs. - self.prog.stack_trace(Object(self.prog, "struct pt_regs", value={})) - - # Likewise, this is nonsense, but we should also accept a struct - # pt_regs *. - task = find_task(self.prog, os.getpid()) - self.prog.stack_trace(cast("struct pt_regs *", task.stack)) + @skip_unless_have_test_kmod + def test_locals(self): + task = self.prog["drgn_test_kthread"] + stack_trace = self.prog.stack_trace(task) + for frame in stack_trace: + if frame.name == "drgn_test_kthread_fn3": + self.assertSetEqual(set(frame.locals()), {"a", "b", "c"}) + break + else: + self.fail("Couldn't find drgn_test_kthread_fn3 frame") def test_registers(self): # Smoke test that we get at least one register and that # StackFrame.registers() agrees with StackFrame.register(). - pid = fork_and_pause() - wait_until(proc_blocked, pid) - trace = self.prog.stack_trace(pid) - have_registers = False - for frame in trace: - for name, value in frame.registers().items(): - self.assertEqual(frame.register(name), value) - have_registers = True - self.assertTrue(have_registers) - os.kill(pid, signal.SIGKILL) - os.waitpid(pid, 0) + with fork_and_sigwait() as pid: + trace = self.prog.stack_trace(pid) + have_registers = False + for frame in trace: + for name, value in frame.registers().items(): + self.assertEqual(frame.register(name), value) + have_registers = True + self.assertTrue(have_registers) + + def test_sp(self): + # Smoke test that the stack pointer register shows up in + # StackFrame.registers(). + with fork_and_sigwait() as pid: + trace = self.prog.stack_trace(pid) + self.assertIn(trace[0].sp, trace[0].registers().values()) def test_prog(self): self.assertEqual( @@ -100,11 +112,8 @@ def test_prog(self): ) def test_stack__repr_pretty_(self): - pid = fork_and_pause() - wait_until(proc_blocked, pid) - trace = self.prog.stack_trace(pid) - assertReprPrettyEqualsStr(trace) - for frame in trace: - assertReprPrettyEqualsStr(frame) - os.kill(pid, signal.SIGKILL) - os.waitpid(pid, 0) + with fork_and_sigwait() as pid: + trace = self.prog.stack_trace(pid) + assertReprPrettyEqualsStr(trace) + for frame in trace: + assertReprPrettyEqualsStr(frame) diff --git a/tests/linux_kernel/test_symbol.py b/tests/linux_kernel/test_symbol.py index c85676b51..702e874af 100644 --- a/tests/linux_kernel/test_symbol.py +++ b/tests/linux_kernel/test_symbol.py @@ -1,5 +1,5 @@ # Copyright (c) 2021, Oracle and/or its affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from drgn import SymbolBinding, SymbolKind from tests.linux_kernel import LinuxKernelTestCase diff --git a/tests/linux_kernel/test_threads.py b/tests/linux_kernel/test_threads.py index 232230422..b49214805 100644 --- a/tests/linux_kernel/test_threads.py +++ b/tests/linux_kernel/test_threads.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from multiprocessing import Barrier, Process import os diff --git a/tests/linux_kernel/test_uts.py b/tests/linux_kernel/test_uts.py deleted file mode 100644 index ef165124b..000000000 --- a/tests/linux_kernel/test_uts.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later - -import os - -import drgn -from tests.linux_kernel import LinuxKernelTestCase - - -class TestUts(LinuxKernelTestCase): - def test_uts_release(self): - self.assertEqual( - self.prog["UTS_RELEASE"].string_().decode(), os.uname().release - ) - - def test_uts_release_no_debug_info(self): - prog = drgn.Program() - prog.set_kernel() - self.assertEqual(prog["UTS_RELEASE"].string_().decode(), os.uname().release) diff --git a/tests/linux_kernel/vmcore/__init__.py b/tests/linux_kernel/vmcore/__init__.py index deb4cb61f..206128ed8 100644 --- a/tests/linux_kernel/vmcore/__init__.py +++ b/tests/linux_kernel/vmcore/__init__.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from pathlib import Path import unittest diff --git a/tests/linux_kernel/vmcore/test_vmcore.py b/tests/linux_kernel/vmcore/test_vmcore.py index 1a8856ad5..1435c2399 100644 --- a/tests/linux_kernel/vmcore/test_vmcore.py +++ b/tests/linux_kernel/vmcore/test_vmcore.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from drgn import ProgramFlags from drgn.helpers.linux.pid import find_task @@ -40,4 +40,6 @@ def test_main_thread(self): def test_crashed_thread(self): crashed_thread = self.prog.crashed_thread() - self.assertGreater(crashed_thread.tid, 0) + # This assumes that we crashed from vmtest.enter_kdump. I don't know + # why anyone would run these tests from kdump otherwise. + self.assertEqual(crashed_thread.object.comm.string_(), b"selfdestruct") diff --git a/tests/test_docs.py b/tests/test_docs.py index 5b0981301..0b8b6789e 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import pydoc import drgn diff --git a/tests/test_dwarf.py b/tests/test_dwarf.py index d089c6429..215ecf4ff 100644 --- a/tests/test_dwarf.py +++ b/tests/test_dwarf.py @@ -1,7 +1,8 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import functools +import logging import operator import os.path import re @@ -29,8 +30,14 @@ identical, ) import tests.assembler as assembler -from tests.dwarf import DW_AT, DW_ATE, DW_END, DW_FORM, DW_LANG, DW_OP, DW_TAG -from tests.dwarfwriter import DwarfAttrib, DwarfDie, DwarfLabel, compile_dwarf +from tests.dwarf import DW_AT, DW_ATE, DW_END, DW_FORM, DW_LANG, DW_OP, DW_TAG, DW_UT +from tests.dwarfwriter import ( + DwarfAttrib, + DwarfDie, + DwarfLabel, + DwarfUnit, + compile_dwarf, +) bool_die = DwarfDie( DW_TAG.base_type, @@ -190,6 +197,7 @@ def dwarf_program(*args, segments=None, **kwds): f.write(compile_dwarf(*args, **kwds)) f.flush() prog.load_debug_info([f.name]) + if segments is not None: add_mock_memory_segments(prog, segments) return prog @@ -724,84 +732,222 @@ def test_struct_missing_size(self): ) def test_incomplete_to_complete(self): - prog = dwarf_program( - wrap_test_type_dies( - DwarfDie( - DW_TAG.pointer_type, - ( - DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8), - DwarfAttrib(DW_AT.type, DW_FORM.ref4, "incomplete_struct_die"), - ), - ), - DwarfLabel("incomplete_struct_die"), - DwarfDie( - DW_TAG.structure_type, - ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "point"), - DwarfAttrib(DW_AT.declaration, DW_FORM.flag_present, True), - ), - ), - DwarfDie( - DW_TAG.structure_type, - ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "point"), - DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8), - DwarfAttrib(DW_AT.decl_file, DW_FORM.udata, "foo.c"), - ), - ( + for version in (4, 5): + with self.subTest(version=version): + prog = dwarf_program( + wrap_test_type_dies( DwarfDie( - DW_TAG.member, + DW_TAG.pointer_type, ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "x"), + DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8), DwarfAttrib( - DW_AT.data_member_location, DW_FORM.data1, 0 + DW_AT.type, DW_FORM.ref4, "incomplete_struct_die" ), - DwarfAttrib(DW_AT.type, DW_FORM.ref4, "int_die"), ), ), + DwarfLabel("incomplete_struct_die"), DwarfDie( - DW_TAG.member, + DW_TAG.structure_type, ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "y"), + DwarfAttrib(DW_AT.name, DW_FORM.string, "point"), DwarfAttrib( - DW_AT.data_member_location, DW_FORM.data1, 4 + DW_AT.declaration, DW_FORM.flag_present, True ), - DwarfAttrib(DW_AT.type, DW_FORM.ref4, "int_die"), ), ), + DwarfDie( + DW_TAG.structure_type, + ( + DwarfAttrib(DW_AT.name, DW_FORM.string, "point"), + DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8), + DwarfAttrib(DW_AT.decl_file, DW_FORM.udata, "foo.c"), + ), + ( + DwarfDie( + DW_TAG.member, + ( + DwarfAttrib(DW_AT.name, DW_FORM.string, "x"), + DwarfAttrib( + DW_AT.data_member_location, DW_FORM.data1, 0 + ), + DwarfAttrib( + DW_AT.type, DW_FORM.ref4, "int_die" + ), + ), + ), + DwarfDie( + DW_TAG.member, + ( + DwarfAttrib(DW_AT.name, DW_FORM.string, "y"), + DwarfAttrib( + DW_AT.data_member_location, DW_FORM.data1, 4 + ), + DwarfAttrib( + DW_AT.type, DW_FORM.ref4, "int_die" + ), + ), + ), + ), + ), + *labeled_int_die, ), - ), - *labeled_int_die, - ) - ) - self.assertIdentical( - prog.type("TEST").type, - prog.pointer_type( - prog.struct_type( - "point", - 8, - ( - TypeMember(prog.int_type("int", 4, True), "x"), - TypeMember(prog.int_type("int", 4, True), "y", 32), + version=version, + ) + self.assertIdentical( + prog.type("TEST").type, + prog.pointer_type( + prog.struct_type( + "point", + 8, + ( + TypeMember(prog.int_type("int", 4, True), "x"), + TypeMember(prog.int_type("int", 4, True), "y", 32), + ), + ) ), ) - ), - ) def test_incomplete_to_complete_namespace(self): - prog = dwarf_program( - wrap_test_type_dies( - DwarfDie( - DW_TAG.pointer_type, - ( - DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8), - DwarfAttrib(DW_AT.type, DW_FORM.ref4, "incomplete_struct_die"), + for version in (4, 5): + with self.subTest(version=version): + prog = dwarf_program( + wrap_test_type_dies( + DwarfDie( + DW_TAG.pointer_type, + ( + DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8), + DwarfAttrib( + DW_AT.type, DW_FORM.ref4, "incomplete_struct_die" + ), + ), + ), + DwarfDie( + DW_TAG.namespace, + (DwarfAttrib(DW_AT.name, DW_FORM.string, "Math"),), + ( + DwarfLabel("incomplete_struct_die"), + DwarfDie( + DW_TAG.structure_type, + ( + DwarfAttrib( + DW_AT.name, DW_FORM.string, "point" + ), + DwarfAttrib( + DW_AT.declaration, + DW_FORM.flag_present, + True, + ), + ), + ), + ), + ), + DwarfDie( + DW_TAG.namespace, + (DwarfAttrib(DW_AT.name, DW_FORM.string, "Math"),), + ( + DwarfDie( + DW_TAG.structure_type, + ( + DwarfAttrib( + DW_AT.name, DW_FORM.string, "point" + ), + DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8), + DwarfAttrib( + DW_AT.decl_file, DW_FORM.udata, "foo.c" + ), + ), + ( + DwarfDie( + DW_TAG.member, + ( + DwarfAttrib( + DW_AT.name, DW_FORM.string, "x" + ), + DwarfAttrib( + DW_AT.data_member_location, + DW_FORM.data1, + 0, + ), + DwarfAttrib( + DW_AT.type, DW_FORM.ref4, "int_die" + ), + ), + ), + DwarfDie( + DW_TAG.member, + ( + DwarfAttrib( + DW_AT.name, DW_FORM.string, "y" + ), + DwarfAttrib( + DW_AT.data_member_location, + DW_FORM.data1, + 4, + ), + DwarfAttrib( + DW_AT.type, DW_FORM.ref4, "int_die" + ), + ), + ), + ), + ), + ), + ), + *labeled_int_die, + # Incorrect structure we should not access + DwarfDie( + DW_TAG.structure_type, + ( + DwarfAttrib(DW_AT.name, DW_FORM.string, "point"), + DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8), + DwarfAttrib(DW_AT.decl_file, DW_FORM.udata, "wrong.c"), + ), + ), ), - ), - DwarfDie( - DW_TAG.namespace, - (DwarfAttrib(DW_AT.name, DW_FORM.string, "Math"),), - ( + lang=DW_LANG.C_plus_plus, + version=version, + ) + self.assertIdentical( + prog.type("TEST").type, + prog.pointer_type( + prog.struct_type( + "point", + 8, + ( + TypeMember( + prog.int_type( + "int", 4, True, language=Language.CPP + ), + "x", + ), + TypeMember( + prog.int_type( + "int", 4, True, language=Language.CPP + ), + "y", + 32, + ), + ), + language=Language.CPP, + ), + language=Language.CPP, + ), + ) + + def test_incomplete_to_complete_specification(self): + for version in (4, 5): + with self.subTest(version=version): + prog = dwarf_program( + wrap_test_type_dies( + DwarfDie( + DW_TAG.pointer_type, + ( + DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8), + DwarfAttrib( + DW_AT.type, DW_FORM.ref4, "incomplete_struct_die" + ), + ), + ), DwarfLabel("incomplete_struct_die"), DwarfDie( DW_TAG.structure_type, @@ -812,18 +958,15 @@ def test_incomplete_to_complete_namespace(self): ), ), ), - ), - ), - DwarfDie( - DW_TAG.namespace, - (DwarfAttrib(DW_AT.name, DW_FORM.string, "Math"),), - ( DwarfDie( DW_TAG.structure_type, ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "point"), + DwarfAttrib( + DW_AT.specification, + DW_FORM.ref4, + "incomplete_struct_die", + ), DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8), - DwarfAttrib(DW_AT.decl_file, DW_FORM.udata, "foo.c"), ), ( DwarfDie( @@ -831,9 +974,7 @@ def test_incomplete_to_complete_namespace(self): ( DwarfAttrib(DW_AT.name, DW_FORM.string, "x"), DwarfAttrib( - DW_AT.data_member_location, - DW_FORM.data1, - 0, + DW_AT.data_member_location, DW_FORM.data1, 0 ), DwarfAttrib( DW_AT.type, DW_FORM.ref4, "int_die" @@ -845,9 +986,7 @@ def test_incomplete_to_complete_namespace(self): ( DwarfAttrib(DW_AT.name, DW_FORM.string, "y"), DwarfAttrib( - DW_AT.data_member_location, - DW_FORM.data1, - 4, + DW_AT.data_member_location, DW_FORM.data1, 4 ), DwarfAttrib( DW_AT.type, DW_FORM.ref4, "int_die" @@ -856,192 +995,136 @@ def test_incomplete_to_complete_namespace(self): ), ), ), + *labeled_int_die, ), - ), - *labeled_int_die, - # Incorrect structure we should not access - DwarfDie( - DW_TAG.structure_type, - ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "point"), - DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8), - DwarfAttrib(DW_AT.decl_file, DW_FORM.udata, "wrong.c"), - ), - ), - ), - lang=DW_LANG.C_plus_plus, - ) - self.assertIdentical( - prog.type("TEST").type, - prog.pointer_type( - prog.struct_type( - "point", - 8, - ( - TypeMember( - prog.int_type("int", 4, True, language=Language.CPP), "x" - ), - TypeMember( - prog.int_type("int", 4, True, language=Language.CPP), - "y", - 32, - ), + version=version, + ) + self.assertIdentical( + prog.type("TEST").type, + prog.pointer_type( + prog.struct_type( + "point", + 8, + ( + TypeMember(prog.int_type("int", 4, True), "x"), + TypeMember(prog.int_type("int", 4, True), "y", 32), + ), + ) ), - language=Language.CPP, - ), - language=Language.CPP, - ), - ) + ) - def test_incomplete_to_complete_ambiguous(self): + def test_incomplete_to_complete_nested(self): prog = dwarf_program( wrap_test_type_dies( DwarfDie( DW_TAG.pointer_type, ( DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8), - DwarfAttrib(DW_AT.type, DW_FORM.ref4, "incomplete_struct_die"), + DwarfAttrib(DW_AT.type, DW_FORM.ref4, "incomplete_class_die"), ), ), - DwarfLabel("incomplete_struct_die"), DwarfDie( - DW_TAG.structure_type, - ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "point"), - DwarfAttrib(DW_AT.declaration, DW_FORM.flag_present, True), - ), - ), - DwarfDie( - DW_TAG.structure_type, + DW_TAG.class_type, ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "point"), - DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8), - DwarfAttrib(DW_AT.decl_file, DW_FORM.udata, "foo.c"), + DwarfAttrib(DW_AT.name, DW_FORM.string, "Foo"), + DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 0), ), ( + DwarfLabel("incomplete_class_die"), DwarfDie( - DW_TAG.member, + DW_TAG.class_type, ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "x"), + DwarfAttrib(DW_AT.name, DW_FORM.string, "Bar"), DwarfAttrib( - DW_AT.data_member_location, DW_FORM.data1, 0 + DW_AT.declaration, DW_FORM.flag_present, True ), - DwarfAttrib(DW_AT.type, DW_FORM.ref4, "int_die"), ), ), DwarfDie( - DW_TAG.member, + DW_TAG.class_type, ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "y"), - DwarfAttrib( - DW_AT.data_member_location, DW_FORM.data1, 4 - ), - DwarfAttrib(DW_AT.type, DW_FORM.ref4, "int_die"), + DwarfAttrib(DW_AT.name, DW_FORM.string, "Bar"), + DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 0), ), ), ), ), - *labeled_int_die, DwarfDie( - DW_TAG.structure_type, - ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "point"), - DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8), - DwarfAttrib(DW_AT.decl_file, DW_FORM.udata, "bar.c"), - ), + DW_TAG.class_type, ( - DwarfDie( - DW_TAG.member, - ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "a"), - DwarfAttrib( - DW_AT.data_member_location, DW_FORM.data1, 0 - ), - DwarfAttrib(DW_AT.type, DW_FORM.ref4, "int_die"), - ), - ), - DwarfDie( - DW_TAG.member, - ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "b"), - DwarfAttrib( - DW_AT.data_member_location, DW_FORM.data1, 4 - ), - DwarfAttrib(DW_AT.type, DW_FORM.ref4, "int_die"), - ), - ), + DwarfAttrib(DW_AT.name, DW_FORM.string, "Bar"), + DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 1), ), ), - ) + DwarfDie( + DW_TAG.subprogram, + (DwarfAttrib(DW_AT.name, DW_FORM.string, "main"),), + ), + ), + lang=DW_LANG.C_plus_plus, ) self.assertIdentical( - prog.type("TEST").type, prog.pointer_type(prog.struct_type("point")) + prog.type("TEST").type.type, + prog.class_type("Bar", 0, ()), ) - def test_incomplete_to_complete_specification(self): + def test_incomplete_to_complete_nested_specification(self): prog = dwarf_program( wrap_test_type_dies( DwarfDie( DW_TAG.pointer_type, ( DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8), - DwarfAttrib(DW_AT.type, DW_FORM.ref4, "incomplete_struct_die"), - ), - ), - DwarfLabel("incomplete_struct_die"), - DwarfDie( - DW_TAG.structure_type, - ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "point"), - DwarfAttrib(DW_AT.declaration, DW_FORM.flag_present, True), + DwarfAttrib(DW_AT.type, DW_FORM.ref4, "incomplete_class_die"), ), ), DwarfDie( - DW_TAG.structure_type, + DW_TAG.class_type, ( - DwarfAttrib( - DW_AT.specification, DW_FORM.ref4, "incomplete_struct_die" - ), - DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8), + DwarfAttrib(DW_AT.name, DW_FORM.string, "Foo"), + DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 0), ), ( + DwarfLabel("incomplete_class_die"), DwarfDie( - DW_TAG.member, + DW_TAG.class_type, ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "x"), + DwarfAttrib(DW_AT.name, DW_FORM.string, "Bar"), DwarfAttrib( - DW_AT.data_member_location, DW_FORM.data1, 0 + DW_AT.declaration, DW_FORM.flag_present, True ), - DwarfAttrib(DW_AT.type, DW_FORM.ref4, "int_die"), ), ), DwarfDie( - DW_TAG.member, + DW_TAG.class_type, ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "y"), DwarfAttrib( - DW_AT.data_member_location, DW_FORM.data1, 4 + DW_AT.specification, + DW_FORM.ref4, + "incomplete_class_die", ), - DwarfAttrib(DW_AT.type, DW_FORM.ref4, "int_die"), + DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 0), ), ), ), ), - *labeled_int_die, - ) - ) - self.assertIdentical( - prog.type("TEST").type, - prog.pointer_type( - prog.struct_type( - "point", - 8, + DwarfDie( + DW_TAG.class_type, ( - TypeMember(prog.int_type("int", 4, True), "x"), - TypeMember(prog.int_type("int", 4, True), "y", 32), + DwarfAttrib(DW_AT.name, DW_FORM.string, "Bar"), + DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 1), ), - ) + ), + DwarfDie( + DW_TAG.subprogram, + (DwarfAttrib(DW_AT.name, DW_FORM.string, "main"),), + ), ), + lang=DW_LANG.C_plus_plus, + ) + self.assertIdentical( + prog.type("TEST").type.type, + prog.class_type("Bar", 0, ()), ) def test_filename(self): @@ -1680,6 +1763,63 @@ def test_template_value_parameter_missing_value(self): ) ).type("TEST").type.template_parameters[0].argument + def test_class_template_parameter_pack(self): + prog = dwarf_program( + wrap_test_type_dies( + DwarfDie( + DW_TAG.class_type, + ( + DwarfAttrib(DW_AT.name, DW_FORM.string, "ParamPack"), + DwarfAttrib(DW_AT.declaration, DW_FORM.flag_present, True), + ), + ( + DwarfDie( + DW_TAG.GNU_template_parameter_pack, + (DwarfAttrib(DW_AT.name, DW_FORM.string, "Params"),), + ( + DwarfDie( + DW_TAG.template_type_parameter, + ( + DwarfAttrib( + DW_AT.type, DW_FORM.ref4, "int_die" + ), + DwarfAttrib(DW_AT.name, DW_FORM.string, "T"), + ), + ), + char_die, # Unexpected die - should be ignored + DwarfDie( + DW_TAG.template_value_parameter, + ( + DwarfAttrib( + DW_AT.type, DW_FORM.ref4, "unsigned_int_die" + ), + DwarfAttrib(DW_AT.name, DW_FORM.string, "N"), + DwarfAttrib( + DW_AT.const_value, DW_FORM.data1, 2 + ), + ), + ), + ), + ), + ), + ), + *labeled_int_die, + *labeled_unsigned_int_die, + ) + ) + self.assertIdentical( + prog.type("TEST").type, + prog.class_type( + "ParamPack", + template_parameters=( + TypeTemplateParameter(prog.int_type("int", 4, True), "T"), + TypeTemplateParameter( + Object(prog, prog.int_type("unsigned int", 4, False), 2), "N" + ), + ), + ), + ) + def test_lazy_cycle(self): prog = dwarf_program( wrap_test_type_dies( @@ -2523,6 +2663,32 @@ def test_array_zero_length_upper_bound(self): prog.type("TEST").type, prog.array_type(prog.int_type("int", 4, True), 0) ) + def test_array_zero_length_upper_bound_cpp(self): + prog = dwarf_program( + wrap_test_type_dies( + DwarfDie( + DW_TAG.array_type, + (DwarfAttrib(DW_AT.type, DW_FORM.ref4, "int_die"),), + ( + DwarfDie( + DW_TAG.subrange_type, + ( + DwarfAttrib( + DW_AT.upper_bound, + DW_FORM.data8, + 18446744073709551615, + ), + ), + ), + ), + ), + *labeled_int_die, + ) + ) + self.assertIdentical( + prog.type("TEST").type, prog.array_type(prog.int_type("int", 4, True), 0) + ) + def test_incomplete_array_no_subrange(self): prog = dwarf_program( wrap_test_type_dies( @@ -3563,98 +3729,521 @@ def test_function_template(self): ), ) - def test_language(self): - for name, lang in DW_LANG.__members__.items(): - if re.fullmatch("C[0-9]*", name): - prog = dwarf_program(wrap_test_type_dies(int_die), lang=lang) - self.assertIdentical( - prog.type("TEST").type, - prog.int_type("int", 4, True, language=Language.C), - ) - prog = dwarf_program(wrap_test_type_dies(int_die), lang=DW_LANG.BLISS) - self.assertIdentical( - prog.type("TEST").type, - prog.int_type("int", 4, True, language=DEFAULT_LANGUAGE), - ) + def test_function_template_parameter_pack(self): + prog = dwarf_program( + wrap_test_type_dies( + DwarfDie( + DW_TAG.subroutine_type, + (DwarfAttrib(DW_AT.type, DW_FORM.ref4, "int_die"),), + ( + DwarfDie( + DW_TAG.GNU_template_parameter_pack, + (DwarfAttrib(DW_AT.name, DW_FORM.string, "Params"),), + ( + DwarfDie( + DW_TAG.template_type_parameter, + ( + DwarfAttrib( + DW_AT.type, DW_FORM.ref4, "int_die" + ), + DwarfAttrib(DW_AT.name, DW_FORM.string, "T"), + ), + ), + char_die, # Unexpected die - should be ignored + DwarfDie( + DW_TAG.template_value_parameter, + ( + DwarfAttrib( + DW_AT.type, DW_FORM.ref4, "unsigned_int_die" + ), + DwarfAttrib(DW_AT.name, DW_FORM.string, "N"), + DwarfAttrib( + DW_AT.const_value, DW_FORM.data1, 2 + ), + ), + ), + ), + ), + ), + ), + *labeled_int_die, + *labeled_unsigned_int_die, + ) + ) + self.assertIdentical( + prog.type("TEST").type, + prog.function_type( + prog.int_type("int", 4, True), + (), + is_variadic=False, + template_parameters=( + TypeTemplateParameter(prog.int_type("int", 4, True), "T"), + TypeTemplateParameter( + Object(prog, prog.int_type("unsigned int", 4, False), 2), "N" + ), + ), + ), + ) + + def test_language(self): + for name, lang in DW_LANG.__members__.items(): + if re.fullmatch("C[0-9]*", name): + prog = dwarf_program(wrap_test_type_dies(int_die), lang=lang) + self.assertIdentical( + prog.type("TEST").type, + prog.int_type("int", 4, True, language=Language.C), + ) + prog = dwarf_program(wrap_test_type_dies(int_die), lang=DW_LANG.BLISS) + self.assertIdentical( + prog.type("TEST").type, + prog.int_type("int", 4, True, language=DEFAULT_LANGUAGE), + ) + + def test_base_type_unit(self): + for version in (4, 5): + with self.subTest(version=version): + prog = dwarf_program( + ( + DwarfUnit( + DW_UT.compile, + DwarfDie( + DW_TAG.compile_unit, + (), + ( + DwarfLabel("signature_die"), + DwarfDie( + DW_TAG.base_type, + ( + DwarfAttrib( + DW_AT.signature, + DW_FORM.ref_sig8, + 0xDEADBEEF, + ), + ), + ), + DwarfDie( + DW_TAG.typedef, + ( + DwarfAttrib( + DW_AT.name, DW_FORM.string, "TEST" + ), + DwarfAttrib( + DW_AT.type, + DW_FORM.ref4, + "signature_die", + ), + ), + ), + ), + ), + ), + DwarfUnit( + DW_UT.type, + DwarfDie( + DW_TAG.type_unit, + (), + labeled_int_die, + ), + type_signature=0xDEADBEEF, + type_offset="int_die", + ), + ), + version=version, + ) + self.assertIdentical( + prog.type("TEST").type, prog.int_type("int", 4, True) + ) + self.assertIdentical(prog.type("int"), prog.type("TEST").type) + + def test_struct_type_unit(self): + for version in (4, 5): + with self.subTest(version=version): + prog = dwarf_program( + ( + DwarfUnit( + DW_UT.compile, + DwarfDie( + DW_TAG.compile_unit, + (), + ( + DwarfLabel("signature_die"), + DwarfDie( + DW_TAG.structure_type, + ( + DwarfAttrib( + DW_AT.signature, + DW_FORM.ref_sig8, + 0xDEADBEEF, + ), + ), + ), + DwarfDie( + DW_TAG.typedef, + ( + DwarfAttrib( + DW_AT.name, DW_FORM.string, "TEST" + ), + DwarfAttrib( + DW_AT.type, + DW_FORM.ref4, + "signature_die", + ), + ), + ), + ), + ), + ), + DwarfUnit( + DW_UT.type, + DwarfDie( + DW_TAG.type_unit, + (), + ( + DwarfLabel("struct_die"), + DwarfDie( + DW_TAG.structure_type, + ( + DwarfAttrib( + DW_AT.name, DW_FORM.string, "point" + ), + DwarfAttrib( + DW_AT.byte_size, DW_FORM.data1, 8 + ), + ), + ( + DwarfDie( + DW_TAG.member, + ( + DwarfAttrib( + DW_AT.name, DW_FORM.string, "x" + ), + DwarfAttrib( + DW_AT.data_member_location, + DW_FORM.data1, + 0, + ), + DwarfAttrib( + DW_AT.type, + DW_FORM.ref4, + "int_die", + ), + ), + ), + DwarfDie( + DW_TAG.member, + ( + DwarfAttrib( + DW_AT.name, DW_FORM.string, "y" + ), + DwarfAttrib( + DW_AT.data_member_location, + DW_FORM.data1, + 4, + ), + DwarfAttrib( + DW_AT.type, + DW_FORM.ref4, + "int_die", + ), + ), + ), + ), + ), + *labeled_int_die, + ), + ), + type_signature=0xDEADBEEF, + type_offset="struct_die", + ), + ), + version=version, + ) + + self.assertIdentical( + prog.type("TEST").type, + prog.struct_type( + "point", + 8, + ( + TypeMember(prog.int_type("int", 4, True), "x", 0), + TypeMember(prog.int_type("int", 4, True), "y", 32), + ), + ), + ) + self.assertIdentical(prog.type("struct point"), prog.type("TEST").type) + + def test_namespaces(self): + def make_composite_die(tag: DW_TAG, name: str) -> DwarfDie: + return DwarfDie( + tag, + ( + DwarfAttrib(DW_AT.name, DW_FORM.string, name), + DwarfAttrib( + DW_AT.byte_size, + DW_FORM.data1, + 4 if tag == DW_TAG.union_type else 8, + ), + DwarfAttrib(DW_AT.decl_file, DW_FORM.udata, "foo.c"), + ), + ( + DwarfDie( + DW_TAG.member, + ( + DwarfAttrib(DW_AT.name, DW_FORM.string, "x"), + DwarfAttrib( + DW_AT.data_member_location, + DW_FORM.data1, + 0, + ), + DwarfAttrib(DW_AT.type, DW_FORM.ref4, "int_die"), + ), + ), + DwarfDie( + DW_TAG.member, + ( + DwarfAttrib(DW_AT.name, DW_FORM.string, "y"), + DwarfAttrib( + DW_AT.data_member_location, + DW_FORM.data1, + 0 if tag == DW_TAG.union_type else 4, + ), + DwarfAttrib(DW_AT.type, DW_FORM.ref4, "float_die"), + ), + ), + ), + ) + + dies = ( + *labeled_int_die, + *labeled_float_die, + DwarfDie( + DW_TAG.namespace, + (DwarfAttrib(DW_AT.name, DW_FORM.string, "moho"),), + ( + DwarfDie( + DW_TAG.namespace, + (DwarfAttrib(DW_AT.name, DW_FORM.string, "eve"),), + ( + DwarfDie( + DW_TAG.namespace, + (DwarfAttrib(DW_AT.name, DW_FORM.string, "kerbin"),), + ( + DwarfDie( + DW_TAG.typedef, + ( + DwarfAttrib( + DW_AT.name, + DW_FORM.string, + "TEST_TYPEDEF", + ), + DwarfAttrib( + DW_AT.type, DW_FORM.ref4, "int_die" + ), + ), + ), + make_composite_die( + DW_TAG.structure_type, "TEST_STRUCT" + ), + make_composite_die(DW_TAG.class_type, "TEST_CLASS"), + make_composite_die(DW_TAG.union_type, "TEST_UNION"), + ), + ), + ), + ), + ), + ), + ) + + prog = dwarf_program(dies, lang=DW_LANG.C_plus_plus) + # Language is not set automatically when there is no DIE for `main` + prog.language = Language.CPP + + self.assertIdentical( + prog.type("moho::eve::kerbin::TEST_TYPEDEF"), + prog.typedef_type("TEST_TYPEDEF", prog.int_type("int", 4, True)), + ) + self.assertIdentical( + prog.type("struct moho::eve::kerbin::TEST_STRUCT"), + prog.struct_type( + "TEST_STRUCT", + 8, + ( + TypeMember(prog.int_type("int", 4, True), "x", 0), + TypeMember(prog.float_type("float", 4), "y", 32), + ), + ), + ) + self.assertIdentical( + prog.type("class moho::eve::kerbin::TEST_CLASS"), + prog.class_type( + "TEST_CLASS", + 8, + ( + TypeMember(prog.int_type("int", 4, True), "x", 0), + TypeMember(prog.float_type("float", 4), "y", 32), + ), + ), + ) + self.assertIdentical( + prog.type("union moho::eve::kerbin::TEST_UNION"), + prog.union_type( + "TEST_UNION", + 4, + ( + TypeMember(prog.int_type("int", 4, True), "x", 0), + TypeMember(prog.float_type("float", 4), "y", 0), + ), + ), + ) + + def test_explicit_global_namespace(self): + prog = dwarf_program( + ( + *labeled_int_die, + DwarfDie( + DW_TAG.typedef, + ( + DwarfAttrib( + DW_AT.name, + DW_FORM.string, + "TEST_TYPEDEF_GLOBAL", + ), + DwarfAttrib(DW_AT.type, DW_FORM.ref4, "int_die"), + ), + ), + ), + lang=DW_LANG.C_plus_plus, + ) + # Language is not set automatically when there is no DIE for `main` + prog.language = Language.CPP + + self.assertIdentical( + prog.type("TEST_TYPEDEF_GLOBAL"), prog.type("::TEST_TYPEDEF_GLOBAL") + ) + + def test_template_in_namespace(self): + dies = ( + *labeled_int_die, + *labeled_unsigned_int_die, + DwarfDie( + DW_TAG.namespace, + (DwarfAttrib(DW_AT.name, DW_FORM.string, "containers"),), + ( + DwarfLabel("typedef_die"), + DwarfDie( + DW_TAG.typedef, + ( + DwarfAttrib( + DW_AT.name, + DW_FORM.string, + "MyTypedef", + ), + DwarfAttrib(DW_AT.type, DW_FORM.ref4, "unsigned_int_die"), + ), + ), + DwarfDie( + DW_TAG.class_type, + ( + DwarfAttrib( + DW_AT.name, + DW_FORM.string, + "Pair", + ), + DwarfAttrib( + DW_AT.byte_size, + DW_FORM.data1, + 8, + ), + ), + ( + DwarfDie( + DW_TAG.template_type_parameter, + ( + DwarfAttrib(DW_AT.type, DW_FORM.ref4, "int_die"), + DwarfAttrib(DW_AT.name, DW_FORM.string, "T"), + ), + ), + DwarfDie( + DW_TAG.template_type_parameter, + ( + DwarfAttrib( + DW_AT.type, DW_FORM.ref4, "typedef_die" + ), + DwarfAttrib(DW_AT.name, DW_FORM.string, "V"), + ), + ), + DwarfDie( + DW_TAG.member, + ( + DwarfAttrib(DW_AT.name, DW_FORM.string, "first"), + DwarfAttrib( + DW_AT.data_member_location, + DW_FORM.data1, + 0, + ), + DwarfAttrib(DW_AT.type, DW_FORM.ref4, "int_die"), + ), + ), + DwarfDie( + DW_TAG.member, + ( + DwarfAttrib(DW_AT.name, DW_FORM.string, "second"), + DwarfAttrib( + DW_AT.data_member_location, + DW_FORM.data1, + 4, + ), + DwarfAttrib( + DW_AT.type, DW_FORM.ref4, "typedef_die" + ), + ), + ), + ), + ), + ), + ), + ) + + prog = dwarf_program(dies, lang=DW_LANG.C_plus_plus) + # Language is not set automatically when there is no DIE for `main` + prog.language = Language.CPP - def test_base_type_unit(self): - prog = dwarf_program( - ( - DwarfDie( - DW_TAG.compile_unit, - (), - ( - DwarfLabel("signature_die"), - DwarfDie( - DW_TAG.base_type, - ( - DwarfAttrib( - DW_AT.signature, DW_FORM.ref_sig8, 0xDEADBEEF - ), - ), - ), - DwarfDie( - DW_TAG.typedef, - ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "TEST"), - DwarfAttrib(DW_AT.type, DW_FORM.ref4, "signature_die"), - ), - ), - ), + self.assertIdentical( + prog.type("class containers::Pair"), + prog.class_type( + "Pair", + 8, + members=( + TypeMember(prog.int_type("int", 4, True), "first", 0), + TypeMember(prog.type("containers::MyTypedef"), "second", 32), ), - DwarfDie( - DW_TAG.type_unit, - (), - labeled_int_die, - type_signature=0xDEADBEEF, - type_offset="int_die", + template_parameters=( + TypeTemplateParameter(prog.int_type("int", 4, True), "T"), + TypeTemplateParameter(prog.type("containers::MyTypedef"), "V"), ), - ) + ), ) - self.assertIdentical(prog.type("TEST").type, prog.int_type("int", 4, True)) - self.assertIdentical(prog.type("int"), prog.type("TEST").type) - def test_struct_type_unit(self): - prog = dwarf_program( - ( - DwarfDie( - DW_TAG.compile_unit, - (), - ( - DwarfLabel("signature_die"), - DwarfDie( - DW_TAG.structure_type, - ( - DwarfAttrib( - DW_AT.signature, DW_FORM.ref_sig8, 0xDEADBEEF - ), - ), - ), - DwarfDie( - DW_TAG.typedef, - ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "TEST"), - DwarfAttrib(DW_AT.type, DW_FORM.ref4, "signature_die"), - ), - ), - ), - ), - DwarfDie( - DW_TAG.type_unit, - (), + def test_cpp_compound_type_specifiers(self): + for keyword, tag in ( + ("struct", DW_TAG.structure_type), + ("union", DW_TAG.union_type), + ("class", DW_TAG.class_type), + ): + with self.subTest(keyword=keyword): + prog = dwarf_program( ( - DwarfLabel("struct_die"), DwarfDie( - DW_TAG.structure_type, + tag, ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "point"), - DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8), + DwarfAttrib(DW_AT.name, DW_FORM.string, "Foo"), + DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 4), ), ( DwarfDie( DW_TAG.member, ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "x"), + DwarfAttrib(DW_AT.name, DW_FORM.string, "bar"), DwarfAttrib( DW_AT.data_member_location, DW_FORM.data1, 0 ), @@ -3663,40 +4252,64 @@ def test_struct_type_unit(self): ), ), ), - DwarfDie( - DW_TAG.member, - ( - DwarfAttrib(DW_AT.name, DW_FORM.string, "y"), - DwarfAttrib( - DW_AT.data_member_location, DW_FORM.data1, 4 - ), - DwarfAttrib( - DW_AT.type, DW_FORM.ref4, "int_die" - ), - ), - ), ), ), *labeled_int_die, ), - type_signature=0xDEADBEEF, - type_offset="struct_die", + ) + self.assertRaises(LookupError, prog.type, "Foo") + prog.language = Language.CPP + self.assertIdentical(prog.type("Foo"), prog.type(keyword + " Foo")) + + def test_cpp_enum_specifier(self): + prog = dwarf_program( + ( + DwarfDie( + DW_TAG.enumeration_type, + ( + DwarfAttrib(DW_AT.name, DW_FORM.string, "Foo"), + DwarfAttrib(DW_AT.type, DW_FORM.ref4, "int_die"), + DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 4), + ), + ( + DwarfDie( + DW_TAG.enumerator, + ( + DwarfAttrib(DW_AT.name, DW_FORM.string, "BAR"), + DwarfAttrib(DW_AT.const_value, DW_FORM.data2, 1337), + ), + ), + ), ), - ) + *labeled_int_die, + ), ) + self.assertRaises(LookupError, prog.type, "Foo") + prog.language = Language.CPP + self.assertIdentical(prog.type("Foo"), prog.type("enum Foo")) - self.assertIdentical( - prog.type("TEST").type, - prog.struct_type( - "point", - 8, - ( - TypeMember(prog.int_type("int", 4, True), "x", 0), - TypeMember(prog.int_type("int", 4, True), "y", 32), + def test_cpp_typedef(self): + prog = dwarf_program( + ( + DwarfDie( + DW_TAG.typedef, + ( + DwarfAttrib(DW_AT.name, DW_FORM.string, "INT"), + DwarfAttrib(DW_AT.type, DW_FORM.ref4, "int_die"), + ), + ), + *labeled_int_die, + DwarfDie( + DW_TAG.subprogram, + (DwarfAttrib(DW_AT.name, DW_FORM.string, "main"),), ), ), + lang=DW_LANG.C_plus_plus, + ) + self.assertIdentical( + prog.type("INT"), + prog.typedef_type("INT", prog.int_type("int", 4, True)), ) - self.assertIdentical(prog.type("struct point"), prog.type("TEST").type) class TestObjects(TestCase): @@ -6006,6 +6619,43 @@ def test_namespaces_nested(self): Object(prog, prog.int_type("int", 4, True), 47), ) + def test_nested_classes(self): + for kind, tag in ( + ("class", DW_TAG.class_type), + ("struct", DW_TAG.structure_type), + ("union", DW_TAG.union_type), + ): + with self.subTest(kind=kind): + prog = dwarf_program( + ( + DwarfDie( + tag, + ( + DwarfAttrib(DW_AT.name, DW_FORM.string, "Foo"), + DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 0), + ), + ( + DwarfDie( + tag, + ( + DwarfAttrib(DW_AT.name, DW_FORM.string, "Bar"), + DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 0), + ), + ), + ), + ), + DwarfDie( + DW_TAG.subprogram, + (DwarfAttrib(DW_AT.name, DW_FORM.string, "main"),), + ), + ), + lang=DW_LANG.C_plus_plus, + ) + self.assertIdentical( + prog.type(kind + " Foo::Bar"), + getattr(prog, kind + "_type")("Bar", 0, ()), + ) + class TestProgram(TestCase): def test_language(self): @@ -6098,3 +6748,263 @@ def test_reference_counting_type_parameter(self): *labeled_int_die, ) self.assertIsNotNone(repr(dwarf_program(dies).type("TEST").type.parameters[0])) + + +class TestCompressedDebugSections(TestCase): + def test_zlib_gnu(self): + prog = dwarf_program(wrap_test_type_dies(int_die), compress="zlib-gnu") + self.assertIdentical(prog.type("TEST").type, prog.int_type("int", 4, True)) + + def test_zlib_gabi(self): + prog = dwarf_program(wrap_test_type_dies(int_die), compress="zlib-gabi") + self.assertIdentical(prog.type("TEST").type, prog.int_type("int", 4, True)) + + +class TestSplitDwarf(TestCase): + def test_dwo4(self): + prog = Program() + with tempfile.TemporaryDirectory() as temp_dir: + with open(os.path.join(temp_dir, "split.dwo"), "wb") as f: + f.write( + compile_dwarf( + DwarfUnit( + DW_UT.split_compile, + DwarfDie( + DW_TAG.compile_unit, + ( + DwarfAttrib( + DW_AT.GNU_dwo_id, + DW_FORM.data8, + 0xDDEEAADDBBEEFFFF, + ), + ), + wrap_test_type_dies(int_die), + ), + ), + split="dwo", + ) + ) + with open(os.path.join(temp_dir, "skeleton"), "wb") as f: + f.write( + compile_dwarf( + DwarfUnit( + DW_UT.skeleton, + DwarfDie( + DW_TAG.compile_unit, + ( + DwarfAttrib( + DW_AT.GNU_dwo_name, DW_FORM.string, "split.dwo" + ), + DwarfAttrib( + DW_AT.GNU_dwo_id, + DW_FORM.data8, + 0xDDEEAADDBBEEFFFF, + ), + ), + ), + ) + ) + ) + prog.load_debug_info([f.name]) + self.assertIdentical(prog.type("TEST").type, prog.int_type("int", 4, True)) + + def test_dwo4_not_found(self): + prog = Program() + with tempfile.TemporaryDirectory() as temp_dir: + with open(os.path.join(temp_dir, "skeleton"), "wb") as f: + f.write( + compile_dwarf( + DwarfUnit( + DW_UT.skeleton, + DwarfDie( + DW_TAG.compile_unit, + ( + DwarfAttrib( + DW_AT.GNU_dwo_name, DW_FORM.string, "split.dwo" + ), + DwarfAttrib( + DW_AT.GNU_dwo_id, + DW_FORM.data8, + 0xDDEEAADDBBEEFFFF, + ), + ), + ), + ) + ) + ) + with self.assertLogs(logging.getLogger("drgn"), "WARNING") as log: + prog.load_debug_info([f.name]) + self.assertTrue( + any( + "split DWARF file split.dwo not found" in output + for output in log.output + ) + ) + + def test_dwo4_id_mismatch(self): + prog = Program() + with tempfile.TemporaryDirectory() as temp_dir: + with open(os.path.join(temp_dir, "split.dwo"), "wb") as f: + f.write( + compile_dwarf( + DwarfUnit( + DW_UT.split_compile, + DwarfDie( + DW_TAG.compile_unit, + ( + DwarfAttrib( + DW_AT.GNU_dwo_id, + DW_FORM.data8, + 0xBBBBBBBB00000000, + ), + ), + ), + ), + split="dwo", + ) + ) + with open(os.path.join(temp_dir, "skeleton"), "wb") as f: + f.write( + compile_dwarf( + DwarfUnit( + DW_UT.skeleton, + DwarfDie( + DW_TAG.compile_unit, + ( + DwarfAttrib( + DW_AT.GNU_dwo_name, DW_FORM.string, "split.dwo" + ), + DwarfAttrib( + DW_AT.GNU_dwo_id, + DW_FORM.data8, + 0xDDEEAADDBBEEFFFF, + ), + ), + ), + ) + ) + ) + with self.assertLogs(logging.getLogger("drgn"), "WARNING") as log: + prog.load_debug_info([f.name]) + self.assertTrue( + any( + "split DWARF file split.dwo not found" in output + for output in log.output + ) + ) + + def test_dwo5(self): + prog = Program() + with tempfile.TemporaryDirectory() as temp_dir: + with open(os.path.join(temp_dir, "split.dwo"), "wb") as f: + f.write( + compile_dwarf( + DwarfUnit( + DW_UT.split_compile, + DwarfDie( + DW_TAG.compile_unit, + (), + wrap_test_type_dies(int_die), + ), + dwo_id=0xDDEEAADDBBEEFFFF, + ), + version=5, + split="dwo", + ) + ) + with open(os.path.join(temp_dir, "skeleton"), "wb") as f: + f.write( + compile_dwarf( + DwarfUnit( + DW_UT.skeleton, + DwarfDie( + DW_TAG.compile_unit, + ( + DwarfAttrib( + DW_AT.dwo_name, DW_FORM.string, "split.dwo" + ), + ), + ), + dwo_id=0xDDEEAADDBBEEFFFF, + ), + version=5, + ) + ) + prog.load_debug_info([f.name]) + self.assertIdentical(prog.type("TEST").type, prog.int_type("int", 4, True)) + + def test_dwo5_not_found(self): + prog = Program() + with tempfile.TemporaryDirectory() as temp_dir: + with open(os.path.join(temp_dir, "skeleton"), "wb") as f: + f.write( + compile_dwarf( + DwarfUnit( + DW_UT.skeleton, + DwarfDie( + DW_TAG.compile_unit, + ( + DwarfAttrib( + DW_AT.dwo_name, DW_FORM.string, "split.dwo" + ), + ), + ), + dwo_id=0xDDEEAADDBBEEFFFF, + ), + version=5, + ) + ) + with self.assertLogs(logging.getLogger("drgn"), "WARNING") as log: + prog.load_debug_info([f.name]) + self.assertTrue( + any( + "split DWARF file split.dwo not found" in output + for output in log.output + ) + ) + + def test_dwo5_id_mismatch(self): + prog = Program() + with tempfile.TemporaryDirectory() as temp_dir: + with open(os.path.join(temp_dir, "split.dwo"), "wb") as f: + f.write( + compile_dwarf( + DwarfUnit( + DW_UT.split_compile, + DwarfDie( + DW_TAG.compile_unit, + (), + wrap_test_type_dies(int_die), + ), + dwo_id=0xBBBBBBBB00000000, + ), + version=5, + split="dwo", + ) + ) + with open(os.path.join(temp_dir, "skeleton"), "wb") as f: + f.write( + compile_dwarf( + DwarfUnit( + DW_UT.skeleton, + DwarfDie( + DW_TAG.compile_unit, + ( + DwarfAttrib( + DW_AT.dwo_name, DW_FORM.string, "split.dwo" + ), + ), + ), + dwo_id=0xDDEEAADDBBEEFFFF, + ), + version=5, + ) + ) + with self.assertLogs(logging.getLogger("drgn"), "WARNING") as log: + prog.load_debug_info([f.name]) + self.assertTrue( + any( + "split DWARF file split.dwo not found" in output + for output in log.output + ) + ) diff --git a/tests/test_language_c.py b/tests/test_language_c.py index ba2c30239..2f851998a 100644 --- a/tests/test_language_c.py +++ b/tests/test_language_c.py @@ -1,6 +1,7 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from functools import reduce +from itertools import chain import operator from drgn import ( @@ -788,6 +789,58 @@ def test_cpp_keywords(self): ] self.assertEqual([token.kind for token in self.lex(s, cpp=True)], tokens) + def test_cpp_identifiers_with_template_parameters(self): + token_pairs = ( + ("vector", ""), + ("pair", ""), + ("unordered_map", ">>"), + ("IntLiteral", "<123>"), + ("IntLiteralWithSuffix", "<123UL>"), + ("FloatLiteral", "<1.987>"), + ("FloatLiteralWithExponent", "<1.23423e+1f>"), + ("PointerLiteral", "<&asdf>"), + ("ParenthesizedPointerLiteral", "<(&asdf)>"), + ("CharLiteral", "<'a'>"), + ("CharLiteralEdgeCase1", "<'<'>"), + ("CharLiteralEdgeCase2", "<'>'>"), + ("CharLiteralEdgeCase3", r"<'\''>"), + ) + self.assertEqual( + [ + (token.kind, token.value) + for token in self.lex( + " ".join("".join(pair) for pair in token_pairs), cpp=True + ) + ], + [ + (C_TOKEN.TEMPLATE_ARGUMENTS if i % 2 else C_TOKEN.IDENTIFIER, value) + for i, value in enumerate(chain.from_iterable(token_pairs)) + ], + ) + + def test_cpp_identifiers_with_invalid_template_parameters(self): + for s in [ + "vector", + "unordered_map>", + ]: + self.assertRaisesRegex( + SyntaxError, + "invalid template arguments", + list, + self.lex(s, cpp=True), + ) + for s in [ + "vectorint>", + "pair>", + ]: + self.assertRaisesRegex( + SyntaxError, + "invalid character", + list, + self.lex(s, cpp=True), + ) + def test_identifiers(self): s = "_ x foo _bar baz1" tokens = s.split() @@ -2356,3 +2409,123 @@ def test_absent(self): else: type_name = type_ self.assertEqual(str(Object(self.prog, type_)), f"({type_name})") + + def test_bigint(self): + segment = bytearray(16) + self.add_memory_segment(segment, virt_addr=0xFFFF0000) + + for byteorder in ("little", "big"): + with self.subTest(byteorder=byteorder): + obj = Object( + self.prog, + self.prog.int_type("unsigned __int128", 16, False, byteorder), + address=0xFFFF0000, + ) + for value in ( + 0, + 0x100, + 0x112233445566778899AABBCCDDEEFF, + 0xFEDCBA9876543210, + ): + segment[:] = value.to_bytes(16, byteorder) + self.assertEqual(str(obj), "(unsigned __int128)" + hex(value)) + + def test_bigint_in_array(self): + segment = bytearray(16 * 3) + self.add_memory_segment(segment, virt_addr=0xFFFF0000) + + for byteorder in ("little", "big"): + with self.subTest(byteorder=byteorder): + segment[:16] = (0x112233445566778899AABBCCDDEEFF).to_bytes( + 16, byteorder + ) + segment[16:32] = (0xDEADBEEF).to_bytes(16, byteorder) + obj = Object( + self.prog, + self.prog.array_type( + self.prog.int_type("unsigned __int128", 16, False, byteorder), 3 + ), + address=0xFFFF0000, + ) + self.assertEqual( + str(obj), + "(unsigned __int128 [3]){ 0x112233445566778899aabbccddeeff, 0xdeadbeef }", + ) + self.assertEqual( + obj.format_(implicit_elements=True), + "(unsigned __int128 [3]){ 0x112233445566778899aabbccddeeff, 0xdeadbeef, 0x0 }", + ) + + def test_bigint_in_struct(self): + segment = bytearray(16 * 3) + self.add_memory_segment(segment, virt_addr=0xFFFF0000) + + for byteorder in ("little", "big"): + with self.subTest(byteorder=byteorder): + segment[:16] = (0x112233445566778899AABBCCDDEEFF).to_bytes( + 16, byteorder + ) + segment[16:32] = (0xDEADBEEF).to_bytes(16, byteorder) + type = self.prog.int_type("unsigned __int128", 16, False, byteorder) + obj = Object( + self.prog, + self.prog.struct_type( + "foo", + 16 * 3, + ( + TypeMember(type, "a"), + TypeMember(type, "b", 16 * 8), + TypeMember(type, "c", 16 * 8 * 2), + ), + ), + address=0xFFFF0000, + ) + self.assertEqual( + str(obj), + """\ +(struct foo){ + .a = (unsigned __int128)0x112233445566778899aabbccddeeff, + .b = (unsigned __int128)0xdeadbeef, + .c = (unsigned __int128)0x0, +}""", + ) + self.assertEqual( + obj.format_(implicit_members=False), + """\ +(struct foo){ + .a = (unsigned __int128)0x112233445566778899aabbccddeeff, + .b = (unsigned __int128)0xdeadbeef, +}""", + ) + + def test_bigint_typedef(self): + self.add_memory_segment( + (0xDEADBEEF).to_bytes(16, "little"), virt_addr=0xFFFF0000 + ) + type = self.prog.typedef_type( + "__uint128_t", self.prog.int_type("unsigned __int128", 16, False) + ) + + self.assertEqual( + str(Object(self.prog, type, address=0xFFFF0000)), + "(__uint128_t)0xdeadbeef", + ) + + self.assertEqual( + str(Object(self.prog, self.prog.array_type(type, 1), address=0xFFFF0000)), + "(__uint128_t [1]){ 0xdeadbeef }", + ) + + self.assertEqual( + str( + Object( + self.prog, + self.prog.struct_type("foo", 16, (TypeMember(type, "a"),)), + address=0xFFFF0000, + ) + ), + """\ +(struct foo){ + .a = (__uint128_t)0xdeadbeef, +}""", + ) diff --git a/tests/test_lexer.py b/tests/test_lexer.py index 6f6ae90a6..11cc0136b 100644 --- a/tests/test_lexer.py +++ b/tests/test_lexer.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from tests import TestCase from tests.libdrgn import Lexer, drgn_test_lexer_func diff --git a/tests/test_logging.py b/tests/test_logging.py new file mode 100644 index 000000000..c5b9aa516 --- /dev/null +++ b/tests/test_logging.py @@ -0,0 +1,30 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +import logging +import sys +import unittest + +from drgn import Program +from tests import TestCase + + +# Test that our monkey patch to sync the log level between the logging module +# and libdrgn works. +class TestLogging(TestCase): + def test_set_level_before(self): + logger = logging.getLogger("drgn") + with self.assertLogs(logger, "DEBUG") as cm: + prog = Program() + prog._log(0, "foo") + self.assertIn("DEBUG:drgn:foo", cm.output) + + @unittest.skipIf( + sys.version_info < (3, 7), "syncing log level only works since Python 3.7" + ) + def test_set_level_after(self): + prog = Program() + logger = logging.getLogger("drgn") + with self.assertLogs(logger, "DEBUG") as cm: + prog._log(0, "bar") + self.assertIn("DEBUG:drgn:bar", cm.output) diff --git a/tests/test_object.py b/tests/test_object.py index 757fbeb60..371db3d12 100644 --- a/tests/test_object.py +++ b/tests/test_object.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import math import operator @@ -141,6 +141,72 @@ def test_float_size(self): ) +def _int_bits_cases(prog): + for signed in (True, False): + for byteorder in ("little", "big"): + for bit_size in range(1, 129): + if bit_size <= 8: + size = 1 + else: + size = 1 << ((bit_size - 1).bit_length() - 3) + type = prog.int_type( + "" if signed else "u" + f"int{size}", size, signed, byteorder + ) + if signed: + values = ( + 0xF8935CF44C45202748DE66B49BA0CBAC % (1 << (bit_size - 1)), + ~0xF8935CF44C45202748DE66B49BA0CBAC % (1 << (bit_size - 1)), + -0xC256D5AAFFDC3179A6AC84E7154A215D % -(1 << (bit_size - 1)), + ~-0xC256D5AAFFDC3179A6AC84E7154A215D % -(1 << (bit_size - 1)), + ) + else: + values = ( + 0xF8935CF44C45202748DE66B49BA0CBAC % (1 << bit_size), + ~0xF8935CF44C45202748DE66B49BA0CBAC % (1 << bit_size), + ) + for value in values: + # value_bytes is the value converted to bytes. + if byteorder == "little": + value_bytes = (value & ((1 << bit_size) - 1)).to_bytes( + (bit_size + 7) // 8, byteorder + ) + else: + value_bytes = (value << (-bit_size % 8)).to_bytes( + (bit_size + 7) // 8, byteorder, signed=signed + ) + for bit_offset in range(8): + # source_bytes is a buffer containing the value at the + # given bit offset, with extra bits that should be + # ignored. + if byteorder == "little": + source_bytes = bytearray( + (value << bit_offset).to_bytes( + (bit_offset + bit_size + 7) // 8, + byteorder, + signed=signed, + ) + ) + source_bytes[0] |= (1 << bit_offset) - 1 + if (bit_offset + bit_size) % 8 != 0: + source_bytes[-1] ^= ( + 0xFF << ((bit_offset + bit_size) % 8) + ) & 0xFF + else: + source_bytes = bytearray( + (value << (-(bit_offset + bit_size) % 8)).to_bytes( + (bit_offset + bit_size + 7) // 8, + byteorder, + signed=signed, + ) + ) + source_bytes[0] ^= (0xFF00 >> bit_offset) & 0xFF + if (bit_offset + bit_size) % 8 != 0: + source_bytes[-1] |= ( + 1 << (-(bit_offset + bit_size) % 8) + ) - 1 + yield signed, byteorder, bit_size, type, bit_offset, value, value_bytes, source_bytes + + class TestReference(MockProgramTestCase): def test_basic(self): self.add_memory_segment((1000).to_bytes(4, "little"), virt_addr=0xFFFF0000) @@ -197,28 +263,88 @@ def test_overflow(self): bit_offset=7, ) - def test_read_unsigned(self): - value = 12345678912345678989 - for bit_size in range(1, 65): - for bit_offset in range(8): - size = (bit_size + bit_offset + 7) // 8 - size_mask = (1 << (8 * size)) - 1 - for byteorder in ["little", "big"]: - if byteorder == "little": - tmp = value << bit_offset - else: - tmp = value << (8 - bit_size - bit_offset) % 8 - tmp &= size_mask - buf = tmp.to_bytes(size, byteorder) - prog = mock_program(segments=[MockMemorySegment(buf, 0)]) - obj = Object( - prog, - prog.int_type("unsigned long long", 8, False, byteorder), - address=0, - bit_field_size=bit_size, - bit_offset=bit_offset, - ) - self.assertEqual(obj.value_(), value & ((1 << bit_size) - 1)) + def test_signed_big(self): + buffer = (-4).to_bytes(16, "little", signed=True) + self.add_memory_segment(buffer, virt_addr=0xFFFF0000) + obj = Object( + self.prog, + self.prog.int_type("__int128", 16, True), + address=0xFFFF0000, + ) + self.assertIs(obj.prog_, self.prog) + self.assertFalse(obj.absent_) + self.assertEqual(obj.address_, 0xFFFF0000) + self.assertEqual(obj.bit_offset_, 0) + self.assertIsNone(obj.bit_field_size_) + self.assertEqual(obj.type_.size, 16) + self.assertEqual(obj.value_(), -4) + self.assertEqual(obj.to_bytes_(), buffer) + self.assertEqual(repr(obj), "Object(prog, '__int128', address=0xffff0000)") + + self.assertIdentical( + obj.read_(), + Object(self.prog, self.prog.int_type("__int128", 16, True), value=-4), + ) + + def test_unsigned_big(self): + buffer = (1000).to_bytes(16, "little") + self.add_memory_segment(buffer, virt_addr=0xFFFF0000) + obj = Object( + self.prog, + self.prog.int_type("unsigned __int128", 16, False), + address=0xFFFF0000, + ) + self.assertIs(obj.prog_, self.prog) + self.assertFalse(obj.absent_) + self.assertEqual(obj.address_, 0xFFFF0000) + self.assertEqual(obj.bit_offset_, 0) + self.assertIsNone(obj.bit_field_size_) + self.assertEqual(obj.type_.size, 16) + self.assertEqual(obj.value_(), 1000) + self.assertEqual(obj.to_bytes_(), buffer) + self.assertEqual( + repr(obj), "Object(prog, 'unsigned __int128', address=0xffff0000)" + ) + + self.assertIdentical( + obj.read_(), + Object( + self.prog, + self.prog.int_type("unsigned __int128", 16, False), + value=1000, + ), + ) + + def test_int_bits(self): + buffer = bytearray(17) + self.add_memory_segment(buffer, virt_addr=0xFFFF0000) + for ( + signed, + byteorder, + bit_size, + type, + bit_offset, + value, + value_bytes, + source_bytes, + ) in _int_bits_cases(self.prog): + with self.subTest( + signed=signed, + byteorder=byteorder, + bit_size=bit_size, + bit_offset=bit_offset, + value=value, + ): + buffer[: len(source_bytes)] = source_bytes + obj = Object( + self.prog, + type, + address=0xFFFF0000, + bit_offset=bit_offset, + bit_field_size=bit_size, + ) + self.assertEqual(obj.value_(), value) + self.assertEqual(obj.to_bytes_(), value_bytes) def test_read_float(self): pi32 = struct.unpack("f", struct.pack("f", math.pi))[0] @@ -451,30 +577,6 @@ def test_non_scalar_bit_offset(self): Object(self.prog, self.point_type, address=0xFFFF0004), ) - def test_big_int(self): - buffer = (1000).to_bytes(16, "little") - self.add_memory_segment(buffer, virt_addr=0xFFFF0000) - obj = Object( - self.prog, - self.prog.int_type("unsigned __int128", 16, False), - address=0xFFFF0000, - ) - self.assertIs(obj.prog_, self.prog) - self.assertFalse(obj.absent_) - self.assertEqual(obj.address_, 0xFFFF0000) - self.assertEqual(obj.bit_offset_, 0) - self.assertIsNone(obj.bit_field_size_) - self.assertEqual(obj.type_.size, 16) - self.assertRaisesRegex( - NotImplementedError, - "integer values larger than 64 bits are not yet supported", - obj.value_, - ) - self.assertEqual(obj.to_bytes_(), buffer) - self.assertEqual( - repr(obj), "Object(prog, 'unsigned __int128', address=0xffff0000)" - ) - def test_bit_field_of_big_int(self): buffer = (1000).to_bytes(4, "little") self.add_memory_segment(buffer, virt_addr=0xFFFF0000) @@ -569,18 +671,6 @@ def test_signed(self): self.assertEqual(obj.value_(), -8) self.assertEqual(repr(obj), "Object(prog, 'int', value=-8, bit_field_size=4)") - value = 12345678912345678989 - for bit_size in range(1, 65): - tmp = value & ((1 << bit_size) - 1) - mask = 1 << (bit_size - 1) - tmp = (tmp ^ mask) - mask - self.assertEqual( - Object( - self.prog, "long", value=value, bit_field_size=bit_size - ).value_(), - tmp, - ) - def test_unsigned(self): obj = Object(self.prog, "unsigned int", value=2**32 - 1) self.assertIs(obj.prog_, self.prog) @@ -628,6 +718,145 @@ def test_unsigned(self): value & ((1 << bit_size) - 1), ) + def _test_big_int_operators(self, type): + big_obj = Object(self.prog, type, 1000) + obj = Object(self.prog, "int", 0) + for op in ( + operator.lt, + operator.le, + operator.eq, + operator.ge, + operator.gt, + operator.add, + operator.and_, + operator.lshift, + operator.mod, + operator.mul, + operator.or_, + operator.rshift, + operator.sub, + operator.truediv, + operator.xor, + ): + self.assertRaises(NotImplementedError, op, big_obj, obj) + self.assertRaises(NotImplementedError, op, obj, big_obj) + + for op in ( + operator.inv, + operator.neg, + operator.pos, + ): + self.assertRaises(NotImplementedError, op, big_obj) + + self.assertFalse(not big_obj) + self.assertTrue(bool(big_obj)) + for op in ( + operator.index, + round, + math.trunc, + math.floor, + math.ceil, + ): + self.assertEqual(op(big_obj), 1000) + + def test_signed_big(self): + type = self.prog.int_type("__int128", 16, True) + obj = Object(self.prog, type, -4) + self.assertIs(obj.prog_, self.prog) + self.assertIdentical(obj.type_, self.prog.int_type("__int128", 16, True)) + self.assertFalse(obj.absent_) + self.assertIsNone(obj.address_) + self.assertIsNone(obj.bit_offset_) + self.assertIsNone(obj.bit_field_size_) + self.assertEqual(obj.value_(), -4) + self.assertEqual(repr(obj), "Object(prog, '__int128', value=-4)") + + self.assertIdentical(Object(self.prog, type, value=2**128 - 4), obj) + self.assertIdentical(Object(self.prog, type, value=-4.6), obj) + + self.assertIdentical( + Object(self.prog, type, value=2**128 + 4), + Object(self.prog, type, value=4), + ) + + self.assertRaisesRegex( + TypeError, + "'__int128' value must be number", + Object, + self.prog, + type, + value=b"asdf", + ) + + self._test_big_int_operators(type) + + def test_unsigned_big(self): + type = self.prog.int_type("unsigned __int128", 16, False) + obj = Object(self.prog, type, 2**128 - 1) + self.assertIs(obj.prog_, self.prog) + self.assertIdentical( + obj.type_, self.prog.int_type("unsigned __int128", 16, False) + ) + self.assertFalse(obj.absent_) + self.assertIsNone(obj.address_) + self.assertIsNone(obj.bit_offset_) + self.assertIsNone(obj.bit_field_size_) + self.assertEqual(obj.value_(), 2**128 - 1) + self.assertEqual( + repr(obj), + "Object(prog, 'unsigned __int128', value=340282366920938463463374607431768211455)", + ) + + self.assertIdentical(Object(self.prog, type, value=-1), obj) + self.assertIdentical(Object(self.prog, type, value=2**128 - 1), obj) + self.assertIdentical(Object(self.prog, type, value=2**129 - 1), obj) + self.assertIdentical( + Object(self.prog, type, value=0.1), Object(self.prog, type, value=0) + ) + + self.assertRaisesRegex( + TypeError, + "'unsigned __int128' value must be number", + Object, + self.prog, + type, + value="foo", + ) + + self._test_big_int_operators(type) + + def test_int_bits(self): + for ( + signed, + byteorder, + bit_size, + type, + bit_offset, + value, + value_bytes, + source_bytes, + ) in _int_bits_cases(self.prog): + with self.subTest( + signed=signed, + byteorder=byteorder, + bit_size=bit_size, + bit_offset=bit_offset, + value=value, + ): + obj = Object(self.prog, type, value, bit_field_size=bit_size) + self.assertEqual(obj.value_(), value) + self.assertEqual(obj.to_bytes_(), value_bytes) + self.assertIdentical( + Object.from_bytes_( + self.prog, + obj.type_, + source_bytes, + bit_offset=bit_offset, + bit_field_size=bit_size, + ), + obj, + ) + def test_float(self): obj = Object(self.prog, "double", value=3.14) self.assertIs(obj.prog_, self.prog) @@ -812,6 +1041,79 @@ def test_compound_offset(self): self.assertIdentical(obj.x, Object(self.prog, "int", value=100)) self.assertIdentical(obj.y, Object(self.prog, "int", value=-5)) + def test_compound_float(self): + for byteorder in ("little", "big"): + for type in ( + self.prog.float_type("double", 8, byteorder), + self.prog.float_type("float", 4, byteorder), + ): + with self.subTest(byteorder=byteorder, type=type.name): + obj = Object( + self.prog, + self.prog.struct_type( + None, + type.size * 2, + ( + TypeMember(type, "a"), + TypeMember(type, "b", type.size * 8), + ), + ), + value={"a": 1234, "b": -3.125}, + ) + self.assertEqual(obj.a.value_(), 1234.0) + self.assertEqual(obj.b.value_(), -3.125) + + def test_compound_bit_fields(self): + a = 0xF8935CF44C45202748DE66B49BA0CBAC + b = -0xC256D5AAFFDC3179A6AC84E7154A215D + for signed in (True, False): + if signed: + + def truncate(x, bit_size): + sign = 1 << (bit_size - 1) + return (x & (sign - 1)) - (x & sign) + + else: + + def truncate(x, bit_size): + return x & ((1 << bit_size) - 1) + + for byteorder in ("little", "big"): + for bit_size in range(1, 128): + with self.subTest( + signed=signed, byteorder=byteorder, bit_size=bit_size + ): + type = self.prog.int_type( + ("" if signed else "unsigned ") + "__int128", 16, signed + ) + obj = Object( + self.prog, + self.prog.struct_type( + None, + type.size * 2, + ( + TypeMember( + Object( + self.prog, type, bit_field_size=bit_size + ), + "a", + ), + TypeMember( + Object( + self.prog, + type, + bit_field_size=128 - bit_size, + ), + "b", + bit_size, + ), + ), + ), + value={"a": a, "b": b}, + ) + self.assertEqual(obj.a.value_(), truncate(a, bit_size)) + self.assertEqual(obj.b.value_(), truncate(b, 128 - bit_size)) + def test_pointer(self): obj = Object(self.prog, "int *", value=0xFFFF0000) self.assertFalse(obj.absent_) @@ -867,29 +1169,7 @@ def test_non_scalar_bit_offset(self): ValueError, "non-scalar must be byte-aligned", obj.member_, "point" ) - def test_big_int(self): - for type in [ - self.prog.int_type("unsigned __int128", 16, False), - self.prog.int_type("__int128", 16, True), - ]: - self.assertRaisesRegex( - NotImplementedError, - "integer values larger than 64 bits are not yet supported", - Object, - self.prog, - type, - 0, - ) - self.assertRaisesRegex( - NotImplementedError, - "integer values larger than 64 bits are not yet supported", - Object.from_bytes_, - self.prog, - type, - (0).to_bytes(16, "little"), - ) - - def test_bit_field_of_big_int(self): + def test_small_bit_field_of_big_int(self): obj = Object( self.prog, self.prog.int_type("unsigned __int128", 16, False), diff --git a/tests/test_path.py b/tests/test_path.py index 4b5548633..a80158591 100644 --- a/tests/test_path.py +++ b/tests/test_path.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import itertools import os.path diff --git a/tests/test_platform.py b/tests/test_platform.py index f766b88f8..737637139 100644 --- a/tests/test_platform.py +++ b/tests/test_platform.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import itertools from drgn import Architecture, Platform, PlatformFlags diff --git a/tests/test_program.py b/tests/test_program.py index 0634096a7..9936837d6 100644 --- a/tests/test_program.py +++ b/tests/test_program.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import ctypes import itertools import os @@ -541,6 +541,26 @@ def test_size_t_and_ptrdiff_t(self): ValueError, "no suitable integer type for ptrdiff_t", prog.type, "ptrdiff_t" ) + def test_not_size_t_or_ptrdiff_t(self): + self.types.append( + self.prog.typedef_type( + "size_tea", self.prog.int_type("unsigned char", 1, False) + ) + ) + self.types.append( + self.prog.typedef_type("ptrdiff_tee", self.prog.int_type("char", 1, True)) + ) + self.assertIdentical( + self.prog.type("size_tea"), + self.prog.typedef_type( + "size_tea", self.prog.int_type("unsigned char", 1, False) + ), + ) + self.assertIdentical( + self.prog.type("ptrdiff_tee"), + self.prog.typedef_type("ptrdiff_tee", self.prog.int_type("char", 1, True)), + ) + def test_tagged_type(self): self.types.append(self.point_type) self.types.append(self.option_type) diff --git a/tests/test_python.py b/tests/test_python.py index ac8426abb..c966d193c 100644 --- a/tests/test_python.py +++ b/tests/test_python.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import _drgn import drgn from tests import TestCase diff --git a/tests/test_serialize.py b/tests/test_serialize.py index 6065f1df4..2462df3ac 100644 --- a/tests/test_serialize.py +++ b/tests/test_serialize.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from tests import TestCase from tests.libdrgn import deserialize_bits, serialize_bits diff --git a/tests/test_symbol.py b/tests/test_symbol.py index c85ff1446..3ff949325 100644 --- a/tests/test_symbol.py +++ b/tests/test_symbol.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import tempfile from typing import NamedTuple diff --git a/tests/test_thread.py b/tests/test_thread.py index b4450529d..33c564b54 100644 --- a/tests/test_thread.py +++ b/tests/test_thread.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import os import os.path diff --git a/tests/test_type.py b/tests/test_type.py index 1d1c48066..d0cae288f 100644 --- a/tests/test_type.py +++ b/tests/test_type.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from drgn import ( Language, diff --git a/tests/test_util.py b/tests/test_util.py index 6722e6da3..99a3dda82 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from functools import cmp_to_key from tests import TestCase diff --git a/tools/bpf_inspect.py b/tools/bpf_inspect.py index ec3c3fc54..7b39360d5 100755 --- a/tools/bpf_inspect.py +++ b/tools/bpf_inspect.py @@ -1,6 +1,6 @@ #!/usr/bin/env drgn # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import argparse diff --git a/util.py b/util.py index 4f3fc4ff8..d8173a263 100644 --- a/util.py +++ b/util.py @@ -1,10 +1,12 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later from functools import total_ordering import os from pathlib import Path +import platform import re +import sys from typing import Union @@ -113,3 +115,129 @@ def __lt__(self, other: object) -> bool: def __str__(self) -> str: return self._release + + +NORMALIZED_MACHINE_NAME = platform.machine() +if NORMALIZED_MACHINE_NAME.startswith("aarch64") or NORMALIZED_MACHINE_NAME == "arm64": + NORMALIZED_MACHINE_NAME = "aarch64" +elif NORMALIZED_MACHINE_NAME.startswith("arm") or NORMALIZED_MACHINE_NAME == "sa110": + NORMALIZED_MACHINE_NAME = "arm" +elif re.fullmatch(r"i.86", NORMALIZED_MACHINE_NAME): + NORMALIZED_MACHINE_NAME = "i386" +elif NORMALIZED_MACHINE_NAME.startswith("ppc64"): + NORMALIZED_MACHINE_NAME = "ppc64" +elif NORMALIZED_MACHINE_NAME.startswith("ppc"): + NORMALIZED_MACHINE_NAME = "ppc" +elif NORMALIZED_MACHINE_NAME == "riscv": + NORMALIZED_MACHINE_NAME = "riscv32" +elif re.match(r"sh[0-9]", NORMALIZED_MACHINE_NAME): + NORMALIZED_MACHINE_NAME = "sh" +elif NORMALIZED_MACHINE_NAME == "sun4u": + NORMALIZED_MACHINE_NAME = "sparc64" + +if NORMALIZED_MACHINE_NAME == "x86_64": + if sys.maxsize > 2**32: + SYS = {"bpf": 321, "kexec_file_load": 320, "rt_sigtimedwait": 128} + else: # x32 + SYS = {"bpf": 321, "kexec_file_load": 320, "rt_sigtimedwait": 523} +else: + SYS = { + "aarch64": { + "bpf": 280, + "kexec_file_load": 294, + "rt_sigtimedwait": 137, + "rt_sigtimedwait_time64": 421, + }, + "alpha": {"bpf": 515, "rt_sigtimedwait": 355}, + "arc": { + "bpf": 280, + "kexec_file_load": 294, + "rt_sigtimedwait": 137, + "rt_sigtimedwait_time64": 421, + }, + "arm": { + "bpf": 386, + "kexec_file_load": 401, + "rt_sigtimedwait": 177, + "rt_sigtimedwait_time64": 421, + }, + "csky": { + "bpf": 280, + "kexec_file_load": 294, + "rt_sigtimedwait": 137, + "rt_sigtimedwait_time64": 421, + }, + "hexagon": { + "bpf": 280, + "kexec_file_load": 294, + "rt_sigtimedwait": 137, + "rt_sigtimedwait_time64": 421, + }, + "i386": {"bpf": 357, "rt_sigtimedwait": 177, "rt_sigtimedwait_time64": 421}, + "ia64": {"bpf": 317, "rt_sigtimedwait": 159}, + "loongarch": { + "bpf": 280, + "kexec_file_load": 294, + "rt_sigtimedwait": 137, + "rt_sigtimedwait_time64": 421, + }, + "loongarch64": { + "bpf": 280, + "kexec_file_load": 294, + "rt_sigtimedwait": 137, + "rt_sigtimedwait_time64": 421, + }, + "m68k": {"bpf": 354, "rt_sigtimedwait": 177, "rt_sigtimedwait_time64": 421}, + "microblaze": { + "bpf": 387, + "rt_sigtimedwait": 177, + "rt_sigtimedwait_time64": 421, + }, + # TODO: mips is missing here because I don't know how to distinguish + # between the o32 and n32 ABIs. + "mips64": {"bpf": 315, "rt_sigtimedwait": 126}, + "nios2": { + "bpf": 280, + "kexec_file_load": 294, + "rt_sigtimedwait": 137, + "rt_sigtimedwait_time64": 421, + }, + "openrisc": { + "bpf": 280, + "kexec_file_load": 294, + "rt_sigtimedwait": 137, + "rt_sigtimedwait_time64": 421, + }, + "parisc": { + "bpf": 341, + "kexec_file_load": 355, + "rt_sigtimedwait": 177, + "rt_sigtimedwait_time64": 421, + }, + "parisc64": {"bpf": 341, "kexec_file_load": 355, "rt_sigtimedwait": 177}, + "ppc": {"bpf": 361, "rt_sigtimedwait": 176, "rt_sigtimedwait_time64": 421}, + "ppc64": {"bpf": 361, "rt_sigtimedwait": 176}, + "riscv32": { + "bpf": 280, + "kexec_file_load": 294, + "rt_sigtimedwait": 137, + "rt_sigtimedwait_time64": 421, + }, + "riscv64": { + "bpf": 280, + "kexec_file_load": 294, + "rt_sigtimedwait": 137, + "rt_sigtimedwait_time64": 421, + }, + "s390": { + "bpf": 351, + "kexec_file_load": 381, + "rt_sigtimedwait": 177, + "rt_sigtimedwait_time64": 421, + }, + "s390x": {"bpf": 351, "kexec_file_load": 381, "rt_sigtimedwait": 177}, + "sh": {"bpf": 375, "rt_sigtimedwait": 177, "rt_sigtimedwait_time64": 421}, + "sparc": {"bpf": 349, "rt_sigtimedwait": 105, "rt_sigtimedwait_time64": 421}, + "sparc64": {"bpf": 349, "rt_sigtimedwait": 105}, + "xtensa": {"bpf": 340, "rt_sigtimedwait": 229, "rt_sigtimedwait_time64": 421}, + }.get(NORMALIZED_MACHINE_NAME, {}) diff --git a/vmtest/__main__.py b/vmtest/__main__.py new file mode 100644 index 000000000..eca89575c --- /dev/null +++ b/vmtest/__main__.py @@ -0,0 +1,307 @@ +#!/usr/bin/env python3 + +from collections import OrderedDict +import logging +import os +from pathlib import Path +import shlex +import subprocess +import sys + +from util import KernelVersion +from vmtest.config import ( + ARCHITECTURES, + HOST_ARCHITECTURE, + KERNEL_FLAVORS, + SUPPORTED_KERNEL_VERSIONS, + Kernel, +) +from vmtest.download import DownloadCompiler, DownloadKernel, download_in_thread +from vmtest.kmod import build_kmod +from vmtest.rootfsbuild import build_drgn_in_rootfs +from vmtest.vm import LostVMError, run_in_vm + +logger = logging.getLogger(__name__) + + +class _ProgressPrinter: + def __init__(self, file): + self._file = file + if hasattr(file, "fileno"): + try: + columns = os.get_terminal_size(file.fileno())[0] + self._color = True + except OSError: + columns = 80 + self._color = False + self._header = "#" * columns + self._passed = {} + self._failed = {} + + def _green(self, s: str) -> str: + if self._color: + return "\033[32m" + s + "\033[0m" + else: + return s + + def _red(self, s: str) -> str: + if self._color: + return "\033[31m" + s + "\033[0m" + else: + return s + + def update(self, category: str, name: str, passed: bool): + d = self._passed if passed else self._failed + d.setdefault(category, []).append(name) + + if self._failed: + header = self._red(self._header) + else: + header = self._green(self._header) + + print(header, file=self._file) + print(file=self._file) + + if self._passed: + first = True + for category, names in self._passed.items(): + if first: + first = False + print(self._green("Passed:"), end=" ") + else: + print(" ", end=" ") + print(f"{category}: {', '.join(names)}") + if self._failed: + first = True + for category, names in self._failed.items(): + if first: + first = False + print(self._red("Failed:"), end=" ") + else: + print(" ", end=" ") + print(f"{category}: {', '.join(names)}") + + print(file=self._file) + print(header, file=self._file, flush=True) + + +def _kdump_works(kernel: Kernel) -> bool: + if kernel.arch.name == "aarch64": + # kexec fails with "kexec: setup_2nd_dtb failed." on older versions. + # See + # http://lists.infradead.org/pipermail/kexec/2020-November/021740.html. + return KernelVersion(kernel.release) >= KernelVersion("5.10") + elif kernel.arch.name == "arm": + # Without virtual address translation, we can't debug vmcores. Besides, + # kexec fails with "Could not find a free area of memory of 0xXXX + # bytes...". + return False + elif kernel.arch.name == "ppc64": + # Without virtual address translation, we can't debug vmcores. + return False + # Before 6.1, sysrq-c hangs. + # return KernelVersion(kernel.release) >= KernelVersion("6.1") + elif kernel.arch.name == "s390x": + # Before 5.15, sysrq-c hangs. + return KernelVersion(kernel.release) >= KernelVersion("5.15") + elif kernel.arch.name == "x86_64": + return True + else: + assert False, kernel.arch.name + + +if __name__ == "__main__": + import argparse + + logging.basicConfig( + format="%(asctime)s:%(levelname)s:%(name)s:%(message)s", level=logging.INFO + ) + parser = argparse.ArgumentParser( + description="test drgn in a virtual machine", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "-d", + "--directory", + metavar="DIR", + type=Path, + default="build/vmtest", + help="directory for vmtest artifacts", + ) + parser.add_argument( + "-a", + "--architecture", + dest="architectures", + action="append", + choices=["all", "foreign", *sorted(ARCHITECTURES)], + default=argparse.SUPPRESS, + required=HOST_ARCHITECTURE is None, + help="architecture to test, " + '"all" to test all supported architectures, ' + 'or "foreign" to test all supported architectures other than the host architecture; ' + "may be given multiple times" + + ( + "" if HOST_ARCHITECTURE is None else f" (default: {HOST_ARCHITECTURE.name})" + ), + ) + parser.add_argument( + "-k", + "--kernel", + metavar="PATTERN|{all," + ",".join(KERNEL_FLAVORS) + "}", + dest="kernels", + action="append", + default=argparse.SUPPRESS, + help="kernel to test, " + '"all" to test all supported kernels, ' + "or flavor name to test all supported kernels of a specific flavor; " + "may be given multiple times (default: none)", + ) + parser.add_argument( + "-l", + "--local", + action="store_true", + help="run local tests", + ) + args = parser.parse_args() + + architecture_names = [] + if hasattr(args, "architectures"): + for name in args.architectures: + if name == "all": + architecture_names.extend(ARCHITECTURES) + elif name == "foreign": + architecture_names.extend( + [ + arch.name + for arch in ARCHITECTURES.values() + if arch is not HOST_ARCHITECTURE + ] + ) + else: + architecture_names.append(name) + architectures = [ + ARCHITECTURES[name] for name in OrderedDict.fromkeys(architecture_names) + ] + else: + architectures = [HOST_ARCHITECTURE] + + if hasattr(args, "kernels"): + kernels = [] + for pattern in args.kernels: + if pattern == "all": + kernels.extend( + [ + version + ".*" + flavor + for version in SUPPORTED_KERNEL_VERSIONS + for flavor in KERNEL_FLAVORS + ] + ) + elif pattern in KERNEL_FLAVORS: + kernels.extend( + [version + ".*" + pattern for version in SUPPORTED_KERNEL_VERSIONS] + ) + else: + kernels.append(pattern) + args.kernels = OrderedDict.fromkeys(kernels) + else: + args.kernels = [] + + if not args.kernels and not args.local: + parser.error("at least one of -k/--kernel or -l/--local is required") + + if args.kernels: + to_download = [DownloadCompiler(arch) for arch in architectures] + for pattern in args.kernels: + for arch in architectures: + to_download.append(DownloadKernel(arch, pattern)) + else: + to_download = [] + + progress = _ProgressPrinter(sys.stderr) + + with download_in_thread(args.directory, to_download) as downloads: + for arch in architectures: + if arch is HOST_ARCHITECTURE: + subprocess.check_call( + [sys.executable, "setup.py", "build_ext", "-i"], + env={ + **os.environ, + "CONFIGURE_FLAGS": "--enable-compiler-warnings=error", + }, + ) + if args.local: + logger.info("running local tests on %s", arch.name) + status = subprocess.call( + [ + sys.executable, + "-m", + "pytest", + "-v", + "--ignore=tests/linux_kernel", + ] + ) + progress.update(arch.name, "local", status == 0) + else: + rootfs = args.directory / arch.name / "rootfs" + build_drgn_in_rootfs(rootfs) + if args.local: + logger.info("running local tests on %s", arch.name) + status = subprocess.call( + [ + "unshare", + "--map-root-user", + "--map-users=auto", + "--map-groups=auto", + "--fork", + "--pid", + "--mount-proc=" + str(rootfs / "proc"), + "sh", + "-c", + r""" +set -e + +mount --bind . "$1/mnt" +chroot "$1" sh -c 'cd /mnt && pytest -v --ignore=tests/linux_kernel' +""", + "sh", + rootfs, + ] + ) + progress.update(arch.name, "local", status == 0) + for kernel in downloads: + if not isinstance(kernel, Kernel): + continue + kmod = build_kmod(args.directory, kernel) + if _kdump_works(kernel): + kdump_command = """\ + "$PYTHON" -Bm vmtest.enter_kdump + # We should crash and not reach this. + exit 1 +""" + else: + kdump_command = "" + test_command = rf""" +set -e + +export PYTHON={shlex.quote(sys.executable)} +export DRGN_TEST_KMOD={shlex.quote(str(kmod))} +export DRGN_RUN_LINUX_KERNEL_TESTS=1 +if [ -e /proc/vmcore ]; then + "$PYTHON" -Bm pytest -v tests/linux_kernel/vmcore +else + insmod "$DRGN_TEST_KMOD" + "$PYTHON" -Bm pytest -v tests/linux_kernel --ignore=tests/linux_kernel/vmcore +{kdump_command} +fi +""" + try: + status = run_in_vm( + test_command, + kernel, + args.directory / kernel.arch.name / "rootfs", + args.directory, + ) + except LostVMError as e: + print("error:", e, file=sys.stderr) + status = -1 + progress.update(kernel.arch.name, kernel.release, status == 0) diff --git a/vmtest/asynciosubprocess.py b/vmtest/asynciosubprocess.py index 8309b42a2..8717888f2 100644 --- a/vmtest/asynciosubprocess.py +++ b/vmtest/asynciosubprocess.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import asyncio from contextlib import contextmanager diff --git a/vmtest/config.py b/vmtest/config.py new file mode 100644 index 000000000..ca74bf251 --- /dev/null +++ b/vmtest/config.py @@ -0,0 +1,411 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + + +from collections import OrderedDict +import inspect +import os +from pathlib import Path +from typing import Dict, Mapping, NamedTuple, Sequence + +from util import NORMALIZED_MACHINE_NAME + +# Kernel versions that we run tests on and therefore support. Keep this in sync +# with docs/support_matrix.rst. +SUPPORTED_KERNEL_VERSIONS = ( + "6.5", + "6.4", + "6.3", + "6.2", + "6.1", + "6.0", + "5.19", + "5.18", + "5.17", + "5.16", + "5.15", + "5.14", + "5.13", + "5.12", + "5.11", + "5.10", + "5.4", + "4.19", + "4.14", + "4.9", +) + +KERNEL_ORG_COMPILER_VERSION = "12.2.0" +VMTEST_KERNEL_VERSION = 21 + + +BASE_KCONFIG = """ +CONFIG_EXPERT=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y + +# We run the tests in KVM. +CONFIG_HYPERVISOR_GUEST=y +CONFIG_KVM_GUEST=y +CONFIG_PARAVIRT=y +CONFIG_PARAVIRT_SPINLOCKS=y + +# Minimum requirements for vmtest. +CONFIG_9P_FS=y +CONFIG_DEVTMPFS=y +CONFIG_INET=y +CONFIG_NET=y +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NET_9P=y +CONFIG_NET_9P_VIRTIO=y +CONFIG_OVERLAY_FS=y +CONFIG_PCI=y +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_XATTR=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_VIRTIO_PCI=y +CONFIG_HW_RANDOM=m +CONFIG_HW_RANDOM_VIRTIO=m + +# Lots of stuff expect Unix sockets. +CONFIG_UNIX=y + +# drgn needs debug info. +CONFIG_DEBUG_KERNEL=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_INFO_DWARF4=y + +# For testing live kernel debugging with /proc/kcore. +CONFIG_PROC_KCORE=y +# drgn needs /proc/kallsyms in some cases. Some test cases also need it. +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y + +# For testing kernel core dumps with /proc/vmcore. +CONFIG_CRASH_DUMP=y +CONFIG_PROC_VMCORE=y +CONFIG_KEXEC=y +CONFIG_KEXEC_FILE=y +# Needed for CONFIG_KEXEC_FILE. +CONFIG_CRYPTO=y +CONFIG_CRYPTO_SHA256=y + +# So that we can trigger a crash with /proc/sysrq-trigger. +CONFIG_MAGIC_SYSRQ=y + +# For block tests. +CONFIG_BLK_DEV_LOOP=m + +# For BPF tests. +CONFIG_BPF_SYSCALL=y +CONFIG_BPF_JIT=y +CONFIG_BPF_JIT_ALWAYS_ON=y +CONFIG_CGROUP_BPF=y +CONFIG_DEBUG_INFO_BTF=y +CONFIG_DEBUG_INFO_BTF_MODULES=y + +# For cgroup tests. +CONFIG_CGROUPS=y +# To select CONFIG_SOCK_CGROUP_DATA. (CONFIG_CGROUP_BPF also selects +# CONFIG_SOCK_CGROUP_DATA, but that's only present since Linux kernel commit +# 3007098494be ("cgroup: add support for eBPF programs") (in v4.10)). +CONFIG_CGROUP_NET_CLASSID=y + +# For kconfig tests. +CONFIG_IKCONFIG=m +CONFIG_IKCONFIG_PROC=y + +# For filesystem tests. +CONFIG_BTRFS_FS=m +CONFIG_EXT4_FS=m +CONFIG_XFS_FS=m + +# For mm tests. +CONFIG_HUGETLBFS=y + +# For net tests. +CONFIG_NAMESPACES=y + +# For nodemask tests. +CONFIG_NUMA=y + +# For slab allocator tests. +CONFIG_SLAB_FREELIST_HARDENED=y + +# For Traffic Control tests. +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_CLS_ACT=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=m + +# To enable CONFIG_XARRAY_MULTI for xarray tests. +CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_READ_ONLY_THP_FOR_FS=y +""" + + +class KernelFlavor(NamedTuple): + name: str + description: str + config: str + + +KERNEL_FLAVORS = OrderedDict( + (flavor.name, flavor) + for flavor in ( + KernelFlavor( + name="default", + description="Default configuration", + config=""" + CONFIG_SMP=y + CONFIG_SLUB=y + # For slab tests. + CONFIG_SLUB_DEBUG=y + """, + ), + KernelFlavor( + name="alternative", + description="SLAB allocator", + config=""" + CONFIG_SMP=y + CONFIG_SLAB=y + """, + ), + KernelFlavor( + name="tiny", + description="!SMP, !PREEMPT, and SLOB allocator", + config=""" + CONFIG_SMP=n + CONFIG_SLOB=y + # Linux kernel commit 149b6fa228ed ("mm, slob: rename CONFIG_SLOB to + # CONFIG_SLOB_DEPRECATED") (in v6.2) renamed the option for SLOB. + CONFIG_SLOB_DEPRECATED=y + # CONFIG_PREEMPT_DYNAMIC is not set + CONFIG_PREEMPT_NONE=y + # !PREEMPTION && !SMP will also select TINY_RCU. + """, + ), + ) +) + + +class Architecture(NamedTuple): + # Architecture name. This matches the names used by + # util.NORMALIZED_MACHINE_NAME and qemu-system-$arch_name. + name: str + # Value of ARCH variable to build the Linux kernel. + kernel_arch: str + # Directory under arch/ in the Linux kernel source tree. + kernel_srcarch: str + # Name of the architecture in Debian. + debian_arch: str + # Linux kernel configuration options. + kernel_config: str + # Flavor-specific Linux kernel configuration options. + kernel_flavor_configs: Mapping[str, str] + # Name of compiler target on + # https://mirrors.kernel.org/pub/tools/crosstool/. + kernel_org_compiler_name: str + # Options to pass to QEMU. + qemu_options: Sequence[str] + # Console device when using QEMU. + qemu_console: str + + +ARCHITECTURES = { + arch.name: arch + for arch in ( + Architecture( + name="aarch64", + kernel_arch="arm64", + kernel_srcarch="arm64", + debian_arch="arm64", + kernel_config=""" + CONFIG_PCI_HOST_GENERIC=y + CONFIG_RTC_CLASS=y + CONFIG_RTC_DRV_PL031=y + CONFIG_SERIAL_AMBA_PL011=y + CONFIG_SERIAL_AMBA_PL011_CONSOLE=y + """, + kernel_flavor_configs={ + "default": """ + CONFIG_ARM64_4K_PAGES=y + CONFIG_ARM64_VA_BITS_48=y + """, + "alternative": """ + CONFIG_ARM64_64K_PAGES=y + CONFIG_ARM64_VA_BITS_52=y + CONFIG_ARM64_PA_BITS_52=y + """, + "tiny": """ + CONFIG_ARM64_16K_PAGES=y + """, + }, + kernel_org_compiler_name="aarch64-linux", + qemu_options=("-M", "virt", "-cpu", "cortex-a57"), + qemu_console="ttyAMA0", + ), + Architecture( + name="arm", + kernel_arch="arm", + kernel_srcarch="arm", + debian_arch="armhf", + kernel_config=""" + CONFIG_NR_CPUS=8 + CONFIG_HIGHMEM=y + CONFIG_ARM_LPAE=n + # Debian armhf userspace assumes EABI and VFP. + CONFIG_AEABI=y + CONFIG_VFP=y + CONFIG_ARCH_VIRT=y + CONFIG_PCI_HOST_GENERIC=y + CONFIG_RTC_CLASS=y + CONFIG_RTC_DRV_PL031=y + CONFIG_SERIAL_AMBA_PL011=y + CONFIG_SERIAL_AMBA_PL011_CONSOLE=y + # Before Linux kernel commit f05eb1d24eb5 ("ARM: + # stackprotector: prefer compiler for TLS based per-task + # protector") (in v5.18), this enables the + # arm_ssp_per_task_plugin GCC plugin, which fails to build with + # the kernel.org cross compiler. + CONFIG_STACKPROTECTOR_PER_TASK=n + """, + kernel_flavor_configs={ + "alternative": """ + CONFIG_ARM_LPAE=y + """, + }, + kernel_org_compiler_name="arm-linux-gnueabi", + qemu_options=("-M", "virt,highmem=off"), + qemu_console="ttyAMA0", + ), + Architecture( + name="ppc64", + kernel_arch="powerpc", + kernel_srcarch="powerpc", + debian_arch="ppc64el", + kernel_config=""" + CONFIG_PPC64=y + CONFIG_CPU_LITTLE_ENDIAN=y + # Debian ppc64el userspace assumes AltiVec and VSX support. + CONFIG_ALTIVEC=y + CONFIG_VSX=y + CONFIG_RTC_CLASS=y + CONFIG_RTC_DRV_GENERIC=y + CONFIG_HVC_CONSOLE=y + # This has a missing dependency in v6.5-rc1 that causes a build + # failure, and we don't need it. + CONFIG_CRYPTO_AES_GCM_P10=n + """, + kernel_flavor_configs={}, + kernel_org_compiler_name="powerpc64-linux", + qemu_options=(), + qemu_console="hvc0", + ), + Architecture( + name="s390x", + kernel_arch="s390", + kernel_srcarch="s390", + debian_arch="s390x", + kernel_config=""" + # Needed for CONFIG_KEXEC_FILE. + CONFIG_CRYPTO_SHA256_S390=y + """, + kernel_flavor_configs={}, + kernel_org_compiler_name="s390-linux", + qemu_options=(), + qemu_console="ttysclp0", + ), + Architecture( + name="x86_64", + kernel_arch="x86_64", + kernel_srcarch="x86", + debian_arch="amd64", + kernel_config=""" + CONFIG_RTC_CLASS=y + CONFIG_RTC_DRV_CMOS=y + CONFIG_SERIAL_8250=y + CONFIG_SERIAL_8250_CONSOLE=y + """, + kernel_flavor_configs={}, + kernel_org_compiler_name="x86_64-linux", + qemu_options=("-nodefaults",), + qemu_console="ttyS0", + ), + ) +} + + +HOST_ARCHITECTURE = ARCHITECTURES.get(NORMALIZED_MACHINE_NAME) + + +class Kernel(NamedTuple): + arch: Architecture + release: str + path: Path + + +def local_kernel(arch: Architecture, path: Path) -> Kernel: + return Kernel( + arch=arch, + release=(path / "build/include/config/kernel.release").read_text().strip(), + path=path, + ) + + +class Compiler(NamedTuple): + target: Architecture + bin: Path + prefix: str + + def env(self) -> Dict[str, str]: + path = str(self.bin.resolve()) + try: + path += ":" + os.environ["PATH"] + except KeyError: + pass + return { + "PATH": path, + "CROSS_COMPILE": self.prefix, + } + + +def kconfig_localversion(flavor: KernelFlavor) -> str: + localversion = f"-vmtest{VMTEST_KERNEL_VERSION}" + # The default flavor should be the "latest" version. + localversion += ".1" if flavor.name == "default" else ".0" + localversion += flavor.name + return localversion + + +def kconfig(arch: Architecture, flavor: KernelFlavor) -> str: + return f"""\ +# Minimal Linux kernel configuration for booting into vmtest and running drgn +# tests ({arch.name} {flavor.name} flavor). + +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=n + +# base options + +{inspect.cleandoc(BASE_KCONFIG)} + +# {flavor.name} flavor options + +{inspect.cleandoc(flavor.config)} + +# {arch.name} options + +{inspect.cleandoc(arch.kernel_config)} + +# {arch.name} {flavor.name} flavor options + +{inspect.cleandoc(arch.kernel_flavor_configs.get(flavor.name, ""))} +""" diff --git a/vmtest/download.py b/vmtest/download.py index ad7ed6cb3..1ba8b9045 100644 --- a/vmtest/download.py +++ b/vmtest/download.py @@ -1,9 +1,10 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import argparse from contextlib import contextmanager import fnmatch +import functools import glob import logging import os @@ -14,69 +15,190 @@ import subprocess import tempfile import threading -from typing import Any, Dict, Iterator, Sequence, Union +from typing import ( + Any, + Callable, + Dict, + Generator, + Iterable, + Iterator, + List, + NamedTuple, + Optional, + Union, +) +import urllib.request -from util import KernelVersion +from util import NORMALIZED_MACHINE_NAME, KernelVersion +from vmtest.config import ( + ARCHITECTURES, + HOST_ARCHITECTURE, + KERNEL_ORG_COMPILER_VERSION, + Architecture, + Compiler, + Kernel, +) from vmtest.githubapi import GitHubApi logger = logging.getLogger(__name__) +COMPILER_URL = "https://mirrors.kernel.org/pub/tools/crosstool/" VMTEST_GITHUB_RELEASE = ("osandov", "drgn", "vmtest-assets") +GitHubAsset = Dict[str, Any] + + def available_kernel_releases( - github_release: Dict[str, Any], arch: str -) -> Dict[str, Dict[str, Any]]: - pattern = re.compile(r"kernel-(.*)\." + re.escape(arch) + r"\.tar\.zst") - releases = {} + github_release: Dict[str, Any] +) -> Dict[str, Dict[str, GitHubAsset]]: + releases: Dict[str, Dict[str, GitHubAsset]] = {} + pattern = re.compile(r"kernel-(?P.*)\.(?P\w+)\.tar\.zst") for asset in github_release["assets"]: match = pattern.fullmatch(asset["name"]) if match: - releases[match.group(1)] = asset + try: + arch_releases = releases[match.group("arch")] + except KeyError: + arch_releases = releases[match.group("arch")] = {} + arch_releases[match.group("release")] = asset return releases -def _download_kernel(gh: GitHubApi, url: str, dir: Path) -> None: - dir.parent.mkdir(parents=True, exist_ok=True) - tmp_dir = Path(tempfile.mkdtemp(dir=dir.parent)) - try: - # Don't assume that the available version of tar has zstd support or - # the non-standard -I/--use-compress-program option. - with subprocess.Popen( - ["zstd", "-d", "-", "--stdout"], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - ) as zstd_proc, subprocess.Popen( - ["tar", "-C", str(tmp_dir), "-x"], - stdin=zstd_proc.stdout, - ) as tar_proc: - assert zstd_proc.stdin is not None - try: - with gh.download(url) as resp: - shutil.copyfileobj(resp, zstd_proc.stdin) - finally: - zstd_proc.stdin.close() - if zstd_proc.returncode != 0: - raise subprocess.CalledProcessError(zstd_proc.returncode, zstd_proc.args) - if tar_proc.returncode != 0: - raise subprocess.CalledProcessError(tar_proc.returncode, tar_proc.args) - except BaseException: - shutil.rmtree(tmp_dir, ignore_errors=True) - raise +class DownloadKernel(NamedTuple): + arch: Architecture + pattern: str + + +class DownloadCompiler(NamedTuple): + target: Architecture + + +Download = Union[DownloadKernel, DownloadCompiler] +Downloaded = Union[Kernel, Compiler] + + +def _download_kernel( + gh: GitHubApi, arch: Architecture, release: str, url: Optional[str], dir: Path +) -> Kernel: + if url is None: + logger.info( + "kernel release %s for %s already downloaded to %s", release, arch.name, dir + ) else: - tmp_dir.rename(dir) + logger.info( + "downloading kernel release %s for %s to %s from %s", + release, + arch.name, + dir, + url, + ) + dir.parent.mkdir(parents=True, exist_ok=True) + tmp_dir = Path(tempfile.mkdtemp(dir=dir.parent)) + try: + # Don't assume that the available version of tar has zstd support or + # the non-standard -I/--use-compress-program option. + with subprocess.Popen( + ["zstd", "-d", "-", "--stdout"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + ) as zstd_proc, subprocess.Popen( + ["tar", "-C", str(tmp_dir), "-x"], + stdin=zstd_proc.stdout, + ) as tar_proc: + assert zstd_proc.stdin is not None + try: + with gh.download(url) as resp: + shutil.copyfileobj(resp, zstd_proc.stdin) + finally: + zstd_proc.stdin.close() + if zstd_proc.returncode != 0: + raise subprocess.CalledProcessError( + zstd_proc.returncode, zstd_proc.args + ) + if tar_proc.returncode != 0: + raise subprocess.CalledProcessError(tar_proc.returncode, tar_proc.args) + except BaseException: + shutil.rmtree(tmp_dir, ignore_errors=True) + raise + else: + tmp_dir.rename(dir) + return Kernel(arch, release, dir) + + +_KERNEL_ORG_COMPILER_HOST_NAME = { + "aarch64": "arm64", + "ppc64": "ppc64le", + "x86_64": "x86_64", +}.get(NORMALIZED_MACHINE_NAME) + + +def downloaded_compiler(download_dir: Path, target: Architecture) -> Compiler: + if _KERNEL_ORG_COMPILER_HOST_NAME is None: + raise FileNotFoundError( + f"kernel.org compilers are not available for {NORMALIZED_MACHINE_NAME} hosts" + ) + return Compiler( + target=target, + bin=download_dir + / f"{_KERNEL_ORG_COMPILER_HOST_NAME}-gcc-{KERNEL_ORG_COMPILER_VERSION}-nolibc-{target.kernel_org_compiler_name}" + / "bin", + prefix=target.kernel_org_compiler_name + "-", + ) + +def _download_compiler(compiler: Compiler) -> Compiler: + dir = compiler.bin.parent + if dir.exists(): + logger.info( + "compiler for %s already downloaded to %s", compiler.target.name, dir + ) + else: + url = f"{COMPILER_URL}files/bin/{_KERNEL_ORG_COMPILER_HOST_NAME}/{KERNEL_ORG_COMPILER_VERSION}/{dir.name}.tar.xz" + logger.info( + "downloading compiler for %s from %s to %s", compiler.target.name, url, dir + ) + dir.parent.mkdir(parents=True, exist_ok=True) + with tempfile.TemporaryDirectory(dir=dir.parent) as tmp_name: + tmp_dir = Path(tmp_name) + with subprocess.Popen( + ["xz", "--decompress"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + ) as xz_proc, subprocess.Popen( + ["tar", "-C", str(tmp_dir), "-x"], + stdin=xz_proc.stdout, + ) as tar_proc: + assert xz_proc.stdin is not None + try: + with urllib.request.urlopen(url) as resp: + shutil.copyfileobj(resp, xz_proc.stdin) + finally: + xz_proc.stdin.close() + if xz_proc.returncode != 0: + raise subprocess.CalledProcessError(xz_proc.returncode, xz_proc.args) + if tar_proc.returncode != 0: + raise subprocess.CalledProcessError(tar_proc.returncode, tar_proc.args) + archive_subdir = Path( + f"gcc-{KERNEL_ORG_COMPILER_VERSION}-nolibc/{compiler.target.kernel_org_compiler_name}" + ) + archive_bin_subdir = archive_subdir / "bin" + if not (tmp_dir / archive_bin_subdir).exists(): + raise FileNotFoundError( + f"downloaded archive does not contain {archive_bin_subdir}" + ) + (tmp_dir / archive_subdir).rename(dir) + return compiler -def download_kernels( - download_dir: Path, arch: str, kernels: Sequence[str] -) -> Iterator[Path]: + +def download(download_dir: Path, downloads: Iterable[Download]) -> Iterator[Downloaded]: gh = GitHubApi(os.getenv("GITHUB_TOKEN")) # We don't want to make any API requests if we don't have to, so we don't # fetch this until we need it. cached_kernel_releases = None - def get_available_kernel_releases() -> Dict[str, Dict[str, Any]]: + def get_available_kernel_releases() -> Dict[str, Dict[str, GitHubAsset]]: nonlocal cached_kernel_releases if cached_kernel_releases is None: logger.info("getting available kernel releases") @@ -85,65 +207,75 @@ def get_available_kernel_releases() -> Dict[str, Dict[str, Any]]: gh.get_release_by_tag( *VMTEST_GITHUB_RELEASE, cache=download_dir / "github_release.json" ), - arch, ) return cached_kernel_releases - arch_download_dir = download_dir / arch - - # Make sure all of the given kernels exist first. - to_download = [] - for kernel in kernels: - if kernel != glob.escape(kernel): - try: - match = max( - ( - available - for available in get_available_kernel_releases() - if fnmatch.fnmatch(available, kernel) - ), - key=KernelVersion, - ) - except ValueError: - raise Exception(f"no available kernel release matches {kernel!r}") + download_calls: List[Callable[[], Downloaded]] = [] + for download in downloads: + if isinstance(download, DownloadKernel): + if download.pattern == glob.escape(download.pattern): + release = download.pattern else: - logger.info("kernel release pattern %s matches %s", kernel, match) - kernel = match - kernel_dir = arch_download_dir / ("kernel-" + kernel) - if kernel_dir.exists(): - # As a policy, vmtest assets will never be updated with the same - # name. Therefore, if the kernel was previously downloaded, we - # don't need to download it again. - url = None - else: - try: - asset = get_available_kernel_releases()[kernel] - except KeyError: - raise Exception(f"kernel release {kernel} not found") - url = asset["url"] - to_download.append((kernel, kernel_dir, url)) - - for release, kernel_dir, url in to_download: - if url is None: - logger.info( - "kernel release %s already downloaded to %s", release, kernel_dir + try: + release = max( + ( + available + for available in get_available_kernel_releases()[ + download.arch.name + ] + if fnmatch.fnmatch(available, download.pattern) + ), + key=KernelVersion, + ) + except ValueError: + raise Exception( + f"no available kernel release matches {download.pattern!r} on {download.arch.name}" + ) + else: + logger.info( + "kernel release pattern %s matches %s on %s", + download.pattern, + release, + download.arch.name, + ) + kernel_dir = download_dir / download.arch.name / ("kernel-" + release) + if kernel_dir.exists(): + # As a policy, vmtest assets will never be updated with the + # same name. Therefore, if the kernel was previously + # downloaded, we don't need to download it again. + url = None + else: + try: + asset = get_available_kernel_releases()[download.arch.name][release] + except KeyError: + raise Exception(f"kernel release {release} not found") + url = asset["url"] + download_calls.append( + functools.partial( + _download_kernel, gh, download.arch, release, url, kernel_dir + ) ) - else: - logger.info( - "downloading kernel release %s to %s from %s", release, kernel_dir, url + elif isinstance(download, DownloadCompiler): + download_calls.append( + functools.partial( + _download_compiler, + downloaded_compiler(download_dir, download.target), + ) ) - _download_kernel(gh, url, kernel_dir) - yield kernel_dir + else: + assert False + + for call in download_calls: + yield call() -def _download_kernels_thread( +def _download_thread( download_dir: Path, - arch: str, - kernels: Sequence[str], - q: "queue.Queue[Union[Path, Exception]]", + downloads: Iterable[Download], + q: "queue.Queue[Union[Downloaded, Exception]]", ) -> None: try: - it = download_kernels(download_dir, arch, kernels) + it = download(download_dir, downloads) while True: q.put(next(it)) except Exception as e: @@ -151,12 +283,12 @@ def _download_kernels_thread( @contextmanager -def download_kernels_in_thread( - download_dir: Path, arch: str, kernels: Sequence[str] -) -> Iterator[Iterator[Path]]: - q: "queue.Queue[Union[Path, Exception]]" = queue.Queue() +def download_in_thread( + download_dir: Path, downloads: Iterable[Download] +) -> Generator[Iterator[Downloaded], None, None]: + q: "queue.Queue[Union[Downloaded, Exception]]" = queue.Queue() - def aux() -> Iterator[Path]: + def aux() -> Iterator[Downloaded]: while True: obj = q.get() if isinstance(obj, StopIteration): @@ -168,8 +300,8 @@ def aux() -> Iterator[Path]: thread = None try: thread = threading.Thread( - target=_download_kernels_thread, - args=(download_dir, arch, kernels, q), + target=_download_thread, + args=(download_dir, downloads, q), daemon=True, ) thread.start() @@ -179,6 +311,35 @@ def aux() -> Iterator[Path]: thread.join() +ARCH_ARGPARSE_METAVAR = f"{{{','.join(ARCHITECTURES)}}}" +if HOST_ARCHITECTURE is None: + DOWNLOAD_KERNEL_ARGPARSE_METAVAR = f"{ARCH_ARGPARSE_METAVAR}:PATTERN" + DEFAULT_ARCH_ARGPARSE_HELP = "" +else: + DOWNLOAD_KERNEL_ARGPARSE_METAVAR = f"[{ARCH_ARGPARSE_METAVAR}:]PATTERN" + DEFAULT_ARCH_ARGPARSE_HELP = f" (default: {HOST_ARCHITECTURE.name})" + + +def architecture_argparse_type(arg: str) -> Architecture: + try: + return ARCHITECTURES[arg] + except KeyError: + raise argparse.ArgumentTypeError( + f"architecture must be one of ({', '.join(ARCHITECTURES)})" + ) from None + + +def download_kernel_argparse_type(arg: str) -> DownloadKernel: + arch_name, sep, pattern = arg.rpartition(":") + if sep: + arch = architecture_argparse_type(arch_name) + else: + if HOST_ARCHITECTURE is None: + raise argparse.ArgumentTypeError("architecture is required") + arch = HOST_ARCHITECTURE + return DownloadKernel(arch, pattern) + + def main() -> None: logging.basicConfig( format="%(asctime)s:%(levelname)s:%(name)s:%(message)s", level=logging.INFO @@ -191,9 +352,23 @@ def main() -> None: parser.add_argument( "-k", "--kernel", + metavar=DOWNLOAD_KERNEL_ARGPARSE_METAVAR, + dest="downloads", action="append", - dest="kernels", - help="download latest kernel matching glob pattern; may be given multiple times", + type=download_kernel_argparse_type, + default=argparse.SUPPRESS, + help=f"download latest kernel for given architecture{DEFAULT_ARCH_ARGPARSE_HELP} matching glob pattern; may be given multiple times", + ) + parser.add_argument( + "-c", + "--compiler", + metavar=ARCH_ARGPARSE_METAVAR, + dest="downloads", + action="append", + nargs=1 if HOST_ARCHITECTURE is None else "?", + type=lambda arg: DownloadCompiler(architecture_argparse_type(arg)), + default=argparse.SUPPRESS, + help=f"download compiler for given architecture{DEFAULT_ARCH_ARGPARSE_HELP} from {COMPILER_URL}; may be given multiple times", ) parser.add_argument( "-d", @@ -204,9 +379,25 @@ def main() -> None: help="directory to download assets to", ) args = parser.parse_args() + if not hasattr(args, "downloads"): + args.downloads = [] + # --compiler with no argument is appended as None. Fix it. + for i, download_arg in enumerate(args.downloads): + if download_arg is None: + assert HOST_ARCHITECTURE is not None + args.downloads[i] = DownloadCompiler(HOST_ARCHITECTURE) - for path in download_kernels(args.download_directory, "x86_64", args.kernels or ()): - print(path) + for downloaded in download(args.download_directory, args.downloads): + if isinstance(downloaded, Kernel): + print( + f"kernel: arch={downloaded.arch.name} release={downloaded.release} path={downloaded.path}" + ) + elif isinstance(downloaded, Compiler): + print( + f"compiler: target={downloaded.target.name} bin={downloaded.bin} prefix={downloaded.prefix}" + ) + else: + assert False if __name__ == "__main__": diff --git a/vmtest/enter_kdump.py b/vmtest/enter_kdump.py index bcb7415ff..ce4af17cc 100644 --- a/vmtest/enter_kdump.py +++ b/vmtest/enter_kdump.py @@ -1,43 +1,66 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later - -# This isn't great: it's specific to x86-64, both by virtue of the syscall -# number and because kexec_file_load isn't implemented on many architectures, -# especially on older kernels. +# SPDX-License-Identifier: LGPL-2.1-or-later import ctypes import os import re +import subprocess + +from util import NORMALIZED_MACHINE_NAME, SYS -SYS_kexec_file_load = 320 # On x86-64. KEXEC_FILE_ON_CRASH = 2 KEXEC_FILE_NO_INITRAMFS = 4 -syscall = ctypes.CDLL(None, use_errno=True).syscall -syscall.restype = ctypes.c_long - -with open("/proc/cmdline", "rb") as f: - cmdline = f.read().rstrip(b"\n") - cmdline = re.sub(rb"(^|\s)crashkernel=\S+", b"", cmdline) - # `nokaslr` is required to avoid sporadically failing to reserve space for the - # capture kernel - cmdline += b" nokaslr" - if os.getenv("KDUMP_NEEDS_NOSMP"): - # `nosmp` is required to avoid QEMU sporadically failing an internal - # assertion when using emulation. - cmdline += b" nosmp" - -with open(f"/lib/modules/{os.uname().release}/vmlinuz", "rb") as kernel: - if syscall( - ctypes.c_long(SYS_kexec_file_load), - ctypes.c_int(kernel.fileno()), - ctypes.c_int(-1), - ctypes.c_ulong(len(cmdline) + 1), - ctypes.c_char_p(cmdline + b"\0"), - ctypes.c_ulong(KEXEC_FILE_ON_CRASH | KEXEC_FILE_NO_INITRAMFS), - ): - errno = ctypes.get_errno() - raise OSError(errno, os.strerror(errno)) - -with open("/proc/sysrq-trigger", "w") as f: - f.write("c") + +def main(): + with open("/proc/cmdline", "rb") as f: + cmdline = f.read().rstrip(b"\n") + cmdline = re.sub(rb"(^|\s)crashkernel=\S+", b"", cmdline) + # `nokaslr` is required to avoid sporadically failing to reserve space for the + # capture kernel + cmdline += b" nokaslr" + if os.getenv("KDUMP_NEEDS_NOSMP"): + # `nosmp` is required to avoid QEMU sporadically failing an internal + # assertion when using emulation. + cmdline += b" nosmp" + + vmlinuz = f"/lib/modules/{os.uname().release}/vmlinuz" + + # On x86-64, kexec_file_load() is supported on all kernel versions we care + # about, and it's simple enough to call ourselves. On other architectures, + # we just use kexec(8). + if NORMALIZED_MACHINE_NAME == "x86_64": + syscall = ctypes.CDLL(None, use_errno=True).syscall + syscall.restype = ctypes.c_long + + with open(vmlinuz, "rb") as kernel: + if syscall( + ctypes.c_long(SYS["kexec_file_load"]), + ctypes.c_int(kernel.fileno()), + ctypes.c_int(-1), + ctypes.c_ulong(len(cmdline) + 1), + ctypes.c_char_p(cmdline + b"\0"), + ctypes.c_ulong(KEXEC_FILE_ON_CRASH | KEXEC_FILE_NO_INITRAMFS), + ): + errno = ctypes.get_errno() + raise OSError(errno, os.strerror(errno)) + else: + subprocess.check_call( + [ + "kexec", + "--load-panic", + "--kexec-syscall-auto", + "--command-line=" + cmdline.decode(), + vmlinuz, + ] + ) + + with open("/proc/self/comm", "w") as f: + f.write("selfdestruct") + + with open("/proc/sysrq-trigger", "w") as f: + f.write("c") + + +if __name__ == "__main__": + main() diff --git a/vmtest/githubapi.py b/vmtest/githubapi.py index 46a7a32fc..298723692 100644 --- a/vmtest/githubapi.py +++ b/vmtest/githubapi.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import json from pathlib import Path @@ -154,7 +154,12 @@ def _request( data: Any = None, ) -> Any: return self._session.request( - method, url, params=params, headers=headers, data=data + method, + url, + params=params, + headers=headers, + data=data, + raise_for_status=True, ) async def _cached_get_json(self, endpoint: str, cache: _CACHE) -> Any: diff --git a/vmtest/kbuild.py b/vmtest/kbuild.py index 46003f9cf..1b3279378 100644 --- a/vmtest/kbuild.py +++ b/vmtest/kbuild.py @@ -1,18 +1,19 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import argparse import asyncio import filecmp import logging +import os from pathlib import Path import shlex import shutil import sys import tempfile -from typing import IO, Any, NamedTuple, Optional, Tuple, Union +from typing import IO, Any, Mapping, NamedTuple, Optional, Sequence, Tuple, Union -from util import nproc +from util import KernelVersion, nproc from vmtest.asynciosubprocess import ( CalledProcessError, check_call, @@ -20,156 +21,135 @@ check_output_shell, pipe_context, ) +from vmtest.config import ( + ARCHITECTURES, + HOST_ARCHITECTURE, + KERNEL_FLAVORS, + Architecture, + Compiler, + KernelFlavor, + kconfig, + kconfig_localversion, +) +from vmtest.download import COMPILER_URL, DownloadCompiler, download logger = logging.getLogger(__name__) -class KernelFlavor(NamedTuple): +_PACKAGE_FORMATS = ("tar.zst", "directory") + + +class _Patch(NamedTuple): name: str - description: str - config: str - - def localversion(self) -> str: - localversion = "-vmtest13" - # The default flavor should be the "latest" version. - localversion += ".1" if self.name == "default" else ".0" - localversion += self.name - return localversion - - -KERNEL_FLAVORS = [ - KernelFlavor( - name="default", - description="Default configuration", - config=""" -CONFIG_SMP=y -CONFIG_SLUB=y -# For slab tests. -CONFIG_SLUB_DEBUG=y -""", + # [inclusive, exclusive) ranges of kernel versions to apply this patch to. + # None means any version. + versions: Sequence[Tuple[Optional[KernelVersion], Optional[KernelVersion]]] + + +_PATCHES = ( + _Patch( + name="proc-kcore-allow-enabling-CONFIG_PROC_KCORE-on-ARM.patch", + versions=((None, None),), ), - KernelFlavor( - name="alternative", - description="SLAB allocator", - config=""" -CONFIG_SMP=y -CONFIG_SLAB=y -""", + _Patch( + name="5.15-kbuild-Unify-options-for-BTF-generation-for-vmlinux.patch", + versions=((KernelVersion("5.13"), KernelVersion("5.15.66")),), ), - KernelFlavor( - name="tiny", - description="!SMP, !PREEMPT, and SLOB allocator", - config=""" -CONFIG_SMP=n -CONFIG_SLOB=y -# CONFIG_PREEMPT_DYNAMIC is not set -CONFIG_PREEMPT_NONE=y -# !PREEMPTION && !SMP will also select TINY_RCU. -""", + _Patch( + name="5.12-kbuild-Quote-OBJCOPY-var-to-avoid-a-pahole-call-brea.patch", + versions=((KernelVersion("5.11"), KernelVersion("5.12.10")),), ), -] - - -_PACKAGE_FORMATS = ("tar.zst", "directory") + _Patch( + name="5.11-bpf-Generate-BTF_KIND_FLOAT-when-linking-vmlinux.patch", + versions=((KernelVersion("5.11"), KernelVersion("5.13")),), + ), + _Patch( + name="5.10-kbuild-skip-per-CPU-BTF-generation-for-pahole-v1.18-.patch", + versions=((KernelVersion("5.11"), KernelVersion("5.13")),), + ), + _Patch( + name="5.11-kbuild-Unify-options-for-BTF-generation-for-vmlinux.patch", + versions=((KernelVersion("5.11"), KernelVersion("5.13")),), + ), + _Patch( + name="kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch", + versions=((KernelVersion("5.18"), KernelVersion("5.19.17")),), + ), + _Patch( + name="5.15-kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch", + versions=( + (KernelVersion("5.16"), KernelVersion("5.18")), + (KernelVersion("5.13"), KernelVersion("5.15.66")), + ), + ), + _Patch( + name="5.10-kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch", + versions=((KernelVersion("5.11"), KernelVersion("5.13")),), + ), + _Patch( + name="s390-mm-make-memory_block_size_bytes-available-for-M.patch", + versions=((KernelVersion("4.3"), KernelVersion("4.11")),), + ), + _Patch( + name="libsubcmd-Fix-use-after-free-for-realloc-.-0.patch", + versions=( + (KernelVersion("5.16"), KernelVersion("5.16.11")), + (KernelVersion("5.11"), KernelVersion("5.15.25")), + (KernelVersion("5.5"), KernelVersion("5.10.102")), + (KernelVersion("4.20"), KernelVersion("5.4.181")), + (KernelVersion("4.15"), KernelVersion("4.19.231")), + (KernelVersion("4.10"), KernelVersion("4.14.268")), + (KernelVersion("4.5"), KernelVersion("4.9.303")), + ), + ), +) -def kconfig(flavor: KernelFlavor) -> str: - return rf"""# Minimal Linux kernel configuration for booting into vmtest and running drgn -# tests ({flavor.name} flavor). - -CONFIG_LOCALVERSION="{flavor.localversion()}" -CONFIG_EXPERT=y -{flavor.config} -CONFIG_MODULES=y -CONFIG_CC_OPTIMIZE_FOR_SIZE=y - -# We run the tests in KVM. -CONFIG_HYPERVISOR_GUEST=y -CONFIG_KVM_GUEST=y -CONFIG_PARAVIRT=y -CONFIG_PARAVIRT_SPINLOCKS=y - -# Minimum requirements for vmtest. -CONFIG_9P_FS=y -CONFIG_DEVTMPFS=y -CONFIG_INET=y -CONFIG_NET=y -CONFIG_NETWORK_FILESYSTEMS=y -CONFIG_NET_9P=y -CONFIG_NET_9P_VIRTIO=y -CONFIG_OVERLAY_FS=y -CONFIG_PCI=y -CONFIG_PROC_FS=y -CONFIG_SERIAL_8250=y -CONFIG_SERIAL_8250_CONSOLE=y -CONFIG_SYSFS=y -CONFIG_TMPFS=y -CONFIG_TMPFS_XATTR=y -CONFIG_VIRTIO_CONSOLE=y -CONFIG_VIRTIO_PCI=y -CONFIG_HW_RANDOM=m -CONFIG_HW_RANDOM_VIRTIO=m - -# drgn needs debug info. -CONFIG_DEBUG_KERNEL=y -CONFIG_DEBUG_INFO=y -CONFIG_DEBUG_INFO_DWARF4=y - -# For testing live kernel debugging with /proc/kcore. -CONFIG_PROC_KCORE=y -# drgn needs /proc/kallsyms in some cases. Some test cases also need it. -CONFIG_KALLSYMS=y -CONFIG_KALLSYMS_ALL=y - -# For testing kernel core dumps with /proc/vmcore. -CONFIG_CRASH_DUMP=y -CONFIG_PROC_VMCORE=y -CONFIG_KEXEC=y -CONFIG_KEXEC_FILE=y -# Needed for CONFIG_KEXEC_FILE. -CONFIG_CRYPTO=y -CONFIG_CRYPTO_SHA256=y - -# So that we can trigger a crash with /proc/sysrq-trigger. -CONFIG_MAGIC_SYSRQ=y - -# For block tests. -CONFIG_BLK_DEV_LOOP=m - -# For BPF tests. -CONFIG_BPF_SYSCALL=y -CONFIG_BPF_JIT=y -CONFIG_BPF_JIT_ALWAYS_ON=y -CONFIG_CGROUP_BPF=y -CONFIG_DEBUG_INFO_BTF=y -CONFIG_DEBUG_INFO_BTF_MODULES=y - -# For cgroup tests. -CONFIG_CGROUPS=y - -# For kconfig tests. -CONFIG_IKCONFIG=m -CONFIG_IKCONFIG_PROC=y - -# For net tests. -CONFIG_NAMESPACES=y - -# For nodemask tests. -CONFIG_NUMA=y - -# For slab allocator tests. -CONFIG_SLAB_FREELIST_HARDENED=y - -# For Traffic Control tests. -CONFIG_NET_SCHED=y -CONFIG_NET_SCH_PRIO=m -CONFIG_NET_SCH_SFQ=m -CONFIG_NET_SCH_TBF=m -CONFIG_NET_SCH_INGRESS=m -CONFIG_NET_CLS_ACT=y -CONFIG_NETDEVICES=y -CONFIG_DUMMY=m -""" +async def apply_patches(kernel_dir: Path) -> None: + patch_dir = Path(__file__).parent / "patches" + version = KernelVersion( + (await check_output("make", "-s", "kernelversion", cwd=kernel_dir)) + .decode() + .strip() + ) + logger.info("applying patches for kernel version %s", version) + any_applied = False + for patch in _PATCHES: + for min_version, max_version in patch.versions: + if (min_version is None or min_version <= version) and ( + max_version is None or version < max_version + ): + break + else: + continue + logger.info("applying %s", patch.name) + any_applied = True + proc = await asyncio.create_subprocess_exec( + "git", + "apply", + str(patch_dir / patch.name), + cwd=kernel_dir, + stderr=asyncio.subprocess.PIPE, + ) + stderr = await proc.stderr.read() + if await proc.wait() != 0: + try: + await check_call( + "git", + "apply", + "--reverse", + "--check", + str(patch_dir / patch.name), + cwd=kernel_dir, + stderr=asyncio.subprocess.DEVNULL, + ) + except CalledProcessError: + sys.stderr.buffer.write(stderr) + sys.stderr.buffer.flush() + raise + logger.info("already applied") + if not any_applied: + logger.info("no patches") class KBuild: @@ -177,15 +157,16 @@ def __init__( self, kernel_dir: Path, build_dir: Path, + arch: Architecture, flavor: KernelFlavor, - arch: str, + env: Optional[Mapping[str, str]] = None, build_log_file: Union[int, IO[Any], None] = None, ) -> None: self._build_dir = build_dir self._kernel_dir = kernel_dir self._flavor = flavor self._arch = arch - self._srcarch = {"x86_64": "x86"}.get(arch, arch) + self._env = env self._build_stdout = build_log_file self._build_stderr = ( None if build_log_file is None else asyncio.subprocess.STDOUT @@ -229,7 +210,8 @@ async def _prepare_make(self) -> Tuple[str, ...]: self._cached_make_args = ( "-C", str(self._kernel_dir), - "ARCH=" + str(self._arch), + "ARCH=" + str(self._arch.kernel_arch), + "LOCALVERSION=" + kconfig_localversion(self._flavor), "O=" + str(build_dir_real), "KBUILD_ABS_SRCTREE=1", "KBUILD_BUILD_USER=drgn", @@ -249,7 +231,11 @@ async def _kernel_release(self) -> str: self._cached_kernel_release = ( ( await check_output( - "make", *self._cached_make_args, "-s", "kernelrelease" + "make", + *self._cached_make_args, + "-s", + "kernelrelease", + env=self._env, ) ) .decode() @@ -258,7 +244,12 @@ async def _kernel_release(self) -> str: return self._cached_kernel_release async def build(self) -> None: - logger.info("building %s kernel in %s", self._flavor.name, self._build_dir) + logger.info( + "building %s %s kernel in %s", + self._arch.name, + self._flavor.name, + self._build_dir, + ) build_log_file_name = getattr(self._build_stdout, "name", None) if build_log_file_name is not None: logger.info("build logs in %s", build_log_file_name) @@ -268,7 +259,7 @@ async def build(self) -> None: config = self._build_dir / ".config" tmp_config = self._build_dir / ".config.vmtest.tmp" - tmp_config.write_text(kconfig(self._flavor)) + tmp_config.write_text(kconfig(self._arch, self._flavor)) await check_call( "make", *make_args, @@ -276,6 +267,7 @@ async def build(self) -> None: "olddefconfig", stdout=self._build_stdout, stderr=self._build_stderr, + env=self._env, ) try: equal = filecmp.cmp(config, tmp_config) @@ -298,34 +290,32 @@ async def build(self) -> None: "all", stdout=self._build_stdout, stderr=self._build_stderr, + env=self._env, + ) + logger.info( + "built kernel %s for %s in %s", + kernel_release, + self._arch.name, + self._build_dir, ) - logger.info("built kernel %s in %s", kernel_release, self._build_dir) - def _copy_module_build(self, modules_dir: Path) -> None: + def _copy_module_build(self, modules_build_dir: Path) -> None: logger.info("copying module build files") - # `make modules_install` creates these as symlinks to the absolute path - # of the source directory. Delete them, populate build, and make source - # a symlink to build. - modules_build_dir = modules_dir / "build" - modules_source_dir = modules_dir / "source" - modules_build_dir.unlink() - modules_build_dir.mkdir() - modules_source_dir.unlink() - modules_source_dir.symlink_to("build") - # Files and directories (as glob patterns) required for external module # builds. This list was determined through trial and error. - files = ( + files = [ ".config", "Module.symvers", - f"arch/{self._srcarch}/Makefile", + f"arch/{self._arch.kernel_srcarch}/Makefile", + f"arch/{self._arch.kernel_srcarch}/kernel/module.lds", "scripts/Kbuild.include", "scripts/Makefile*", "scripts/basic/fixdep", "scripts/check-local-export", "scripts/gcc-goto.sh", "scripts/gcc-version.sh", + "scripts/ld-version.sh", "scripts/mod/modpost", "scripts/module-common.lds", "scripts/module.lds", @@ -333,10 +323,21 @@ def _copy_module_build(self, modules_dir: Path) -> None: "scripts/pahole-flags.sh", "scripts/pahole-version.sh", "scripts/subarch.include", + "tools/bpf/resolve_btfids/resolve_btfids", "tools/objtool/objtool", - ) + ] + # Before Linux kernel commit bca8f17f57bd ("arm64: Get rid of + # asm/opcodes.h") (in v4.10), AArch64 includes this file from 32-bit + # Arm. + if self._arch.name == "aarch64": + files.append("arch/arm/include/asm/opcodes.h") + # Before Linux kernel commit efe0160cfd40 ("powerpc/64: Linker + # on-demand sfpr functions for modules") (in v4.13), this must be + # available to link into modules. + if self._arch.name == "ppc64": + files.append("arch/powerpc/lib/crtsavres.o") directories = ( - f"arch/{self._srcarch}/include", + f"arch/{self._arch.kernel_srcarch}/include", "include", ) @@ -370,7 +371,7 @@ def _copy_module_build(self, modules_dir: Path) -> None: dirs_exist_ok=True, ) - async def _test_external_module_build(self, modules_dir: Path) -> None: + async def _test_external_module_build(self, modules_build_dir: Path) -> None: logger.info("testing external module build") with tempfile.TemporaryDirectory( @@ -398,12 +399,16 @@ async def _test_external_module_build(self, modules_dir: Path) -> None: # missing some files. cmd = ( "make", + "ARCH=" + str(self._arch.kernel_arch), "-C", - modules_dir / "build", + modules_build_dir, f"M={test_module_dir.resolve()}", ) proc = await asyncio.create_subprocess_exec( - *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + *cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + env=self._env, ) try: stdout_task = asyncio.create_task(proc.stdout.readline()) @@ -453,23 +458,24 @@ async def package(self, format: str, output_dir: Path) -> Path: kernel_release = await self._kernel_release() extension = "" if format == "directory" else ("." + format) - package = output_dir / f"kernel-{kernel_release}.{self._arch}{extension}" + package = output_dir / f"kernel-{kernel_release}.{self._arch.name}{extension}" logger.info( - "packaging kernel %s from %s to %s", + "packaging kernel %s for %s from %s to %s", kernel_release, + self._arch.name, self._build_dir, package, ) image_name = ( - (await check_output("make", *make_args, "-s", "image_name")) + (await check_output("make", *make_args, "-s", "image_name", env=self._env)) .decode() .strip() ) with tempfile.TemporaryDirectory( - prefix="install.", dir=self._build_dir + prefix=package.name + ".tmp.", dir=package.parent ) as tmp_name: install_dir = Path(tmp_name) modules_dir = install_dir / "lib" / "modules" / kernel_release @@ -482,25 +488,54 @@ async def package(self, format: str, output_dir: Path) -> Path: "modules_install", stdout=self._build_stdout, stderr=self._build_stderr, + env=self._env, ) + # `make modules_install` creates these as symlinks to the absolute + # path of the source directory. Delete them, make build a + # directory, and make source a symlink to build. + modules_build_dir = modules_dir / "build" + modules_source_dir = modules_dir / "source" + modules_build_dir.unlink() + modules_build_dir.mkdir() + modules_source_dir.unlink() + modules_source_dir.symlink_to("build") + logger.info("copying vmlinux") - vmlinux = modules_dir / "vmlinux" + vmlinux = modules_build_dir / "vmlinux" await check_call( - "objcopy", + (os.environ if self._env is None else self._env).get( + "CROSS_COMPILE", "" + ) + + "objcopy", "--remove-relocations=*", self._build_dir / "vmlinux", str(vmlinux), + env=self._env, ) vmlinux.chmod(0o644) logger.info("copying vmlinuz") vmlinuz = modules_dir / "vmlinuz" - shutil.copy(self._build_dir / image_name, vmlinuz) + try: + shutil.copy(self._build_dir / image_name, vmlinuz) + except FileNotFoundError: + # Before Linux kernel commits 06995804b576 ("arm64: Use full + # path in KBUILD_IMAGE definition") and 152e6744ebfc ("arm: Use + # full path in KBUILD_IMAGE definition") (in v4.12), image_name + # may be relative to the architecture boot directory. + shutil.copy( + self._build_dir + / "arch" + / self._arch.kernel_srcarch + / "boot" + / image_name, + vmlinuz, + ) vmlinuz.chmod(0o644) - self._copy_module_build(modules_dir) - await self._test_external_module_build(modules_dir) + self._copy_module_build(modules_build_dir) + await self._test_external_module_build(modules_build_dir) package.parent.mkdir(parents=True, exist_ok=True) if format == "directory": @@ -528,7 +563,11 @@ async def package(self, format: str, output_dir: Path) -> Path: raise CalledProcessError(zstd_returncode, zstd_cmd) logger.info( - "packaged kernel %s from %s to %s", kernel_release, self._build_dir, package + "packaged kernel %s for %s from %s to %s", + kernel_release, + self._arch.name, + self._build_dir, + package, ) return package @@ -572,15 +611,40 @@ async def main() -> None: help='package archive format, or "directory" for an unarchived directory', default=_PACKAGE_FORMATS[0], ) + parser.add_argument( + "--patch", + action="store_true", + help="apply patches to kernel source tree", + ) + parser.add_argument( + "-a", + "--architecture", + choices=sorted(ARCHITECTURES), + help="architecture to build for", + default=None if HOST_ARCHITECTURE is None else HOST_ARCHITECTURE.name, + required=HOST_ARCHITECTURE is None, + ) parser.add_argument( "-f", "--flavor", - choices=[flavor.name for flavor in KERNEL_FLAVORS], + choices=KERNEL_FLAVORS, help="kernel configuration flavor. " + ". ".join( - [f"{flavor.name}: {flavor.description}" for flavor in KERNEL_FLAVORS] + [ + f"{flavor.name}: {flavor.description}" + for flavor in KERNEL_FLAVORS.values() + ] ), - default=KERNEL_FLAVORS[0].name, + default=next(iter(KERNEL_FLAVORS)), + ) + default_download_compiler_directory = Path("build/vmtest") + parser.add_argument( + "--download-compiler", + metavar="DIR", + nargs="?", + default=argparse.SUPPRESS, + type=Path, + help=f"download a compiler from {COMPILER_URL} to the given directory ({default_download_compiler_directory} by default) and use it to build", ) parser.add_argument( "--dump-kconfig", @@ -589,13 +653,26 @@ async def main() -> None: ) args = parser.parse_args() - flavor = [flavor for flavor in KERNEL_FLAVORS if flavor.name == args.flavor][0] + arch = ARCHITECTURES[args.architecture] + flavor = KERNEL_FLAVORS[args.flavor] if args.dump_kconfig: - sys.stdout.write(kconfig(flavor)) + sys.stdout.write(kconfig(arch, flavor)) return - kbuild = KBuild(args.kernel_directory, args.build_directory, flavor, "x86_64") + if hasattr(args, "download_compiler"): + if args.download_compiler is None: + args.download_compiler = default_download_compiler_directory + downloaded = next(download(args.download_compiler, [DownloadCompiler(arch)])) + assert isinstance(downloaded, Compiler) + env = {**os.environ, **downloaded.env()} + else: + env = None + + if args.patch: + await apply_patches(args.kernel_directory) + + kbuild = KBuild(args.kernel_directory, args.build_directory, arch, flavor, env) await kbuild.build() if hasattr(args, "package"): await kbuild.package(args.package_format, args.package) diff --git a/vmtest/kmod.py b/vmtest/kmod.py new file mode 100644 index 000000000..9e686917d --- /dev/null +++ b/vmtest/kmod.py @@ -0,0 +1,125 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +import logging +import os +from pathlib import Path +import shutil +import subprocess +import tempfile + +from util import nproc, out_of_date +from vmtest.config import Kernel, local_kernel +from vmtest.download import downloaded_compiler + +logger = logging.getLogger(__name__) + + +def build_kmod(download_dir: Path, kernel: Kernel) -> Path: + kmod = kernel.path.parent / f"drgn_test-{kernel.release}.ko" + # External modules can't do out-of-tree builds for some reason, so copy the + # source files to a temporary directory and build the module there, then + # move it to the final location. + kmod_source_dir = Path("tests/linux_kernel/kmod") + source_files = ("drgn_test.c", "Makefile") + if out_of_date(kmod, *[kmod_source_dir / filename for filename in source_files]): + logger.info("building %s", kmod) + + compiler = downloaded_compiler(download_dir, kernel.arch) + kernel_build_dir = kernel.path / "build" + + with tempfile.TemporaryDirectory(dir=kmod.parent) as tmp_name: + tmp_dir = Path(tmp_name) + # Make sure that header files have the same paths as in the + # original kernel build. + debug_prefix_map = [ + f"{kernel_build_dir.resolve()}=.", + f"{tmp_dir.resolve()}=./drgn_test", + ] + cflags = " ".join(["-fdebug-prefix-map=" + map for map in debug_prefix_map]) + for filename in source_files: + shutil.copy(kmod_source_dir / filename, tmp_dir / filename) + subprocess.check_call( + [ + "make", + "ARCH=" + kernel.arch.kernel_arch, + "-C", + kernel_build_dir, + f"M={tmp_dir.resolve()}", + "KAFLAGS=" + cflags, + "KCFLAGS=" + cflags, + "-j", + str(nproc()), + ], + env={**os.environ, **compiler.env()}, + ) + (tmp_dir / "drgn_test.ko").rename(kmod) + else: + logger.info("%s is up to date", kmod) + return kmod + + +def _main() -> None: + import argparse + + from vmtest.download import ( + DOWNLOAD_KERNEL_ARGPARSE_METAVAR, + DownloadCompiler, + download_in_thread, + download_kernel_argparse_type, + ) + + logging.basicConfig( + format="%(asctime)s:%(levelname)s:%(name)s:%(message)s", level=logging.INFO + ) + + parser = argparse.ArgumentParser( + description="build drgn test kernel module", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "-k", + "--kernel", + metavar=DOWNLOAD_KERNEL_ARGPARSE_METAVAR, + dest="kernels", + action="append", + type=download_kernel_argparse_type, + default=argparse.SUPPRESS, + help="kernel to build for; may be given multiple times", + ) + parser.add_argument( + "-d", + "--directory", + metavar="DIR", + type=Path, + default="build/vmtest", + help="directory to download assets to", + ) + args = parser.parse_args() + if not hasattr(args, "kernels"): + args.kernels = [] + + compilers_needed = { + kernel.arch.name: DownloadCompiler(kernel.arch) for kernel in args.kernels + } + + to_download = list(compilers_needed.values()) + for kernel in args.kernels: + if not kernel.pattern.startswith(".") and not kernel.pattern.startswith("/"): + to_download.append(kernel) + with download_in_thread(args.directory, to_download) as downloads: + download_it = iter(downloads) + + for i in range(len(compilers_needed)): + next(download_it) + + for kernel in args.kernels: + if kernel.pattern.startswith(".") or kernel.pattern.startswith("/"): + downloaded = local_kernel(kernel.arch, Path(kernel.pattern)) + else: + downloaded = next(download_it) # type: ignore[assignment] + print(build_kmod(args.directory, downloaded)) + + +if __name__ == "__main__": + _main() diff --git a/vmtest/manage.py b/vmtest/manage.py index 196f6f22d..941b27109 100644 --- a/vmtest/manage.py +++ b/vmtest/manage.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import argparse import asyncio @@ -13,11 +13,13 @@ AsyncIterator, Dict, List, + Mapping, NamedTuple, Optional, Sequence, Tuple, Union, + cast, ) import aiohttp @@ -25,17 +27,29 @@ from util import KernelVersion from vmtest.asynciosubprocess import check_call, check_output -from vmtest.download import VMTEST_GITHUB_RELEASE, available_kernel_releases +from vmtest.config import ( + ARCHITECTURES, + KERNEL_FLAVORS, + Architecture, + Compiler, + KernelFlavor, + kconfig_localversion, +) +from vmtest.download import ( + VMTEST_GITHUB_RELEASE, + DownloadCompiler, + available_kernel_releases, + download, +) from vmtest.githubapi import AioGitHubApi -from vmtest.kbuild import KERNEL_FLAVORS, KBuild, KernelFlavor +from vmtest.kbuild import KBuild, apply_patches logger = logging.getLogger(__name__) # [inclusive, exclusive) ranges of kernel versions to ignore when building # latest releases of each version. IGNORE_KERNEL_RANGES = ( - (KernelVersion("~"), KernelVersion("4.4")), - (KernelVersion("4.5~"), KernelVersion("4.9")), + (KernelVersion("~"), KernelVersion("4.9")), (KernelVersion("4.10~"), KernelVersion("4.14")), (KernelVersion("4.15~"), KernelVersion("4.19")), (KernelVersion("4.20~"), KernelVersion("5.4")), @@ -83,7 +97,7 @@ def kernel_tag_to_release(tag: str, flavor: KernelFlavor) -> str: match.group(1), match.group(2) or ".0", match.group(3) or "", - flavor.localversion(), + kconfig_localversion(flavor), ] ) @@ -101,7 +115,7 @@ async def fetch_kernel_tags(kernel_dir: Path, kernel_tags: Sequence[str]) -> Non else: mainline_tags.append(tag) - for (name, url, tags) in ( + for name, url, tags in ( ("mainline", LINUX_GIT_URL, mainline_tags), ("stable", STABLE_LINUX_GIT_URL, stable_tags), ): @@ -122,29 +136,44 @@ async def fetch_kernel_tags(kernel_dir: Path, kernel_tags: Sequence[str]) -> Non async def build_kernels( kernel_dir: Path, build_dir: Path, - arch: str, - kernel_revs: Sequence[Tuple[str, Sequence[KernelFlavor]]], + kernel_revs: Sequence[ + Tuple[str, Sequence[Tuple[Architecture, Sequence[KernelFlavor]]]] + ], + compilers: Mapping[str, Compiler], keep_builds: bool, ) -> AsyncIterator[Path]: build_dir.mkdir(parents=True, exist_ok=True) - for rev, flavors in kernel_revs: + for rev, arches in kernel_revs: logger.info("checking out %s in %s", rev, kernel_dir) - await check_call("git", "-C", str(kernel_dir), "checkout", "-q", rev) - for flavor in flavors: - flavor_rev_build_dir = build_dir / f"build-{flavor.name}-{rev}" - with open( - build_dir / f"build-{flavor.name}-{rev}.log", "w" - ) as build_log_file: - kbuild = KBuild( - kernel_dir, flavor_rev_build_dir, flavor, arch, build_log_file + await check_call( + "git", "-C", str(kernel_dir), "checkout", "--force", "--quiet", rev + ) + await check_call("git", "-C", str(kernel_dir), "clean", "-dqf") + await apply_patches(kernel_dir) + for arch, flavors in arches: + env = {**os.environ, **compilers[arch.name].env()} + for flavor in flavors: + flavor_rev_build_dir = ( + build_dir / f"build-{arch.name}-{flavor.name}-{rev}" ) - await kbuild.build() - yield await kbuild.package("tar.zst", build_dir) - if not keep_builds: - logger.info("deleting %s", flavor_rev_build_dir) - # Shell out instead of using, e.g., shutil.rmtree(), to - # avoid blocking the main thread and the GIL. - await check_call("rm", "-rf", str(flavor_rev_build_dir)) + with open( + build_dir / f"build-{arch.name}-{flavor.name}-{rev}.log", "w" + ) as build_log_file: + kbuild = KBuild( + kernel_dir, + flavor_rev_build_dir, + arch, + flavor, + env=env, + build_log_file=build_log_file, + ) + await kbuild.build() + yield await kbuild.package("tar.zst", build_dir) + if not keep_builds: + logger.info("deleting %s", flavor_rev_build_dir) + # Shell out instead of using, e.g., shutil.rmtree(), to + # avoid blocking the main thread and the GIL. + await check_call("rm", "-rf", str(flavor_rev_build_dir)) class AssetUploadWork(NamedTuple): @@ -196,6 +225,24 @@ async def main() -> None: action="store_true", help="build and upload latest supported kernel releases", ) + parser.add_argument( + "-a", + "--architecture", + dest="architectures", + choices=["all", *sorted(ARCHITECTURES)], + action="append", + help="build architecture; may be given multiple times (default: all)", + default=argparse.SUPPRESS, + ) + parser.add_argument( + "-f", + "--flavor", + dest="flavors", + choices=["all", *KERNEL_FLAVORS], + action="append", + help="build flavor; may be given multiple times (default: all)", + default=argparse.SUPPRESS, + ) parser.add_argument( "--no-build", dest="build", @@ -236,30 +283,42 @@ async def main() -> None: default=argparse.SUPPRESS, ) parser.add_argument( - "--cache-directory", + "--download-directory", metavar="DIR", type=Path, default="build/vmtest", - help="directory to cache API calls in", + help="directory to download assets to", ) args = parser.parse_args() + if not hasattr(args, "architectures") or "all" in args.architectures: + args.architectures = list(ARCHITECTURES.values()) + else: + args.architectures = [ + arch for arch in ARCHITECTURES.values() if arch.name in args.architectures + ] + if not hasattr(args, "flavors") or "all" in args.flavors: + args.flavors = list(KERNEL_FLAVORS.values()) + else: + args.flavors = [ + flavor for flavor in KERNEL_FLAVORS.values() if flavor.name in args.flavors + ] + if not args.build: args.upload = False if not hasattr(args, "keep_builds"): args.keep_builds = not args.upload - arch = "x86_64" - async with aiohttp.ClientSession(trust_env=True) as session: GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") if GITHUB_TOKEN is None and args.upload: sys.exit("GITHUB_TOKEN environment variable is not set") gh = AioGitHubApi(session, GITHUB_TOKEN) - args.cache_directory.mkdir(parents=True, exist_ok=True) + args.download_directory.mkdir(parents=True, exist_ok=True) github_release_coro = gh.get_release_by_tag( - *VMTEST_GITHUB_RELEASE, cache=args.cache_directory / "github_release.json" + *VMTEST_GITHUB_RELEASE, + cache=args.download_directory / "github_release.json", ) if args.latest_kernels: github_release, latest_kernel_tags = await asyncio.gather( @@ -268,34 +327,63 @@ async def main() -> None: else: github_release = await github_release_coro - kernel_releases = available_kernel_releases(github_release, arch) - logger.info( - "available %s kernel releases: %s", - arch, - ", ".join(sorted(kernel_releases, key=KernelVersion, reverse=True)), - ) + kernel_releases = available_kernel_releases(github_release) + logger.info("available kernel releases:") + for arch in args.architectures: + arch_kernel_releases = kernel_releases.get(arch.name, {}) + logger.info( + " %s: %s", + arch.name, + ", ".join( + sorted(arch_kernel_releases, key=KernelVersion, reverse=True) + ), + ) to_build = [] if args.latest_kernels: logger.info("latest kernel versions: %s", ", ".join(latest_kernel_tags)) for tag in latest_kernel_tags: - flavors = [ - flavor - for flavor in KERNEL_FLAVORS - if kernel_tag_to_release(tag, flavor) not in kernel_releases - ] - if flavors: - to_build.append((tag, flavors)) + tag_arches_to_build = [] + for arch in args.architectures: + arch_kernel_releases = kernel_releases.get(arch.name, {}) + tag_arch_flavors_to_build = [ + flavor + for flavor in args.flavors + if kernel_tag_to_release(tag, flavor) + not in arch_kernel_releases + ] + if tag_arch_flavors_to_build: + tag_arches_to_build.append((arch, tag_arch_flavors_to_build)) + if tag_arches_to_build: + to_build.append((tag, tag_arches_to_build)) if to_build: - logger.info( - "kernel versions to build: %s", - ", ".join( - f"{tag} ({', '.join([flavor.name for flavor in flavors])})" - for tag, flavors in to_build - ), - ) + logger.info("kernel versions to build:") + for tag, tag_arches_to_build in to_build: + logger.info( + " %s (%s)", + tag, + ", ".join( + [ + f"{arch.name} [{', '.join([flavor.name for flavor in tag_arch_flavors_to_build])}]" + for arch, tag_arch_flavors_to_build in tag_arches_to_build + ] + ), + ) + if args.build: + compilers = { + cast(Compiler, downloaded).target.name: cast(Compiler, downloaded) + for downloaded in download( + args.download_directory, + { + arch.name: DownloadCompiler(arch) + for _, tag_arches_to_build in to_build + for arch, _ in tag_arches_to_build + }.values(), + ) + } + if args.upload: upload_queue: "asyncio.Queue[Optional[AssetUploadWork]]" = ( asyncio.Queue() @@ -309,8 +397,8 @@ async def main() -> None: async for kernel_package in build_kernels( args.kernel_directory, args.build_directory, - arch, to_build, + compilers, args.keep_builds, ): if args.upload: diff --git a/vmtest/onoatimehack.c b/vmtest/onoatimehack.c index 1eb54e363..2cbf3ef36 100644 --- a/vmtest/onoatimehack.c +++ b/vmtest/onoatimehack.c @@ -1,5 +1,5 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: LGPL-2.1-or-later /* * QEMU's 9pfs server passes through O_NOATIME from the client. If the server diff --git a/vmtest/patches/5.10-bpf-Generate-BTF_KIND_FLOAT-when-linking-vmlinux.patch b/vmtest/patches/5.10-bpf-Generate-BTF_KIND_FLOAT-when-linking-vmlinux.patch new file mode 100644 index 000000000..315d10224 --- /dev/null +++ b/vmtest/patches/5.10-bpf-Generate-BTF_KIND_FLOAT-when-linking-vmlinux.patch @@ -0,0 +1,53 @@ +From bbaea0f1cd33d702d053d5bdaf6d6dec3932894c Mon Sep 17 00:00:00 2001 +Message-Id: +From: Ilya Leoshkevich +Date: Wed, 19 Oct 2022 10:56:00 +0200 +Subject: [PATCH] bpf: Generate BTF_KIND_FLOAT when linking vmlinux + +commit db16c1fe92d7ba7d39061faef897842baee2c887 upstream. + +[backported for dependency only extra_paholeopt variable setup and +usage, we don't want floats generated in 5.10] + +pahole v1.21 supports the --btf_gen_floats flag, which makes it +generate the information about the floating-point types [1]. + +Adjust link-vmlinux.sh to pass this flag to pahole in case it's +supported, which is determined using a simple version check. + +[1] https://lore.kernel.org/dwarves/YHRiXNX1JUF2Az0A@kernel.org/ + +Signed-off-by: Ilya Leoshkevich +Signed-off-by: Andrii Nakryiko +Acked-by: Andrii Nakryiko +Link: https://lore.kernel.org/bpf/20210413190043.21918-1-iii@linux.ibm.com +Signed-off-by: Jiri Olsa +Signed-off-by: Greg Kroah-Hartman +--- + scripts/link-vmlinux.sh | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh +index d0b44bee9286..cdfccbfed452 100755 +--- a/scripts/link-vmlinux.sh ++++ b/scripts/link-vmlinux.sh +@@ -146,6 +146,7 @@ vmlinux_link() + gen_btf() + { + local pahole_ver ++ local extra_paholeopt= + + if ! [ -x "$(command -v ${PAHOLE})" ]; then + echo >&2 "BTF: ${1}: pahole (${PAHOLE}) is not available" +@@ -161,7 +162,7 @@ gen_btf() + vmlinux_link ${1} + + info "BTF" ${2} +- LLVM_OBJCOPY=${OBJCOPY} ${PAHOLE} -J ${1} ++ LLVM_OBJCOPY=${OBJCOPY} ${PAHOLE} -J ${extra_paholeopt} ${1} + + # Create ${2} which contains just .BTF section but no symbols. Add + # SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all +-- +2.30.2 + diff --git a/vmtest/patches/5.10-kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch b/vmtest/patches/5.10-kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch new file mode 100644 index 000000000..061e927ff --- /dev/null +++ b/vmtest/patches/5.10-kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch @@ -0,0 +1,46 @@ +From ecad3312111798d84dac1ce6a853e0ac9de8d505 Mon Sep 17 00:00:00 2001 +Message-Id: +From: Martin Rodriguez Reboredo +Date: Wed, 19 Oct 2022 10:56:04 +0200 +Subject: [PATCH] kbuild: Add skip_encoding_btf_enum64 option to pahole + +New pahole (version 1.24) generates by default new BTF_KIND_ENUM64 BTF tag, +which is not supported by stable kernel. + +As a result the kernel with CONFIG_DEBUG_INFO_BTF option will fail to +compile with following error: + + BTFIDS vmlinux +FAILED: load BTF from vmlinux: Invalid argument + +New pahole provides --skip_encoding_btf_enum64 option to skip BTF_KIND_ENUM64 +generation and produce BTF supported by stable kernel. + +Adding this option to scripts/pahole-flags.sh. + +This change does not have equivalent commit in linus tree, because linus tree +has support for BTF_KIND_ENUM64 tag, so it does not need to be disabled. + +Signed-off-by: Martin Rodriguez Reboredo +Signed-off-by: Jiri Olsa +Signed-off-by: Greg Kroah-Hartman +--- + scripts/pahole-flags.sh | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/scripts/pahole-flags.sh b/scripts/pahole-flags.sh +index 27445cb72974..8c82173e42e5 100755 +--- a/scripts/pahole-flags.sh ++++ b/scripts/pahole-flags.sh +@@ -14,4 +14,8 @@ if [ "${pahole_ver}" -ge "118" ] && [ "${pahole_ver}" -le "121" ]; then + extra_paholeopt="${extra_paholeopt} --skip_encoding_btf_vars" + fi + ++if [ "${pahole_ver}" -ge "124" ]; then ++ extra_paholeopt="${extra_paholeopt} --skip_encoding_btf_enum64" ++fi ++ + echo ${extra_paholeopt} +-- +2.30.2 + diff --git a/vmtest/patches/5.10-kbuild-skip-per-CPU-BTF-generation-for-pahole-v1.18-.patch b/vmtest/patches/5.10-kbuild-skip-per-CPU-BTF-generation-for-pahole-v1.18-.patch new file mode 100644 index 000000000..dc9282e4e --- /dev/null +++ b/vmtest/patches/5.10-kbuild-skip-per-CPU-BTF-generation-for-pahole-v1.18-.patch @@ -0,0 +1,57 @@ +From f5f413cb3e8af235c5d310bea9942424fb242c2c Mon Sep 17 00:00:00 2001 +Message-Id: +From: Andrii Nakryiko +Date: Wed, 19 Oct 2022 10:56:02 +0200 +Subject: [PATCH] kbuild: skip per-CPU BTF generation for pahole v1.18-v1.21 + +commit a0b8200d06ad6450c179407baa5f0f52f8cfcc97 upstream. + +[small context changes due to missing floats support in 5.10] + +Commit "mm/page_alloc: convert per-cpu list protection to local_lock" will +introduce a zero-sized per-CPU variable, which causes pahole to generate +invalid BTF. Only pahole versions 1.18 through 1.21 are impacted, as +before 1.18 pahole doesn't know anything about per-CPU variables, and 1.22 +contains the proper fix for the issue. + +Luckily, pahole 1.18 got --skip_encoding_btf_vars option disabling BTF +generation for per-CPU variables in anticipation of some unanticipated +problems. So use this escape hatch to disable per-CPU var BTF info on +those problematic pahole versions. Users relying on availability of +per-CPU var BTFs would need to upgrade to pahole 1.22+, but everyone won't +notice any regressions. + +Link: https://lkml.kernel.org/r/20210530002536.3193829-1-andrii@kernel.org +Signed-off-by: Andrii Nakryiko +Acked-by: Mel Gorman +Cc: Arnaldo Carvalho de Melo +Cc: Hao Luo +Cc: Michal Suchanek +Cc: Jiri Olsa +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +Signed-off-by: Jiri Olsa +Signed-off-by: Greg Kroah-Hartman +--- + scripts/link-vmlinux.sh | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh +index 72bf14df6903..bbb22be4c8f1 100755 +--- a/scripts/link-vmlinux.sh ++++ b/scripts/link-vmlinux.sh +@@ -161,6 +161,11 @@ gen_btf() + + vmlinux_link ${1} + ++ if [ "${pahole_ver}" -ge "118" ] && [ "${pahole_ver}" -le "121" ]; then ++ # pahole 1.18 through 1.21 can't handle zero-sized per-CPU vars ++ extra_paholeopt="${extra_paholeopt} --skip_encoding_btf_vars" ++ fi ++ + info "BTF" ${2} + LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${extra_paholeopt} ${1} + +-- +2.40.1 + diff --git a/vmtest/patches/5.11-bpf-Generate-BTF_KIND_FLOAT-when-linking-vmlinux.patch b/vmtest/patches/5.11-bpf-Generate-BTF_KIND_FLOAT-when-linking-vmlinux.patch new file mode 100644 index 000000000..e4b0fc3a8 --- /dev/null +++ b/vmtest/patches/5.11-bpf-Generate-BTF_KIND_FLOAT-when-linking-vmlinux.patch @@ -0,0 +1,49 @@ +From 796deb4725c9b501305fb87002f22bdc851d28ab Mon Sep 17 00:00:00 2001 +Message-Id: <796deb4725c9b501305fb87002f22bdc851d28ab.1685662812.git.osandov@osandov.com> +From: Ilya Leoshkevich +Date: Tue, 13 Apr 2021 21:00:43 +0200 +Subject: [PATCH] bpf: Generate BTF_KIND_FLOAT when linking vmlinux + +pahole v1.21 supports the --btf_gen_floats flag, which makes it +generate the information about the floating-point types [1]. + +Adjust link-vmlinux.sh to pass this flag to pahole in case it's +supported, which is determined using a simple version check. + +[1] https://lore.kernel.org/dwarves/YHRiXNX1JUF2Az0A@kernel.org/ + +Signed-off-by: Ilya Leoshkevich +Signed-off-by: Andrii Nakryiko +Acked-by: Andrii Nakryiko +Link: https://lore.kernel.org/bpf/20210413190043.21918-1-iii@linux.ibm.com +(cherry picked from commit db16c1fe92d7ba7d39061faef897842baee2c887) +[Omar: only define and use extra_paholeopt] +Signed-off-by: Omar Sandoval +--- + scripts/link-vmlinux.sh | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh +index 1ddb8961fc49..94c548b11db9 100755 +--- a/scripts/link-vmlinux.sh ++++ b/scripts/link-vmlinux.sh +@@ -140,6 +140,7 @@ vmlinux_link() + gen_btf() + { + local pahole_ver ++ local extra_paholeopt= + + if ! [ -x "$(command -v ${PAHOLE})" ]; then + echo >&2 "BTF: ${1}: pahole (${PAHOLE}) is not available" +@@ -155,7 +156,7 @@ gen_btf() + vmlinux_link ${1} + + info "BTF" ${2} +- LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${1} ++ LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${extra_paholeopt} ${1} + + # Create ${2} which contains just .BTF section but no symbols. Add + # SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all +-- +2.40.1 + diff --git a/vmtest/patches/5.11-kbuild-Unify-options-for-BTF-generation-for-vmlinux.patch b/vmtest/patches/5.11-kbuild-Unify-options-for-BTF-generation-for-vmlinux.patch new file mode 100644 index 000000000..6d27263e9 --- /dev/null +++ b/vmtest/patches/5.11-kbuild-Unify-options-for-BTF-generation-for-vmlinux.patch @@ -0,0 +1,119 @@ +From a1bc199b0aa9540632057d719b1c22879c1ec10f Mon Sep 17 00:00:00 2001 +Message-Id: +From: Jiri Olsa +Date: Fri, 29 Oct 2021 14:57:29 +0200 +Subject: [PATCH] kbuild: Unify options for BTF generation for vmlinux and + modules + +Using new PAHOLE_FLAGS variable to pass extra arguments to +pahole for both vmlinux and modules BTF data generation. + +Adding new scripts/pahole-flags.sh script that detect and +prints pahole options. + +[ fixed issues found by kernel test robot ] + +Signed-off-by: Jiri Olsa +Signed-off-by: Andrii Nakryiko +Acked-by: Andrii Nakryiko +Link: https://lore.kernel.org/bpf/20211029125729.70002-1-jolsa@kernel.org +(cherry picked from commit 9741e07ece7c247dd65e1aa01e16b683f01c05a8) +[Omar: fix up context for RESOLVE_BTFIDS and --btf_gen_floats, remove + --btf_gen_floats from pahole-flags.sh, squash in 'exit 0' change from + merge commit fc02cb2b37fe] +Signed-off-by: Omar Sandoval +--- + Makefile | 3 +++ + scripts/Makefile.modfinal | 2 +- + scripts/link-vmlinux.sh | 8 +------- + scripts/pahole-flags.sh | 17 +++++++++++++++++ + 4 files changed, 22 insertions(+), 8 deletions(-) + create mode 100755 scripts/pahole-flags.sh + +diff --git a/Makefile b/Makefile +index ff363cc6b11f..e728e4faa4bc 100644 +--- a/Makefile ++++ b/Makefile +@@ -464,6 +464,8 @@ LZ4 = lz4c + XZ = xz + ZSTD = zstd + ++PAHOLE_FLAGS = $(shell PAHOLE=$(PAHOLE) $(srctree)/scripts/pahole-flags.sh) ++ + CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ + -Wbitwise -Wno-return-void -Wno-unknown-attribute $(CF) + NOSTDINC_FLAGS := +@@ -517,6 +519,7 @@ export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE + export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE + export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE + export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL ++export PAHOLE_FLAGS + + # Files to ignore in find ... statements + +diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal +index aef583f824dc..4212c576e10c 100644 +--- a/scripts/Makefile.modfinal ++++ b/scripts/Makefile.modfinal +@@ -40,7 +40,7 @@ quiet_cmd_ld_ko_o = LD [M] $@ + quiet_cmd_btf_ko = BTF [M] $@ + cmd_btf_ko = \ + if [ -f vmlinux ]; then \ +- LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J --btf_base vmlinux $@; \ ++ LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) --btf_base vmlinux $@; \ + else \ + printf "Skipping BTF generation for %s due to unavailability of vmlinux\n" $@ 1>&2; \ + fi; +diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh +index 55bcfa85873c..cff7ef9a5773 100755 +--- a/scripts/link-vmlinux.sh ++++ b/scripts/link-vmlinux.sh +@@ -140,7 +140,6 @@ vmlinux_link() + gen_btf() + { + local pahole_ver +- local extra_paholeopt= + + if ! [ -x "$(command -v ${PAHOLE})" ]; then + echo >&2 "BTF: ${1}: pahole (${PAHOLE}) is not available" +@@ -155,13 +154,8 @@ gen_btf() + + vmlinux_link ${1} + +- if [ "${pahole_ver}" -ge "118" ] && [ "${pahole_ver}" -le "121" ]; then +- # pahole 1.18 through 1.21 can't handle zero-sized per-CPU vars +- extra_paholeopt="${extra_paholeopt} --skip_encoding_btf_vars" +- fi +- + info "BTF" ${2} +- LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${extra_paholeopt} ${1} ++ LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1} + + # Create ${2} which contains just .BTF section but no symbols. Add + # SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all +diff --git a/scripts/pahole-flags.sh b/scripts/pahole-flags.sh +new file mode 100755 +index 000000000000..27445cb72974 +--- /dev/null ++++ b/scripts/pahole-flags.sh +@@ -0,0 +1,17 @@ ++#!/bin/sh ++# SPDX-License-Identifier: GPL-2.0 ++ ++extra_paholeopt= ++ ++if ! [ -x "$(command -v ${PAHOLE})" ]; then ++ exit 0 ++fi ++ ++pahole_ver=$(${PAHOLE} --version | sed -E 's/v([0-9]+)\.([0-9]+)/\1\2/') ++ ++if [ "${pahole_ver}" -ge "118" ] && [ "${pahole_ver}" -le "121" ]; then ++ # pahole 1.18 through 1.21 can't handle zero-sized per-CPU vars ++ extra_paholeopt="${extra_paholeopt} --skip_encoding_btf_vars" ++fi ++ ++echo ${extra_paholeopt} +-- +2.40.1 + diff --git a/vmtest/patches/5.12-kbuild-Quote-OBJCOPY-var-to-avoid-a-pahole-call-brea.patch b/vmtest/patches/5.12-kbuild-Quote-OBJCOPY-var-to-avoid-a-pahole-call-brea.patch new file mode 100644 index 000000000..cc4e472fe --- /dev/null +++ b/vmtest/patches/5.12-kbuild-Quote-OBJCOPY-var-to-avoid-a-pahole-call-brea.patch @@ -0,0 +1,82 @@ +From 47b0e3728f47b9babe0beadf25f77c24063decf6 Mon Sep 17 00:00:00 2001 +Message-Id: <47b0e3728f47b9babe0beadf25f77c24063decf6.1685662304.git.osandov@osandov.com> +From: Javier Martinez Canillas +Date: Wed, 26 May 2021 23:52:28 +0200 +Subject: [PATCH] kbuild: Quote OBJCOPY var to avoid a pahole call break the + build + +[ Upstream commit ff2e6efda0d5c51b33e2bcc0b0b981ac0a0ef214 ] + +The ccache tool can be used to speed up cross-compilation, by calling the +compiler and binutils through ccache. For example, following should work: + + $ export ARCH=arm64 CROSS_COMPILE="ccache aarch64-linux-gnu-" + + $ make M=drivers/gpu/drm/rockchip/ + +but pahole fails to extract the BTF info from DWARF, breaking the build: + + CC [M] drivers/gpu/drm/rockchip//rockchipdrm.mod.o + LD [M] drivers/gpu/drm/rockchip//rockchipdrm.ko + BTF [M] drivers/gpu/drm/rockchip//rockchipdrm.ko + aarch64-linux-gnu-objcopy: invalid option -- 'J' + Usage: aarch64-linux-gnu-objcopy [option(s)] in-file [out-file] + Copies a binary file, possibly transforming it in the process + ... + make[1]: *** [scripts/Makefile.modpost:156: __modpost] Error 2 + make: *** [Makefile:1866: modules] Error 2 + +this fails because OBJCOPY is set to "ccache aarch64-linux-gnu-copy" and +later pahole is executed with the following command line: + + LLVM_OBJCOPY=$(OBJCOPY) $(PAHOLE) -J --btf_base vmlinux $@ + +which gets expanded to: + + LLVM_OBJCOPY=ccache aarch64-linux-gnu-objcopy pahole -J ... + +instead of: + + LLVM_OBJCOPY="ccache aarch64-linux-gnu-objcopy" pahole -J ... + +Fixes: 5f9ae91f7c0d ("kbuild: Build kernel module BTFs if BTF is enabled and pahole supports it") +Signed-off-by: Javier Martinez Canillas +Signed-off-by: Andrii Nakryiko +Acked-by: Andrii Nakryiko +Acked-by: Arnaldo Carvalho de Melo +Link: https://lore.kernel.org/bpf/20210526215228.3729875-1-javierm@redhat.com +Signed-off-by: Sasha Levin +--- + scripts/Makefile.modfinal | 2 +- + scripts/link-vmlinux.sh | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal +index 735e11e9041b..19468831fcc7 100644 +--- a/scripts/Makefile.modfinal ++++ b/scripts/Makefile.modfinal +@@ -59,7 +59,7 @@ quiet_cmd_ld_ko_o = LD [M] $@ + quiet_cmd_btf_ko = BTF [M] $@ + cmd_btf_ko = \ + if [ -f vmlinux ]; then \ +- LLVM_OBJCOPY=$(OBJCOPY) $(PAHOLE) -J --btf_base vmlinux $@; \ ++ LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J --btf_base vmlinux $@; \ + else \ + printf "Skipping BTF generation for %s due to unavailability of vmlinux\n" $@ 1>&2; \ + fi; +diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh +index 3b261b0f74f0..0a16928e495b 100755 +--- a/scripts/link-vmlinux.sh ++++ b/scripts/link-vmlinux.sh +@@ -228,7 +228,7 @@ gen_btf() + vmlinux_link ${1} + + info "BTF" ${2} +- LLVM_OBJCOPY=${OBJCOPY} ${PAHOLE} -J ${1} ++ LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${1} + + # Create ${2} which contains just .BTF section but no symbols. Add + # SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all +-- +2.40.1 + diff --git a/vmtest/patches/5.15-kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch b/vmtest/patches/5.15-kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch new file mode 100644 index 000000000..d21e7f35e --- /dev/null +++ b/vmtest/patches/5.15-kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch @@ -0,0 +1,48 @@ +From b775fbf532dc01ae53a6fc56168fd30cb4b0c658 Mon Sep 17 00:00:00 2001 +Message-Id: +From: Martin Rodriguez Reboredo +Date: Sun, 4 Sep 2022 15:19:01 +0200 +Subject: [PATCH] kbuild: Add skip_encoding_btf_enum64 option to pahole + +New pahole (version 1.24) generates by default new BTF_KIND_ENUM64 BTF tag, +which is not supported by stable kernel. + +As a result the kernel with CONFIG_DEBUG_INFO_BTF option will fail to +compile with following error: + + BTFIDS vmlinux +FAILED: load BTF from vmlinux: Invalid argument + +New pahole provides --skip_encoding_btf_enum64 option to skip BTF_KIND_ENUM64 +generation and produce BTF supported by stable kernel. + +Adding this option to scripts/pahole-flags.sh. + +This change does not have equivalent commit in linus tree, because linus tree +has support for BTF_KIND_ENUM64 tag, so it does not need to be disabled. + +Signed-off-by: Martin Rodriguez Reboredo +Signed-off-by: Jiri Olsa +Signed-off-by: Greg Kroah-Hartman +[Omar: fold in "kbuild: fix up permissions on scripts/pahole-flags.sh"] +Signed-off-by: Omar Sandoval +--- + scripts/pahole-flags.sh | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/scripts/pahole-flags.sh b/scripts/pahole-flags.sh +index e6093adf4c06..7acee326aa6c 100755 +--- a/scripts/pahole-flags.sh ++++ b/scripts/pahole-flags.sh +@@ -17,4 +17,8 @@ if [ "${pahole_ver}" -ge "121" ]; then + extra_paholeopt="${extra_paholeopt} --btf_gen_floats" + fi + ++if [ "${pahole_ver}" -ge "124" ]; then ++ extra_paholeopt="${extra_paholeopt} --skip_encoding_btf_enum64" ++fi ++ + echo ${extra_paholeopt} +-- +2.30.2 + diff --git a/vmtest/patches/5.15-kbuild-Unify-options-for-BTF-generation-for-vmlinux.patch b/vmtest/patches/5.15-kbuild-Unify-options-for-BTF-generation-for-vmlinux.patch new file mode 100644 index 000000000..f7069575b --- /dev/null +++ b/vmtest/patches/5.15-kbuild-Unify-options-for-BTF-generation-for-vmlinux.patch @@ -0,0 +1,125 @@ +From 0baced0e0938f2895ceba54038eaf15ed91032e7 Mon Sep 17 00:00:00 2001 +Message-Id: <0baced0e0938f2895ceba54038eaf15ed91032e7.1676496740.git.osandov@osandov.com> +From: Jiri Olsa +Date: Sun, 4 Sep 2022 15:19:00 +0200 +Subject: [PATCH] kbuild: Unify options for BTF generation for vmlinux and + modules + +commit e27f05147bff21408c1b8410ad8e90cd286e7952 upstream. + +Using new PAHOLE_FLAGS variable to pass extra arguments to +pahole for both vmlinux and modules BTF data generation. + +Adding new scripts/pahole-flags.sh script that detect and +prints pahole options. + +[ fixed issues found by kernel test robot ] + +Signed-off-by: Jiri Olsa +Signed-off-by: Andrii Nakryiko +Acked-by: Andrii Nakryiko +Link: https://lore.kernel.org/bpf/20211029125729.70002-1-jolsa@kernel.org +Signed-off-by: Greg Kroah-Hartman +[Omar: fold in "kbuild: fix up permissions on scripts/pahole-flags.sh"] +Signed-off-by: Omar Sandoval +--- + Makefile | 3 +++ + scripts/Makefile.modfinal | 2 +- + scripts/link-vmlinux.sh | 11 +---------- + scripts/pahole-flags.sh | 20 ++++++++++++++++++++ + 4 files changed, 25 insertions(+), 11 deletions(-) + create mode 100755 scripts/pahole-flags.sh + +diff --git a/Makefile b/Makefile +index 9142dbf41f0d..21316686af35 100644 +--- a/Makefile ++++ b/Makefile +@@ -480,6 +480,8 @@ LZ4 = lz4c + XZ = xz + ZSTD = zstd + ++PAHOLE_FLAGS = $(shell PAHOLE=$(PAHOLE) $(srctree)/scripts/pahole-flags.sh) ++ + CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ + -Wbitwise -Wno-return-void -Wno-unknown-attribute $(CF) + NOSTDINC_FLAGS := +@@ -534,6 +536,7 @@ export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE + export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE + export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE + export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL ++export PAHOLE_FLAGS + + # Files to ignore in find ... statements + +diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal +index ff805777431c..ce9661d968a3 100644 +--- a/scripts/Makefile.modfinal ++++ b/scripts/Makefile.modfinal +@@ -40,7 +40,7 @@ quiet_cmd_ld_ko_o = LD [M] $@ + quiet_cmd_btf_ko = BTF [M] $@ + cmd_btf_ko = \ + if [ -f vmlinux ]; then \ +- LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J --btf_base vmlinux $@; \ ++ LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) --btf_base vmlinux $@; \ + else \ + printf "Skipping BTF generation for %s due to unavailability of vmlinux\n" $@ 1>&2; \ + fi; +diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh +index 3819a461465d..57ef6accbb40 100755 +--- a/scripts/link-vmlinux.sh ++++ b/scripts/link-vmlinux.sh +@@ -211,7 +211,6 @@ vmlinux_link() + gen_btf() + { + local pahole_ver +- local extra_paholeopt= + + if ! [ -x "$(command -v ${PAHOLE})" ]; then + echo >&2 "BTF: ${1}: pahole (${PAHOLE}) is not available" +@@ -226,16 +225,8 @@ gen_btf() + + vmlinux_link ${1} + +- if [ "${pahole_ver}" -ge "118" ] && [ "${pahole_ver}" -le "121" ]; then +- # pahole 1.18 through 1.21 can't handle zero-sized per-CPU vars +- extra_paholeopt="${extra_paholeopt} --skip_encoding_btf_vars" +- fi +- if [ "${pahole_ver}" -ge "121" ]; then +- extra_paholeopt="${extra_paholeopt} --btf_gen_floats" +- fi +- + info "BTF" ${2} +- LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${extra_paholeopt} ${1} ++ LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1} + + # Create ${2} which contains just .BTF section but no symbols. Add + # SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all +diff --git a/scripts/pahole-flags.sh b/scripts/pahole-flags.sh +new file mode 100755 +index 000000000000..e6093adf4c06 +--- /dev/null ++++ b/scripts/pahole-flags.sh +@@ -0,0 +1,20 @@ ++#!/bin/sh ++# SPDX-License-Identifier: GPL-2.0 ++ ++extra_paholeopt= ++ ++if ! [ -x "$(command -v ${PAHOLE})" ]; then ++ exit 0 ++fi ++ ++pahole_ver=$(${PAHOLE} --version | sed -E 's/v([0-9]+)\.([0-9]+)/\1\2/') ++ ++if [ "${pahole_ver}" -ge "118" ] && [ "${pahole_ver}" -le "121" ]; then ++ # pahole 1.18 through 1.21 can't handle zero-sized per-CPU vars ++ extra_paholeopt="${extra_paholeopt} --skip_encoding_btf_vars" ++fi ++if [ "${pahole_ver}" -ge "121" ]; then ++ extra_paholeopt="${extra_paholeopt} --btf_gen_floats" ++fi ++ ++echo ${extra_paholeopt} +-- +2.30.2 + diff --git a/vmtest/patches/kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch b/vmtest/patches/kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch new file mode 100644 index 000000000..c794d5b0e --- /dev/null +++ b/vmtest/patches/kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch @@ -0,0 +1,46 @@ +From 6cfc3d5b06fe82ec7b7109f5ddfb22745475d165 Mon Sep 17 00:00:00 2001 +Message-Id: <6cfc3d5b06fe82ec7b7109f5ddfb22745475d165.1676498105.git.osandov@osandov.com> +From: Martin Rodriguez Reboredo +Date: Fri, 16 Sep 2022 14:12:34 -0300 +Subject: [PATCH] kbuild: Add skip_encoding_btf_enum64 option to pahole + +New pahole (version 1.24) generates by default new BTF_KIND_ENUM64 BTF tag, +which is not supported by stable kernel. + +As a result the kernel with CONFIG_DEBUG_INFO_BTF option will fail to +compile with following error: + + BTFIDS vmlinux +FAILED: load BTF from vmlinux: Invalid argument + +New pahole provides --skip_encoding_btf_enum64 option to skip BTF_KIND_ENUM64 +generation and produce BTF supported by stable kernel. + +Adding this option to scripts/pahole-flags.sh. + +This change does not have equivalent commit in linus tree, because linus tree +has support for BTF_KIND_ENUM64 tag, so it does not need to be disabled. + +Signed-off-by: Martin Rodriguez Reboredo +Signed-off-by: Jiri Olsa +Signed-off-by: Greg Kroah-Hartman +--- + scripts/pahole-flags.sh | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/scripts/pahole-flags.sh b/scripts/pahole-flags.sh +index 0d99ef17e4a5..d4f3d63cb434 100755 +--- a/scripts/pahole-flags.sh ++++ b/scripts/pahole-flags.sh +@@ -20,4 +20,8 @@ if [ "${pahole_ver}" -ge "122" ]; then + extra_paholeopt="${extra_paholeopt} -j" + fi + ++if [ "${pahole_ver}" -ge "124" ]; then ++ extra_paholeopt="${extra_paholeopt} --skip_encoding_btf_enum64" ++fi ++ + echo ${extra_paholeopt} +-- +2.30.2 + diff --git a/vmtest/patches/libsubcmd-Fix-use-after-free-for-realloc-.-0.patch b/vmtest/patches/libsubcmd-Fix-use-after-free-for-realloc-.-0.patch new file mode 100644 index 000000000..76859e1a0 --- /dev/null +++ b/vmtest/patches/libsubcmd-Fix-use-after-free-for-realloc-.-0.patch @@ -0,0 +1,67 @@ +From 52a9dab6d892763b2a8334a568bd4e2c1a6fde66 Mon Sep 17 00:00:00 2001 +Message-Id: <52a9dab6d892763b2a8334a568bd4e2c1a6fde66.1685663905.git.osandov@osandov.com> +From: Kees Cook +Date: Sun, 13 Feb 2022 10:24:43 -0800 +Subject: [PATCH] libsubcmd: Fix use-after-free for realloc(..., 0) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +GCC 12 correctly reports a potential use-after-free condition in the +xrealloc helper. Fix the warning by avoiding an implicit "free(ptr)" +when size == 0: + +In file included from help.c:12: +In function 'xrealloc', + inlined from 'add_cmdname' at help.c:24:2: subcmd-util.h:56:23: error: pointer may be used after 'realloc' [-Werror=use-after-free] + 56 | ret = realloc(ptr, size); + | ^~~~~~~~~~~~~~~~~~ +subcmd-util.h:52:21: note: call to 'realloc' here + 52 | void *ret = realloc(ptr, size); + | ^~~~~~~~~~~~~~~~~~ +subcmd-util.h:58:31: error: pointer may be used after 'realloc' [-Werror=use-after-free] + 58 | ret = realloc(ptr, 1); + | ^~~~~~~~~~~~~~~ +subcmd-util.h:52:21: note: call to 'realloc' here + 52 | void *ret = realloc(ptr, size); + | ^~~~~~~~~~~~~~~~~~ + +Fixes: 2f4ce5ec1d447beb ("perf tools: Finalize subcmd independence") +Reported-by: Valdis Klētnieks +Signed-off-by: Kees Kook +Tested-by: Valdis Klētnieks +Tested-by: Justin M. Forbes +Acked-by: Josh Poimboeuf +Cc: linux-hardening@vger.kernel.org +Cc: Valdis Klētnieks +Link: http://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org +Signed-off-by: Arnaldo Carvalho de Melo +--- + tools/lib/subcmd/subcmd-util.h | 11 ++--------- + 1 file changed, 2 insertions(+), 9 deletions(-) + +diff --git a/tools/lib/subcmd/subcmd-util.h b/tools/lib/subcmd/subcmd-util.h +index 794a375dad36..b2aec04fce8f 100644 +--- a/tools/lib/subcmd/subcmd-util.h ++++ b/tools/lib/subcmd/subcmd-util.h +@@ -50,15 +50,8 @@ static NORETURN inline void die(const char *err, ...) + static inline void *xrealloc(void *ptr, size_t size) + { + void *ret = realloc(ptr, size); +- if (!ret && !size) +- ret = realloc(ptr, 1); +- if (!ret) { +- ret = realloc(ptr, size); +- if (!ret && !size) +- ret = realloc(ptr, 1); +- if (!ret) +- die("Out of memory, realloc failed"); +- } ++ if (!ret) ++ die("Out of memory, realloc failed"); + return ret; + } + +-- +2.40.1 + diff --git a/vmtest/patches/proc-kcore-allow-enabling-CONFIG_PROC_KCORE-on-ARM.patch b/vmtest/patches/proc-kcore-allow-enabling-CONFIG_PROC_KCORE-on-ARM.patch new file mode 100644 index 000000000..23a2be1b6 --- /dev/null +++ b/vmtest/patches/proc-kcore-allow-enabling-CONFIG_PROC_KCORE-on-ARM.patch @@ -0,0 +1,34 @@ +From 67abb14dc0f5d4c9991ef6377a7b1871336cbe43 Mon Sep 17 00:00:00 2001 +Message-Id: <67abb14dc0f5d4c9991ef6377a7b1871336cbe43.1676535977.git.osandov@osandov.com> +From: Omar Sandoval +Date: Wed, 15 Feb 2023 14:54:17 -0800 +Subject: [PATCH] proc/kcore: allow enabling CONFIG_PROC_KCORE on ARM + +CONFIG_PROC_KCORE cannot be enabled on ARM since ancient history (Linux +2.6.0). See commits 5a8f43aee2ce ("[PATCH] Make /proc/kcore +configurable") [1] and 72d717bfd9a9 ("[PATCH] Make modules work on ARM") +[2] from the Linux kernel history tree and the patch submission for the +latter [3]. However, the reasoning for it being disabled is probably no +longer relevant. + +1: https://git.kernel.org/pub/scm/linux/kernel/git/history/history.git/commit/?id=5a8f43aee2cee75d7607c257369ab9b864795cc4 +2: https://git.kernel.org/pub/scm/linux/kernel/git/history/history.git/commit/?id=72d717bfd9a9e1170be2cf989ed322f85dcfe68f +3: https://lwn.net/Articles/45316/ + +Signed-off-by: Omar Sandoval +--- + fs/proc/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/proc/Kconfig b/fs/proc/Kconfig +index 32b1116ae137c..12b9348b0143e 100644 +--- a/fs/proc/Kconfig ++++ b/fs/proc/Kconfig +@@ -32,3 +32,3 @@ config PROC_FS + config PROC_KCORE +- bool "/proc/kcore support" if !ARM ++ bool "/proc/kcore support" + depends on PROC_FS && MMU +-- +2.30.2 + diff --git a/vmtest/patches/s390-mm-make-memory_block_size_bytes-available-for-M.patch b/vmtest/patches/s390-mm-make-memory_block_size_bytes-available-for-M.patch new file mode 100644 index 000000000..01e522cd8 --- /dev/null +++ b/vmtest/patches/s390-mm-make-memory_block_size_bytes-available-for-M.patch @@ -0,0 +1,56 @@ +From 604ddad038bfa0ae6f447c2ff29fcd430cec8181 Mon Sep 17 00:00:00 2001 +Message-Id: <604ddad038bfa0ae6f447c2ff29fcd430cec8181.1676539827.git.osandov@osandov.com> +From: Heiko Carstens +Date: Mon, 13 Feb 2017 14:58:36 +0100 +Subject: [PATCH] s390/mm: make memory_block_size_bytes available for + !MEMORY_HOTPLUG + +Fix this compile error for !MEMORY_HOTPLUG && NUMA: +arch/s390/built-in.o: In function `emu_setup_size_adjust': +arch/s390/numa/mode_emu.c:477: undefined reference to `memory_block_size_bytes' + +Signed-off-by: Heiko Carstens +Signed-off-by: Martin Schwidefsky +--- + arch/s390/mm/init.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c +index ba0c8d18e10d4..ee5066718b212 100644 +--- a/arch/s390/mm/init.c ++++ b/arch/s390/mm/init.c +@@ -151,6 +151,15 @@ void __init free_initrd_mem(unsigned long start, unsigned long end) + } + #endif + ++unsigned long memory_block_size_bytes(void) ++{ ++ /* ++ * Make sure the memory block size is always greater ++ * or equal than the memory increment size. ++ */ ++ return max_t(unsigned long, MIN_MEMORY_BLOCK_SIZE, sclp.rzm); ++} ++ + #ifdef CONFIG_MEMORY_HOTPLUG + int arch_add_memory(int nid, u64 start, u64 size, bool for_device) + { +@@ -194,15 +203,6 @@ int arch_add_memory(int nid, u64 start, u64 size, bool for_device) + return rc; + } + +-unsigned long memory_block_size_bytes(void) +-{ +- /* +- * Make sure the memory block size is always greater +- * or equal than the memory increment size. +- */ +- return max_t(unsigned long, MIN_MEMORY_BLOCK_SIZE, sclp.rzm); +-} +- + #ifdef CONFIG_MEMORY_HOTREMOVE + int arch_remove_memory(u64 start, u64 size) + { +-- +2.30.2 + diff --git a/vmtest/rootfsbuild.py b/vmtest/rootfsbuild.py new file mode 100644 index 000000000..d149cf414 --- /dev/null +++ b/vmtest/rootfsbuild.py @@ -0,0 +1,197 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# SPDX-License-Identifier: LGPL-2.1-or-later + +import logging +from pathlib import Path +import subprocess +import sys +import tempfile +import typing + +if typing.TYPE_CHECKING: + if sys.version_info < (3, 8): + from typing_extensions import Literal + else: + from typing import Literal # novermin + +from vmtest.config import ARCHITECTURES, HOST_ARCHITECTURE, Architecture + +logger = logging.getLogger(__name__) + + +_ROOTFS_PACKAGES = [ + # drgn build dependencies. + "autoconf", + "automake", + "gcc", + "git", + "libdw-dev", + "libelf-dev", + "libkdumpfile-dev", + "libtool", + "make", + "pkgconf", + "python3", + "python3-dev", + "python3-pip", + "python3-setuptools", + # Test dependencies. + "iproute2", + "kexec-tools", + "kmod", + "python3-pyroute2", + "python3-pytest", + "zstd", +] + + +def build_rootfs( + arch: Architecture, + path: Path, + *, + btrfs: "Literal['never', 'always', 'auto']" = "auto", +) -> None: + if path.exists(): + logger.info("%s already exists", path) + return + + logger.info("creating debootstrap rootfs %s", path) + path.parent.mkdir(parents=True, exist_ok=True) + with tempfile.TemporaryDirectory(dir=path.parent) as tmp_name: + tmp_dir = Path(tmp_name) + snapshot = False + + if btrfs != "never": + try: + import btrfsutil + + btrfsutil.create_subvolume(tmp_dir / path.name) + snapshot = True + except (ImportError, OSError): + if btrfs == "always": + raise + + subprocess.check_call( + [ + "unshare", + "--map-root-user", + "--map-users=auto", + "--map-groups=auto", + "sh", + "-c", + r""" +set -e + +arch="$1" +target="$2" +packages="$3" + +# We're not really an LXC container, but this convinces debootstrap to skip +# some operations that it can't do in a user namespace. +export container=lxc +debootstrap --variant=minbase --foreign --include="$packages" --arch="$arch" stable "$target" +chroot "$target" /debootstrap/debootstrap --second-stage +chroot "$target" apt clean +""", + "sh", + arch.debian_arch, + tmp_dir / path.name, + ",".join(_ROOTFS_PACKAGES), + ] + ) + (tmp_dir / path.name).rename(path) + logger.info("created debootstrap rootfs %s", path) + + if snapshot: + snapshot_dir = path.parent / (path.name + ".pristine") + btrfsutil.create_snapshot(path, snapshot_dir, read_only=True) + logger.info("created snapshot %s", snapshot_dir) + + +def build_drgn_in_rootfs(rootfs: Path) -> None: + logger.info("building drgn using %s", rootfs) + subprocess.check_call( + [ + "unshare", + "--mount", + "--map-root-user", + "--map-users=auto", + "--map-groups=auto", + "sh", + "-c", + r""" +set -e + +mount --bind . "$1/mnt" +chroot "$1" sh -c 'cd /mnt && CONFIGURE_FLAGS=--enable-compiler-warnings=error python3 setup.py build_ext -i' +""", + "sh", + rootfs, + ] + ) + + +if __name__ == "__main__": + import argparse + + logging.basicConfig( + format="%(asctime)s:%(levelname)s:%(name)s:%(message)s", level=logging.INFO + ) + + parser = argparse.ArgumentParser( + description="build root filesystems for vmtest. " + "This requires debootstrap(8), qemu-user-static, and unprivileged user namespaces.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "-d", + "--directory", + metavar="DIR", + type=Path, + default="build/vmtest", + help="directory for vmtest artifacts", + ) + parser.add_argument( + "--build-drgn", + action="store_true", + help="also build drgn in the current directory using the built rootfs", + ) + parser.add_argument( + "--btrfs", + choices=["never", "always", "auto"], + default="auto", + help="make the rootfs a Btrfs subvolume and create a read-only snapshot", + ) + parser.add_argument( + "-a", + "--architecture", + dest="architectures", + action="append", + choices=["all", "foreign", *sorted(ARCHITECTURES)], + default=argparse.SUPPRESS, + help='architecture to build for, or "foreign" for all architectures other than the host architecture; may be given multiple times (default: foreign)', + ) + args = parser.parse_args() + + if not hasattr(args, "architectures"): + args.architectures = ["foreign"] + architectures = [] + for name in args.architectures: + if name == "foreign": + architectures.extend( + [ + arch + for arch in ARCHITECTURES.values() + if arch is not HOST_ARCHITECTURE + ] + ) + elif name == "all": + architectures.extend(ARCHITECTURES.values()) + else: + architectures.append(ARCHITECTURES[name]) + + for arch in architectures: + dir = args.directory / arch.name / "rootfs" + build_rootfs(arch, dir, btrfs=args.btrfs) + if args.build_drgn: + build_drgn_in_rootfs(dir) diff --git a/vmtest/vm.py b/vmtest/vm.py index 5fbd7addb..c5bc1778a 100644 --- a/vmtest/vm.py +++ b/vmtest/vm.py @@ -1,5 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. -# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: LGPL-2.1-or-later import os from pathlib import Path @@ -9,10 +9,16 @@ import subprocess import sys import tempfile +from typing import Optional from util import nproc, out_of_date - -_9PFS_MSIZE = 1024 * 1024 +from vmtest.config import HOST_ARCHITECTURE, Kernel, local_kernel +from vmtest.download import ( + DOWNLOAD_KERNEL_ARGPARSE_METAVAR, + DownloadKernel, + download, + download_kernel_argparse_type, +) # Script run as init in the virtual machine. _INIT_TEMPLATE = r"""#!/bin/sh @@ -27,10 +33,13 @@ set -eu export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" -export PYTHON={python} {kdump_needs_nosmp} -trap 'poweroff -f' EXIT +# On exit, power off. We don't use the poweroff command because very minimal +# installations don't have it (e.g., the debootstrap minbase variant). The +# magic SysRq returns immediately without waiting for the poweroff, so we sleep +# for a while and panic if it takes longer than that. +trap 'echo o > /proc/sysrq-trigger && sleep 60' exit umask 022 @@ -38,39 +47,45 @@ VPORT_NAME=com.osandov.vmtest.0 RELEASE=$(uname -r) -# Set up overlayfs on the temporary directory containing this script. -mnt=$(dirname "$0") -mount -t tmpfs tmpfs "$mnt" -mkdir "$mnt/upper" "$mnt/work" "$mnt/merged" - -mkdir "$mnt/upper/dev" "$mnt/upper/etc" "$mnt/upper/mnt" -mkdir -m 555 "$mnt/upper/proc" "$mnt/upper/sys" -mkdir -m 1777 "$mnt/upper/tmp" +# Set up overlayfs. +if [ ! -w /tmp ]; then + mount -t tmpfs tmpfs /tmp +fi +mkdir /tmp/upper /tmp/work /tmp/merged +mkdir /tmp/upper/dev /tmp/upper/etc /tmp/upper/mnt +mkdir -m 555 /tmp/upper/proc /tmp/upper/sys +mkdir -m 1777 /tmp/upper/tmp +if [ -e /tmp/host ]; then + mkdir /tmp/host_upper /tmp/host_work /tmp/upper/host +fi -mount -t overlay -o lowerdir=/,upperdir="$mnt/upper",workdir="$mnt/work" overlay "$mnt/merged" +mount -t overlay -o lowerdir=/,upperdir=/tmp/upper,workdir=/tmp/work overlay /tmp/merged +if [ -e /tmp/host ]; then + mount -t overlay -o lowerdir=/tmp/host,upperdir=/tmp/host_upper,workdir=/tmp/host_work overlay /tmp/merged/host +fi # Mount core filesystems. -mount -t devtmpfs -o nosuid,noexec dev "$mnt/merged/dev" -mkdir "$mnt/merged/dev/shm" -mount -t tmpfs -o nosuid,nodev tmpfs "$mnt/merged/dev/shm" -mount -t proc -o nosuid,nodev,noexec proc "$mnt/merged/proc" -mount -t sysfs -o nosuid,nodev,noexec sys "$mnt/merged/sys" +mount -t devtmpfs -o nosuid,noexec dev /tmp/merged/dev +mkdir /tmp/merged/dev/shm +mount -t tmpfs -o nosuid,nodev tmpfs /tmp/merged/dev/shm +mount -t proc -o nosuid,nodev,noexec proc /tmp/merged/proc +mount -t sysfs -o nosuid,nodev,noexec sys /tmp/merged/sys # cgroup2 was added in Linux v4.5. -mount -t cgroup2 -o nosuid,nodev,noexec cgroup2 "$mnt/merged/sys/fs/cgroup" || true +mount -t cgroup2 -o nosuid,nodev,noexec cgroup2 /tmp/merged/sys/fs/cgroup || true # Ideally we'd just be able to create an opaque directory for /tmp on the upper # layer. However, before Linux kernel commit 51f7e52dc943 ("ovl: share inode # for hard link") (in v4.8), overlayfs doesn't handle hard links correctly, # which breaks some tests. -mount -t tmpfs -o nosuid,nodev tmpfs "$mnt/merged/tmp" +mount -t tmpfs -o nosuid,nodev tmpfs /tmp/merged/tmp # Pivot into the new root. -pivot_root "$mnt/merged" "$mnt/merged/mnt" +pivot_root /tmp/merged /tmp/merged/mnt cd / umount -l /mnt # Load kernel modules. mkdir -p "/lib/modules/$RELEASE" -mount -t 9p -o trans=virtio,cache=loose,ro,msize={_9PFS_MSIZE} modules "/lib/modules/$RELEASE" +mount --bind {kernel_dir} "/lib/modules/$RELEASE" for module in configs rng_core virtio_rng; do modprobe "$module" done @@ -173,19 +188,24 @@ class LostVMError(Exception): pass -def run_in_vm(command: str, kernel_dir: Path, build_dir: Path) -> int: +def run_in_vm( + command: str, kernel: Kernel, root_dir: Optional[Path], build_dir: Path +) -> int: + if root_dir is None: + if kernel.arch is HOST_ARCHITECTURE: + root_dir = Path("/") + else: + root_dir = build_dir / kernel.arch.name / "rootfs" + + qemu_exe = "qemu-system-" + kernel.arch.name match = re.search( r"QEMU emulator version ([0-9]+(?:\.[0-9]+)*)", - subprocess.check_output( - ["qemu-system-x86_64", "-version"], universal_newlines=True - ), + subprocess.check_output([qemu_exe, "-version"], universal_newlines=True), ) if not match: raise Exception("could not determine QEMU version") qemu_version = tuple(int(x) for x in match.group(1).split(".")) - # multidevs was added in QEMU 4.2.0. - multidevs = ",multidevs=remap" if qemu_version >= (4, 2) else "" # QEMU's 9pfs O_NOATIME handling was fixed in 5.1.0. The fix was backported # to 5.0.1. env = os.environ.copy() @@ -193,14 +213,21 @@ def run_in_vm(command: str, kernel_dir: Path, build_dir: Path) -> int: onoatimehack_so = _build_onoatimehack(build_dir) env["LD_PRELOAD"] = f"{str(onoatimehack_so)}:{env.get('LD_PRELOAD', '')}" - if os.access("/dev/kvm", os.R_OK | os.W_OK): - kvm_args = ["-cpu", "host", "-enable-kvm"] - else: - print( - "warning: /dev/kvm cannot be accessed; falling back to emulation", - file=sys.stderr, - ) - kvm_args = [] + kvm_args = [] + if HOST_ARCHITECTURE is not None and kernel.arch.name == HOST_ARCHITECTURE.name: + if os.access("/dev/kvm", os.R_OK | os.W_OK): + kvm_args = ["-cpu", "host", "-enable-kvm"] + else: + print( + "warning: /dev/kvm cannot be accessed; falling back to emulation", + file=sys.stderr, + ) + + virtfs_options = "security_model=none,readonly=on" + # multidevs was added in QEMU 4.2.0. + if qemu_version >= (4, 2): + virtfs_options += ",multidevs=remap" + _9pfs_mount_options = f"trans=virtio,cache=loose,msize={1024 * 1024}" with tempfile.TemporaryDirectory(prefix="drgn-vmtest-") as temp_dir, socket.socket( socket.AF_UNIX @@ -210,47 +237,75 @@ def run_in_vm(command: str, kernel_dir: Path, build_dir: Path) -> int: server_sock.bind(str(socket_path)) server_sock.listen() - init = (temp_path / "init").resolve() - with open(init, "w") as init_file: + init_path = temp_path / "init" + + unshare_args = [] + if root_dir == Path("/"): + host_virtfs_args = [] + init = str(init_path.resolve()) + host_dir_prefix = "" + else: + # Try to detect if the rootfs was created without privileges (e.g., + # by vmtest.rootfsbuild) and remap the UIDs/GIDs if so. + if (root_dir / "bin" / "mount").stat().st_uid != 0: + unshare_args = [ + "unshare", + "--map-root-user", + "--map-users=auto", + "--map-groups=auto", + ] + host_virtfs_args = [ + "-virtfs", + f"local,path=/,mount_tag=host,{virtfs_options}", + ] + init = f'/bin/sh -- -c "/bin/mount -t tmpfs tmpfs /tmp && /bin/mkdir /tmp/host && /bin/mount -t 9p -o {_9pfs_mount_options},ro host /tmp/host && . /tmp/host{init_path.resolve()}"' + host_dir_prefix = "/host" + + with init_path.open("w") as init_file: init_file.write( _INIT_TEMPLATE.format( - _9PFS_MSIZE=_9PFS_MSIZE, - python=shlex.quote(sys.executable), - cwd=shlex.quote(os.getcwd()), + cwd=shlex.quote(host_dir_prefix + os.getcwd()), + kernel_dir=shlex.quote( + host_dir_prefix + str(kernel.path.resolve()) + ), command=shlex.quote(command), kdump_needs_nosmp="" if kvm_args else "export KDUMP_NEEDS_NOSMP=1", ) ) - os.chmod(init, 0o755) + init_path.chmod(0o755) + with subprocess.Popen( [ # fmt: off - "qemu-system-x86_64", *kvm_args, + *unshare_args, + + qemu_exe, *kvm_args, - "-smp", str(nproc()), "-m", "2G", + # Limit the number of cores to 8, otherwise we can reach an OOM troubles. + "-smp", str(min(nproc(), 8)), "-m", "2G", - "-nodefaults", "-display", "none", "-serial", "mon:stdio", + "-display", "none", "-serial", "mon:stdio", # This along with -append panic=-1 ensures that we exit on a # panic instead of hanging. "-no-reboot", "-virtfs", - f"local,id=root,path=/,mount_tag=/dev/root,security_model=none,readonly=on{multidevs}", + f"local,id=root,path={root_dir},mount_tag=/dev/root,{virtfs_options}", + *host_virtfs_args, - "-virtfs", - f"local,path={kernel_dir},mount_tag=modules,security_model=none,readonly=on", - - "-device", "virtio-rng-pci", + "-device", "virtio-rng", "-device", "virtio-serial", "-chardev", f"socket,id=vmtest,path={socket_path}", "-device", "virtserialport,chardev=vmtest,name=com.osandov.vmtest.0", - "-kernel", str(kernel_dir / "vmlinuz"), + *kernel.arch.qemu_options, + + "-kernel", str(kernel.path / "vmlinuz"), "-append", - f"rootfstype=9p rootflags=trans=virtio,cache=loose,msize={_9PFS_MSIZE} ro console=0,115200 panic=-1 crashkernel=256M init={init}", + f"rootfstype=9p rootflags={_9pfs_mount_options} ro console={kernel.arch.qemu_console},115200 panic=-1 crashkernel=256M init={init}", # fmt: on ], env=env, @@ -299,7 +354,7 @@ def run_in_vm(command: str, kernel_dir: Path, build_dir: Path) -> int: metavar="DIR", type=Path, default="build/vmtest", - help="directory for build artifacts and downloaded kernels", + help="directory for vmtest artifacts", ) parser.add_argument( "--lost-status", @@ -311,8 +366,20 @@ def run_in_vm(command: str, kernel_dir: Path, build_dir: Path) -> int: parser.add_argument( "-k", "--kernel", + metavar=DOWNLOAD_KERNEL_ARGPARSE_METAVAR, + type=download_kernel_argparse_type, + required=HOST_ARCHITECTURE is None, default=argparse.SUPPRESS, - help="kernel to use (default: latest available kernel)", + help="kernel to use" + + ("" if HOST_ARCHITECTURE is None else " (default: latest available kernel)"), + ) + parser.add_argument( + "-r", + "--root-directory", + metavar="DIR", + default=argparse.SUPPRESS, + type=Path, + help="directory to use as root directory in VM (default: / for the host architecture, $directory/$arch/rootfs otherwise)", ) parser.add_argument( "command", @@ -322,17 +389,19 @@ def run_in_vm(command: str, kernel_dir: Path, build_dir: Path) -> int: ) args = parser.parse_args() - kernel = getattr(args, "kernel", "*") - if kernel.startswith(".") or kernel.startswith("/"): - kernel_dir = Path(kernel) + if not hasattr(args, "kernel"): + assert HOST_ARCHITECTURE is not None + args.kernel = DownloadKernel(HOST_ARCHITECTURE, "*") + if args.kernel.pattern.startswith(".") or args.kernel.pattern.startswith("/"): + kernel = local_kernel(args.kernel.arch, Path(args.kernel.pattern)) else: - from vmtest.download import download_kernels - - kernel_dir = next(download_kernels(args.directory, "x86_64", (kernel,))) + kernel = next(download(args.directory, [args.kernel])) # type: ignore[assignment] + if not hasattr(args, "root_directory"): + args.root_directory = None try: command = " ".join(args.command) if args.command else "sh -i" - sys.exit(run_in_vm(command, kernel_dir, args.directory)) + sys.exit(run_in_vm(command, kernel, args.root_directory, args.directory)) except LostVMError as e: print("error:", e, file=sys.stderr) sys.exit(args.lost_status)