summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2021-03-15 05:42:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2021-03-15 05:42:58 +0000
commit1ad264274db5ff6927d3f4de2e356f6680c43b40 (patch)
tree66d6ed1ee34b681d59bba29caccccad3db79bbb5
parentInitial commit. (diff)
downloaddwarves-dfsg-1ad264274db5ff6927d3f4de2e356f6680c43b40.tar.xz
dwarves-dfsg-1ad264274db5ff6927d3f4de2e356f6680c43b40.zip
Adding upstream version 1.19.upstream/1.19
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--.gitignore2
-rw-r--r--.gitmodules3
-rw-r--r--CMakeLists.txt166
-rw-r--r--COPYING339
-rw-r--r--MANIFEST71
-rw-r--r--NEWS668
-rw-r--r--README27
-rw-r--r--README.DEBUG6
-rw-r--r--README.btf598
-rw-r--r--README.cross7
-rw-r--r--README.ctracer57
-rw-r--r--README.tarball1
-rw-r--r--btf_encoder.c721
-rw-r--r--btf_encoder.h19
-rw-r--r--btf_loader.c579
-rwxr-xr-xbtfdiff33
-rw-r--r--changes-v1.13209
-rw-r--r--changes-v1.16104
-rw-r--r--changes-v1.17540
-rw-r--r--changes-v1.18117
-rw-r--r--changes-v1.19134
-rw-r--r--cmake/modules/FindDWARF.cmake101
-rw-r--r--codiff.c859
-rw-r--r--config.h.cmake9
-rw-r--r--ctf.h159
-rw-r--r--ctf_encoder.c353
-rw-r--r--ctf_encoder.h14
-rw-r--r--ctf_loader.c771
-rwxr-xr-xctfdwdiff57
-rw-r--r--ctracer.c1129
-rw-r--r--dtagnames.c63
-rw-r--r--dutil.c207
-rw-r--r--dutil.h337
-rw-r--r--dwarf_loader.c2620
-rw-r--r--dwarves.c2415
-rw-r--r--dwarves.h1337
-rw-r--r--dwarves_emit.c346
-rw-r--r--dwarves_emit.h38
-rw-r--r--dwarves_fprintf.c1967
-rw-r--r--dwarves_reorganize.c848
-rw-r--r--dwarves_reorganize.h30
-rw-r--r--elf_symtab.c68
-rw-r--r--elf_symtab.h92
-rw-r--r--elfcreator.c293
-rw-r--r--elfcreator.h18
-rwxr-xr-xfullcircle43
-rw-r--r--gobuffer.c143
-rw-r--r--gobuffer.h46
-rw-r--r--hash.h70
-rw-r--r--lib/Makefile38
-rw-r--r--lib/bpf/.lgtm.yml14
-rw-r--r--lib/bpf/.travis.yml130
-rw-r--r--lib/bpf/BPF-CHECKPOINT-COMMIT1
-rw-r--r--lib/bpf/CHECKPOINT-COMMIT1
-rw-r--r--lib/bpf/LICENSE1
-rw-r--r--lib/bpf/LICENSE.BSD-2-Clause32
-rw-r--r--lib/bpf/LICENSE.LPGL-2.1503
-rw-r--r--lib/bpf/README.md145
-rw-r--r--lib/bpf/include/asm/barrier.h7
-rw-r--r--lib/bpf/include/linux/compiler.h70
-rw-r--r--lib/bpf/include/linux/err.h38
-rw-r--r--lib/bpf/include/linux/filter.h126
-rw-r--r--lib/bpf/include/linux/kernel.h44
-rw-r--r--lib/bpf/include/linux/list.h91
-rw-r--r--lib/bpf/include/linux/overflow.h90
-rw-r--r--lib/bpf/include/linux/ring_buffer.h18
-rw-r--r--lib/bpf/include/linux/types.h31
-rw-r--r--lib/bpf/include/uapi/linux/bpf.h5051
-rw-r--r--lib/bpf/include/uapi/linux/bpf_common.h57
-rw-r--r--lib/bpf/include/uapi/linux/btf.h172
-rw-r--r--lib/bpf/include/uapi/linux/if_link.h1047
-rw-r--r--lib/bpf/include/uapi/linux/if_xdp.h111
-rw-r--r--lib/bpf/include/uapi/linux/netlink.h252
-rwxr-xr-xlib/bpf/scripts/coverity.sh105
-rwxr-xr-xlib/bpf/scripts/sync-kernel.sh343
-rw-r--r--lib/bpf/src/.gitignore6
-rw-r--r--lib/bpf/src/Makefile156
-rw-r--r--lib/bpf/src/README.rst168
-rw-r--r--lib/bpf/src/bpf.c933
-rw-r--r--lib/bpf/src/bpf.h285
-rw-r--r--lib/bpf/src/bpf_core_read.h345
-rw-r--r--lib/bpf/src/bpf_endian.h99
-rw-r--r--lib/bpf/src/bpf_helper_defs.h3619
-rw-r--r--lib/bpf/src/bpf_helpers.h131
-rw-r--r--lib/bpf/src/bpf_prog_linfo.c246
-rw-r--r--lib/bpf/src/bpf_tracing.h432
-rw-r--r--lib/bpf/src/btf.c4611
-rw-r--r--lib/bpf/src/btf.h368
-rw-r--r--lib/bpf/src/btf_dump.c1440
-rw-r--r--lib/bpf/src/hashmap.c241
-rw-r--r--lib/bpf/src/hashmap.h195
-rw-r--r--lib/bpf/src/libbpf.c10862
-rw-r--r--lib/bpf/src/libbpf.h765
-rw-r--r--lib/bpf/src/libbpf.map348
-rw-r--r--lib/bpf/src/libbpf.pc.template12
-rw-r--r--lib/bpf/src/libbpf_common.h42
-rw-r--r--lib/bpf/src/libbpf_errno.c66
-rw-r--r--lib/bpf/src/libbpf_internal.h321
-rw-r--r--lib/bpf/src/libbpf_probes.c357
-rw-r--r--lib/bpf/src/libbpf_util.h47
-rw-r--r--lib/bpf/src/netlink.c372
-rw-r--r--lib/bpf/src/nlattr.c195
-rw-r--r--lib/bpf/src/nlattr.h106
-rw-r--r--lib/bpf/src/ringbuf.c284
-rw-r--r--lib/bpf/src/str_error.c21
-rw-r--r--lib/bpf/src/str_error.h6
-rw-r--r--lib/bpf/src/xsk.c930
-rw-r--r--lib/bpf/src/xsk.h255
-rwxr-xr-xlib/bpf/travis-ci/managers/debian.sh84
-rw-r--r--lib/bpf/travis-ci/managers/travis_wait.bash61
-rwxr-xr-xlib/bpf/travis-ci/managers/ubuntu.sh27
-rwxr-xr-xlib/bpf/travis-ci/vmtest/build_pahole.sh30
-rwxr-xr-xlib/bpf/travis-ci/vmtest/build_selftests.sh41
-rwxr-xr-xlib/bpf/travis-ci/vmtest/checkout_latest_kernel.sh44
-rw-r--r--lib/bpf/travis-ci/vmtest/configs/INDEX8
-rw-r--r--lib/bpf/travis-ci/vmtest/configs/blacklist/BLACKLIST-5.5.076
-rw-r--r--lib/bpf/travis-ci/vmtest/configs/blacklist/BLACKLIST-latest1
-rw-r--r--lib/bpf/travis-ci/vmtest/configs/latest.config3054
-rw-r--r--lib/bpf/travis-ci/vmtest/configs/whitelist/WHITELIST-4.9.07
-rw-r--r--lib/bpf/travis-ci/vmtest/helpers.sh12
-rwxr-xr-xlib/bpf/travis-ci/vmtest/mkrootfs.sh158
-rwxr-xr-xlib/bpf/travis-ci/vmtest/prepare_selftests.sh20
-rwxr-xr-xlib/bpf/travis-ci/vmtest/run.sh453
-rwxr-xr-xlib/bpf/travis-ci/vmtest/run_selftests.sh51
-rwxr-xr-xlib/bpf/travis-ci/vmtest/run_vmtest.sh38
-rw-r--r--lib/bpf/travis-ci/vmtest/vmlinux.h82043
-rw-r--r--lib/ctracer_relay.c101
-rw-r--r--lib/ctracer_relay.h23
-rw-r--r--lib/linux.blacklist.cu2
-rw-r--r--libbtf.c825
-rw-r--r--libbtf.h71
-rw-r--r--libctf.c772
-rw-r--r--libctf.h115
-rw-r--r--list.h601
-rw-r--r--man-pages/pahole.1963
-rwxr-xr-xostra/ostra-cg413
-rwxr-xr-xostra/python/ostra.py397
-rw-r--r--pahole.c2839
-rw-r--r--pahole_strings.h31
-rw-r--r--pdwtags.c157
-rw-r--r--pfunct.c792
-rw-r--r--pglobal.c339
-rw-r--r--prefcnt.c154
-rw-r--r--rbtree.c371
-rw-r--r--rbtree.h159
-rwxr-xr-xregtest282
-rw-r--r--rpm/SPECS/dwarves.spec454
-rw-r--r--scncopy.c125
-rw-r--r--strings.c88
-rw-r--r--syscse.c170
150 files changed, 152068 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..96e05c7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/build
+/config.h
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..6be99dc
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "lib/bpf"]
+ path = lib/bpf
+ url = https://github.com/libbpf/libbpf
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..857487a
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,166 @@
+project(pahole C)
+cmake_minimum_required(VERSION 2.8.8)
+cmake_policy(SET CMP0005 NEW)
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/lib/bpf/include/uapi)
+
+# Try to parse this later, Helio just showed me a KDE4 example to support
+# x86-64 builds.
+# the following are directories where stuff will be installed to
+set(__LIB "" CACHE STRING "Define suffix of directory name (32/64)" )
+
+macro(_set_fancy _var _value _comment)
+ if (NOT DEFINED ${_var})
+ set(${_var} ${_value})
+ else (NOT DEFINED ${_var})
+ set(${_var} "${${_var}}" CACHE PATH "${_comment}")
+ endif (NOT DEFINED ${_var})
+endmacro(_set_fancy)
+
+# where to look first for cmake modules,
+# before ${CMAKE_ROOT}/Modules/ is checked
+set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
+
+if (NOT CMAKE_BUILD_TYPE)
+ set (CMAKE_BUILD_TYPE Debug CACHE STRING
+ "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel."
+ FORCE)
+endif (NOT CMAKE_BUILD_TYPE)
+
+set(CMAKE_C_FLAGS_DEBUG "-Wall -Werror -ggdb -O0")
+set(CMAKE_C_FLAGS_RELEASE "-Wall -O2")
+
+# Just for grepping, DWARVES_VERSION isn't used anywhere anymore
+# add_definitions(-D_GNU_SOURCE -DDWARVES_VERSION="v1.19")
+add_definitions(-D_GNU_SOURCE -DDWARVES_MAJOR_VERSION=1)
+add_definitions(-D_GNU_SOURCE -DDWARVES_MINOR_VERSION=19)
+find_package(DWARF REQUIRED)
+find_package(ZLIB REQUIRED)
+
+# make sure git submodule(s) are checked out
+find_package(Git QUIET)
+if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
+ # Update submodules as needed
+ option(GIT_SUBMODULE "Check submodules during build" ON)
+ if(GIT_SUBMODULE)
+ message(STATUS "Submodule update")
+ execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ RESULT_VARIABLE GIT_SUBMOD_RESULT)
+ if(NOT GIT_SUBMOD_RESULT EQUAL "0")
+ message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules")
+ else()
+ message(STATUS "Submodule update - done")
+ endif()
+ endif()
+endif()
+if(NOT EXISTS "${PROJECT_SOURCE_DIR}/lib/bpf/src/btf.h")
+ message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.")
+endif()
+
+_set_fancy(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}${CMAKE_INSTALL_PREFIX}/${__LIB}" "libdir")
+
+# libbpf uses reallocarray, which is not available in all versions of glibc
+# libbpf's include/tools/libc_compat.h provides implementation, but needs
+# COMPACT_NEED_REALLOCARRAY to be set
+INCLUDE(CheckCSourceCompiles)
+CHECK_C_SOURCE_COMPILES(
+"
+#define _GNU_SOURCE
+#include <stdlib.h>
+int main(void)
+{
+ return !!reallocarray(NULL, 1, 1);
+}
+" HAVE_REALLOCARRAY_SUPPORT)
+if (NOT HAVE_REALLOCARRAY_SUPPORT)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DCOMPAT_NEED_REALLOCARRAY")
+endif()
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64")
+
+file(GLOB libbpf_sources "lib/bpf/src/*.c")
+add_library(bpf OBJECT ${libbpf_sources})
+set_property(TARGET bpf PROPERTY POSITION_INDEPENDENT_CODE 1)
+target_include_directories(bpf PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}/lib/bpf/include
+ ${CMAKE_CURRENT_SOURCE_DIR}/lib/bpf/include/uapi)
+
+set(dwarves_LIB_SRCS dwarves.c dwarves_fprintf.c gobuffer strings
+ ctf_encoder.c ctf_loader.c libctf.c btf_encoder.c btf_loader.c libbtf.c
+ dwarf_loader.c dutil.c elf_symtab.c rbtree.c)
+add_library(dwarves SHARED ${dwarves_LIB_SRCS} $<TARGET_OBJECTS:bpf>)
+set_target_properties(dwarves PROPERTIES VERSION 1.0.0 SOVERSION 1)
+set_target_properties(dwarves PROPERTIES INTERFACE_LINK_LIBRARIES "")
+target_include_directories(dwarves PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}/lib/bpf/include/uapi)
+target_link_libraries(dwarves ${DWARF_LIBRARIES} ${ZLIB_LIBRARIES})
+
+set(dwarves_emit_LIB_SRCS dwarves_emit.c)
+add_library(dwarves_emit SHARED ${dwarves_emit_LIB_SRCS})
+set_target_properties(dwarves_emit PROPERTIES VERSION 1.0.0 SOVERSION 1)
+target_link_libraries(dwarves_emit dwarves)
+
+set(dwarves_reorganize_LIB_SRCS dwarves_reorganize.c)
+add_library(dwarves_reorganize SHARED ${dwarves_reorganize_LIB_SRCS})
+set_target_properties(dwarves_reorganize PROPERTIES VERSION 1.0.0 SOVERSION 1)
+target_link_libraries(dwarves_reorganize dwarves)
+
+set(codiff_SRCS codiff.c)
+add_executable(codiff ${codiff_SRCS})
+target_link_libraries(codiff dwarves)
+
+set(ctracer_SRCS ctracer.c)
+add_executable(ctracer ${ctracer_SRCS})
+target_link_libraries(ctracer dwarves dwarves_emit dwarves_reorganize ${ELF_LIBRARY})
+
+set(dtagnames_SRCS dtagnames.c)
+add_executable(dtagnames ${dtagnames_SRCS})
+target_link_libraries(dtagnames dwarves)
+
+set(pahole_SRCS pahole.c)
+add_executable(pahole ${pahole_SRCS})
+target_link_libraries(pahole dwarves dwarves_reorganize)
+
+set(pdwtags_SRCS pdwtags.c)
+add_executable(pdwtags ${pdwtags_SRCS})
+target_link_libraries(pdwtags dwarves)
+
+set(pglobal_SRCS pglobal.c)
+add_executable(pglobal ${pglobal_SRCS})
+target_link_libraries(pglobal dwarves)
+
+set(pfunct_SRCS pfunct.c)
+add_executable(pfunct ${pfunct_SRCS})
+target_link_libraries(pfunct dwarves dwarves_emit ${ELF_LIBRARY})
+
+set(prefcnt_SRCS prefcnt.c)
+add_executable(prefcnt ${prefcnt_SRCS})
+target_link_libraries(prefcnt dwarves)
+
+set(scncopy_SRCS scncopy.c elfcreator.c)
+add_executable(scncopy ${scncopy_SRCS})
+target_link_libraries(scncopy dwarves ${ELF_LIBRARY})
+
+set(syscse_SRCS syscse.c)
+add_executable(syscse ${syscse_SRCS})
+target_link_libraries(syscse dwarves)
+
+install(TARGETS codiff ctracer dtagnames pahole pdwtags
+ pfunct pglobal prefcnt scncopy syscse RUNTIME DESTINATION
+ ${CMAKE_INSTALL_PREFIX}/bin)
+install(TARGETS dwarves LIBRARY DESTINATION ${LIB_INSTALL_DIR})
+install(TARGETS dwarves dwarves_emit dwarves_reorganize LIBRARY DESTINATION ${LIB_INSTALL_DIR})
+install(FILES dwarves.h dwarves_emit.h dwarves_reorganize.h
+ dutil.h gobuffer.h list.h rbtree.h pahole_strings.h
+ btf_encoder.h config.h ctf_encoder.h ctf.h
+ elfcreator.h elf_symtab.h hash.h libbtf.h libctf.h
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/include/dwarves/)
+install(FILES man-pages/pahole.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1/)
+install(PROGRAMS ostra/ostra-cg DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
+install(PROGRAMS btfdiff fullcircle DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
+install(FILES ostra/python/ostra.py DESTINATION ${CMAKE_INSTALL_PREFIX}/share/dwarves/runtime/python)
+install(FILES lib/Makefile lib/ctracer_relay.c lib/ctracer_relay.h lib/linux.blacklist.cu
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/share/dwarves/runtime)
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ 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.
+
+ GNU GENERAL PUBLIC LICENSE
+ 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 a brief idea of what it does.>
+ Copyright (C) <year> <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
+
+This 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.
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..57a721a
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,71 @@
+config.h.cmake
+btfdiff
+btf_encoder.c
+btf_encoder.h
+btf_loader.c
+ctf_encoder.c
+ctf_encoder.h
+ctf_loader.c
+dwarf_loader.c
+dwarves.c
+dwarves.h
+dwarves_emit.c
+dwarves_emit.h
+dwarves_fprintf.c
+dwarves_reorganize.c
+dwarves_reorganize.h
+cmake/modules/FindDWARF.cmake
+CMakeLists.txt
+codiff.c
+ctracer.c
+dtagnames.c
+elfcreator.c
+elfcreator.h
+elf_symtab.c
+elf_symtab.h
+fullcircle
+gobuffer.c
+gobuffer.h
+hash.h
+libbtf.c
+libbtf.h
+list.h
+MANIFEST
+man-pages/pahole.1
+pahole.c
+pdwtags.c
+pfunct.c
+pglobal.c
+prefcnt.c
+rbtree.c
+rbtree.h
+scncopy.c
+syscse.c
+strings.c
+pahole_strings.h
+dutil.c
+dutil.h
+changes-v1.13
+changes-v1.16
+changes-v1.17
+changes-v1.18
+changes-v1.19
+COPYING
+NEWS
+README
+README.DEBUG
+README.btf
+README.ctracer
+README.tarball
+rpm/SPECS/dwarves.spec
+lib/Makefile
+lib/ctracer_relay.c
+lib/ctracer_relay.h
+lib/linux.blacklist.cu
+ostra/ostra-cg
+ostra/python/ostra.py
+ctf.h
+libctf.c
+libctf.h
+regtest
+lib/bpf/
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..9de30d8
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,668 @@
+v1.19:
+
+Fri Nov 20 2020
+
+bf21da407593f104 fprintf: Make typedef__fprintf print anonymous enums
+9c4bdf9331bf06a7 fprintf: Align enumerators
+89cf28228a8ab55e fprintf: Add enumeration__max_entry_name_len()
+932b84eb45a9b8ba fprintf: Make typedef__fprintf print anonymous structs
+4a1479305b53933d pahole: Add heuristic to auto-add --btf_base for /sys/kernel/btf/ prefixed files
+e1d01045828a5c4c btf: Fallback to raw BTF mode if the header magic matches
+24cea890abedb15b pahole: Force '-F btf' with --btf_base
+cfad738682506ce4 libbtf: Assume its raw_btf if filename starts with "/sys/kernel/btf/"
+7293c7fceac36844 pahole: The --btf_base option receives a PATH, not a SIZE
+b3dd4f3c3d5ea59e btf_encoder: Use better fallback message
+d06048c53094d9d2 btf_encoder: Move btf_elf__verbose/btf_elf__force setup
+8156bec8aedb685b btf_encoder: Fix function generation
+d0cd007339ee509e btf_encoder: Generate also .init functions
+25753e0396abea25 pfunct: Use load stealer to speed up --class
+aa8fb8c091446467 man-pages: Add entry for -J/--btf_encode to pahole's man page
+ace05ba9414c1fe4 btf: Add support for split BTF loading and encoding
+7290d08b4a6e5876 libbpf: Update libbpf submodule reference to latest master
+344f2483cfcd4952 libbtf: Improve variable naming and error reporting when writing out BTF
+94a7535939f92e91 btf_encoder: Fix array index type numbering
+9fa3a100f71e7a13 pfunct: Use a load stealer to stop as soon as a function is found
+de18bd5fe3515358 pfunct: Try sole argument as a function name, just like pahole
+bc1afd458562f21e pahole: Introduce --numeric_version for use in scripts and Makefiles
+784c3dfbd63d5bcf dwarves: Switch from a string based version to major/minor numbers
+fc06ee1b6e9dc14a pahole: Check if the sole arg is a file, not considering it a type if so
+f47b3a2df3622204 dwarf_loader: Fix partial unit warning
+5a22c2de79fb9edf btf_encoder: Change functions check due to broken dwarf
+7b1af3f4844b36b9 btf_encoder: Move find_all_percpu_vars in generic collect_symbols
+863e6f0f2cc08592 btf_encoder: Check var type after checking var addr.
+5e7ab5b9e064a3eb btf_loader: Handle union forward declaration properly
+ec3f944102a71241 cmake: Make libbpf's Linux UAPI headers available to all binaries
+8cac1c54c83c346b btf_encoder: Ignore zero-sized ELF symbols
+040fd7f585c9b9fc btf_encoder: Support cross-compiled ELF binaries with different endianness
+29fce8dc8571c6af strings: use BTF's string APIs for strings management
+75f3520fedf6afdb strings: Rename strings.h to avoid clashing with /usr/include/strings.h
+bba7151e0fd2fb3a dwarf_loader: increase the size of lookup hash map
+2e719cca66721284 btf_encoder: revamp how per-CPU variables are encoded
+0258a47ef99500db btf_encoder: Discard CUs after BTF encoding
+3c913e18b2702a9e btf_encoder: Fix emitting __ARRAY_SIZE_TYPE__ as index range type
+48efa92933e81d28 btf_encoder: Use libbpf APIs to encode BTF type info
+5d863aa7ce539e86 btf_loader: Use libbpf to load BTF
+0a9b89910e711951 dwarves: Expose and maintain active debug info loader operations
+7bc2dd07d51cb5ee btf_encoder: detect BTF encoding errors and exit
+c35b7fa52cb8112a libbpf: Update to latest libbpf version
+ef4f971a9cf745fc dwarf_loader: Conditionally define DW_AT_alignment
+cc3f9dce3378280f pahole: Implement --packed
+08f49262f474370a man-pages: Fix 'coimbine' typo
+
+v1.18:
+
+Fri 02 Oct 2020
+
+aee6808c478b760f btf_loader: Initialize function->lexblock.tags to fix segfault in pdwtags
+c815d26689313d8d btf_encoder: Handle DW_TAG_variable that has DW_AT_specification
+b8068e7373cc1592 pahole: Only try using a single file name as a type name if not encoding BTF or CTF
+8b1c63283194ebe1 libctf: Make can't get header message to appear only in verbose mode
+63e11400e80f6ac4 libbtf: Make can't get header message to appear only in verbose mode
+fc2b317db0bdc1a9 dwarf_loader: Check for unsupported_tag return in last two missing places
+2b5f4895e8968e83 dwarf_loader: Warn user about unsupported TAGs
+010a71e181b450d7 dwarf_loader: Handle unsupported_tag return in die__process_class()
+3d616609ee0fd6df dwarf_loader: Add minimal handling of DW_TAG_subrange_type
+2e8cd6a435d96335 dwarf_loader: Ignore DW_TAG_variant_part for now to fix a segfault
+e9e6285fd0d63ed0 dwarf_loader: Skip empty CUs
+1abc001417b579b8 btf_encoder: Introduce option '--btf_encode_force'
+da4ad2f65028e321 btf_encoder: Allow disabling BTF var encoding.
+f5847773d94d4875 fprintf: Support DW_TAG_string_type
+8b00a5cd677df778 dwarf_loader: Support DW_TAG_string_type
+0d9c3c9835659fb7 dwarves: Check if a member type wasn't found and avoid a NULL deref
+2ecc308518edfe05 dwarf_loader: Bail out at DW_TAG_imported_unit tags
+8c92fd298101171d dwarf_loader: Ignore entries in a DW_TAG_partial_unit, for now
+4cfd420f7ef13af4 README: Add instructions to do a cross build
+9e495f68c683574f dwarf_loader: Move vaddr to conditional where it is used
+69fce762074a5483 pahole: Use "%s" in a snprintf call
+22f93766cf02f4e0 pahole: Support multiple types for pretty printing
+78f2177d904902c6 pahole: Print the evaluated range= per class
+5c32b9d5c7ac8fff pahole: Count the total number of bytes read from stdin
+e3e5a4626c94d9c5 pahole: Make sure the header is read only once
+208bcd9873442013 pahole: Introduce 'range=member' as a class argument for pretty printing
+b9e406311990c2d5 pahole: Cache the type_enum lookups into struct enumerator
+fda1825f0b9f1c9f dwarves: Introduce tag_cu_node, so that we can have the leaner tag_cu
+47d4dd4c8a2cf9fd pahole: Optimize --header processing by keeping the first successfull instance
+fdfc64ec44f4ad53 pahole: Introduce --range
+f63d3677e31d88a3 pahole: Support multiple enums in type_enum=
+c50b6d37e99fddec pahole: Add infrastructure to have multiple concatenated type_enum
+671ea22ca18dd41f pahole: Move finding type_enum to a separate function
+dd3c0e9eb0cbc77f dwarves: Move the common initialization of fields for 'struct type'
+aa7ab3e8755a4c12 pahole: Allow for more compact enum filters by suppressing common prefix
+4ece15c37b028a80 dwarves: Find common enumerators prefix
+d28bc97b5c12736b man-pages: Document pretty printing capabilities and provide examples
+a345691f08f04f19 pahole: Make --header without -C to be equivalent to -C header-arg --count=1
+eba4ac6b2c1dcb11 pahole: Fallback to pretty printing using types in multiple CUs
+7c12b234ee30f6d2 dwarves: Introduce cus__find_type_by_name()
+c6f3386e3364486d pahole: Make the type_instance constructor receive the looked up type + its CU
+30297256e1f27de5 pahole: Pass the header type_instance to tag__stdio_fprintf_value()
+ead8084d3ffcda1f pahole: Store the CU in the type_instance struct
+2c886fff0af13510 pahole: Store the CU where type_enum was found
+526b116bfec71054 pahole: If pretty printing, don't discard CUs, keep them
+a1b8fddb4dcd90d9 pahole: Show which classes were not processed and why
+bc6a3cac50cdbdf6 pahole: Fixup the --class_name parsing wrt class args (type=, sizeof=, etc)
+363c37340521debe pahole: Remope pretty printed classes from the prototypes list
+9f675e7fdbf525fb cmake: Use -O0 for debug builds
+4e5b02beea24656d pahole: Don't stop when not finding the type_enum
+823739b56f2b5230 pahole: Convert class_names into a list of struct prototypes
+0a97d6c143fcc92a pahole: Factor out parsing class prototypes
+80af0fbbf3a3f1f6 dutils: Allow for having a priv area per strlist
+04b6547e7343410e pahole: Honour --hex_fmt when pretty printing
+266c0255984ddbe7 pahole: Support filters without 'filter='
+6c683ce0e11d6c1c pahole: Allow for a 'type' boolean class arg meaning 'type=type'
+1c68f41bb87f7718 pahole: Allow for a 'sizeof' boolean class arg meaning 'sizeof=size'
+b4b3fb0a3aeb9686 pahole: First look for ',' then '=' to allow for boolean args
+446b85f7276bda4c pahole: Add support for --size_bytes, accepts header variables
+0f10fa7529d4a9c7 pahole: Move reading the header to outside the --seek_bytes code
+9310b04854a8b709 pahole: Add support for referencing header variables when pretty printing
+611f5e8bd7eb760d pahole: Add == class member filtering
+f2987224beb2b496 pahole: Do the 'type_enum' class argument validation as soon as we parse it
+02ca176c6290ff96 pahole: Do the 'type' class argument validation earlier
+172d743d9ce3f3a1 pahole: Do the 'sizeof' class argument validation earlier
+9a30c101d7c019ee pahole: As soon as a attribute is found, check if the type is a struct or class
+a38ccf47237298dc pahole: Allow filter=expression as a class argument for pretty printing
+3c4ee1d91f5311ac pahole: Pretty print bitfields
+451d66ec02e94492 pahole: Pretty print unions too, cope with unnamed ones
+48c7704b49ffa6b1 pahole: Check if the type with arguments is present in the current CU
+472519ac2c49d340 pahole: Support nested structs
+ae43029363ee2c53 dwarves_fprintf: Export the 'tabs' variable
+ab714acec86841b7 pahole: Support zero sized base type arrays
+ff7815a0f823a676 pahole: Add missing space before '}' in array__fprintf_base_type_value()
+5f2502274e1097db pahole: Support zero sized arrays in array__fprintf_base_type_value()
+3aadfbdd72e32016 pahole: Follow array type typedefs to find real sizeof(entry)
+7309039aa7510eec pahole: Make 'type' + 'type_enum' select a type to cast a variable sized record
+42b7a759f37e5d45 dutil: Add a strlwr() helper to lowercase a string, returning it
+a5bb31b86fbe5362 pahole: Fix --skip for variable sized records
+f4df384d77312867 pahole: Decouple reading ->sizeof_member from printing
+78cdde5cb7200555 pahole: Introduce 'type_enum' class argument
+163c330d3138e56f dwarves: Introduce cu__find_enumeration_by_name()
+20051b7157fce20c pahole: Add the 'type' modifier to make a struct member be used to find a cast type
+210dffe0d13d171b pahole: Iterate classes in the order specified in the command line:
+37a5c7c5ba0e1e7d strlist: Allow iterating a strlist in the original order
+6b5d938d99b2e457 pahole: Support multiple class/struct args
+affbebf04bcaa1ec pahole: Make the class splitter take into account per-class parameters
+84387de4a5cf141d pahole: Allow specifying a struct member based sizeof() override
+e0773683fa3edd61 dwarves: Allow setting a struct/class member as the source of sizeof()
+f399db09a0d5b069 pahole: Allow simple parser for arguments to classes
+04d957ba3cdf1047 pahole: Add variable for class name when traversing list of classes
+f3d9054ba8ff1df0 btf_encoder: Teach pahole to store percpu variables in vmlinux BTF.
+fe284221448c950d pahole: Introduce --seek_bytes
+44af7c02b5d0a7f2 pahole: Implement --skip, just like dd
+5631fdcea1bbe2bc pahole: Introduce --count, just like dd's
+272bc75024b4f08c man-pages: Add information about stdin raw data pretty printing
+66835f9e190ce77e pahole: Add support for base type arrays
+1a67d6e70090953d pahole: Factor out base_type__fprintf_value()
+6fb98aa1209f02b5 pahole: Support char arrays when dumping from stdin
+a231d00f8d08825e pahole: Print comma at the end of field name + field value
+1b2cdda38c0a6f5e dwarves: Introduce tag__is_array()
+a83313fb22520d8d pahole: Pretty print base types in structs from stdin
+cc65946e3068f7b6 dwarves: Adopt tag__is_base_type() from ctrace.c
+d8079c6d373a5754 pahole: Hex dump a type from stdio when it isn't a tty
+38109ab45fe0307d spec: Fix date
+
+v1.17:
+
+Fri 13 Mar 2020
+
+f14426e41046 docs: Add command line to generate tarball with a prefix
+0b2621d426ea dwarves: Avoid truncation when concatenating paths for dir entries
+d7b351079583 dwarves: Don't use conf if its NULL in cus__load_running_kernel()
+dde3eb086dd3 dwarves: Make list__for_all_tags() more robust
+081f3618a795 dwarves: Add -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 to build libbpf
+e8f43a4963bf libbpf: Get latest libbpf
+de84ae36c738 cmake: libebl was merged into libdw
+290b8fdbdab7 pahole: Improve --contains --recursive a bit
+3d5be866e55f pahole: Fill 'tab' with spaces earlier
+88674f039551 gobuffer: Do not crash on object without any public symbols
+cb17f94f46a0 Add changes-v1.16 to the MANIFEST file
+32a19e044c93 pahole: Fix changelog date in dwarves.spec
+9b09f578b7d1 pahole: Initialize var to silence -Werror=maybe-uninitialized on gcc version 4.8.5 20150623
+277c2b3d1b4a man-pages: Add section about --hex + -E to locate offsets deep into sub structs
+812c980b3f76 man-pages: Update some info, expand BTF info, add some EXAMPLES
+e0942c7b031f pahole: Add support for finding pointers to void
+6a326e93088e pahole: Make --contains and --find_pointers_to to work with base types
+3c1a9a3329d9 pahole: Make --contains look for more than just unions, structs
+ded5d36f9cf5 dwarves: Introduce cu__find_type_by_name
+88d99562e55c pahole: Add --structs to ask just for structs, counterpart of --unions
+0b444cf1118c pahole: Make --contains and --find_pointers_to honour --unions
+a8a7e605bb45 pahole: Make --find_pointers_to consider unions
+0dc327e382f8 pahole: Consider unions when looking for classes containing some class
+3661f17d0b2c pahole: Introduce --unions to consider just unions
+2dd09c617101 pahole: union have no holes, fix --sizes formatter
+6c29cca8c28f pahole: Don't consider unions for options that only make sense for structs
+dcba200367f7 pahole: When the sole argument passed isn't a file, take it as a class name
+1944de0c93e6 pahole: Use function__for_each_parameter()
+66e640508e4d dwarves: Make function__for_each_parameter receive 'cu' arg
+252b0fcc91cc pahole: Fix -m/--nr_methods - Number of functions operating on a type pointer
+202c8d5f5bd5 pahole: Do not require a class name to operate without a file name
+617f5ac2e6c9 dwarves: Move BTF loader ahead of the CTF one
+cdd5e1399b9c btf loader: Support raw BTF as available in /sys/kernel/btf/vmlinux
+
+v1.16:
+
+Mon Dec 16 2019
+
+69df73444c69 (HEAD -> master, acme.korg/master) dwarves: Add -O2 to CFLAGS
+6d11dd157f75 elfcreator: Address initialization warning emitted by 'gcc -O6'
+1bc63a4cffa7 fprintf: Fixup truncation possibility pointed out by gcc -O2
+5c590fc29d06 ptr_table: Zero out new id ranges
+644466dce77f fprintf: Remove extraneous sizeof operator
+a59459bb80e7 fprintf: Account inline type __aligned__ member types for spacing
+56547f133a5b fprintf: Fix alignment of class members that are structs/enums/unions
+be37b64aef38 dwarves: Ignore static members for alignment
+cca018e495a8 SPEC: Add dwarves dependency on libdwarves1
+ccf3eebfcd9c btf_loader: Add support for BTF_KIND_FUNC
+f95fd85f7b53 pfunct: type->type == 0 is void, fix --compile for that
+3e8f09e304ba (origin/master, origin/HEAD) pdwtags: Print DW_TAG_subroutine_type as well
+3c5f2a224aa1 btf_encoder: Preserve and encode exported functions as BTF_KIND_FUNC
+910e060b5c4b btf_loader: Skip BTF_KIND_DATASEC entries
+96235a74a34d btf_loader: Replace if/else series with a proper switch {}
+a4ba2234ff58 btf_loader: Support BTF_KIND_VAR
+5965ce015eab dwarves: Fix ptr_table__add_with_id() handling of pt->nr_entries
+173911ac38ea btf_loader: Show the unknown kind numbers
+0d8e11e8568e pglobal: Allow passing the format path specifier, to use with BTF
+ba27df9f2f94 CMakeLists: Use lib/bpf/include/uapi when building libdwarves
+95517e1d995e libbpf: Get latest libbpf
+ca294eebfc47 MANIFEST: Add missing files
+d5e01d10e515 fprintf: Set tconf.type_spacing earlier
+c6a9a0eb6ac7 fprintf: Fix up decrementing recursivity level in type__fprintf()
+93c3cdf89715 cmake: Add -Wall to CFLAGS
+7360f0528ff3 elfcreator: Remove unused 'machine' variable
+0f52b11f911c prefcnt: Avoid ambiguous else with for_each macro
+608813d0affa pglobal: Avoid ambiguous else
+77d06c2d305d reorganize: Enclose bitfield routines under ifdef
+2221ae30bce9 reorganize: Ditch unused variable
+c419c513eae5 libbtf: Enlarge the 'cmd' buffer to not possibly truncate the pathname
+092fffe56701 btf_loader: Enum values are s32, cast before calling btf_elf__get32()
+7bfb1aa80f9a libctf: Enlarge the 'cmd' buffer to not possibly truncate the pathname
+f67c281f9841 fprintf: Correct the type for the 'cacheline' variable, it should be uint32_t
+7b36fab5a8df fprintf: Add guard against unlikely overlapping copy
+e737976c09d9 fprintf: Do not scrub type when looking up its type
+e95dacb704bf fprintf: Remove unused printf arg when printing enumerations
+71c4f83f2828 emit: Remove unused 'is_pointer' variable
+fe87354c31bb dwarves: Ditch unused asprintf() function
+60c73a769882 dwarves: We need to consistently check if 'conf was specified
+5fdfd09a6bbe dwarves: Fix check in type__find_first_biggest_size_base_type_member()
+24ced5be8a75 dwarf_loader: Fix array overrun access
+33e2d7aa35a7 ctf_loader: Plug leak when bailing out due to unknown tag
+aefa9303818b codiff: Remove unused filenames variable
+de5e72bc15fb btf_loader: Plug leak when bailing out due to unknown tag
+511a79129494 dwarves: Remove unused variable
+b1412a88bb61 fprintf: Fixup handling classes with no members
+[acme@quaco pahole]$
+
+v1.15
+
+Thu Jun 27 2019
+
+3ed9a67967cf fprintf: Avoid null dereference with NULL configs
+568dae4bd498 printf: Fixup printing "const" early with "const void"
+68f261d8dfff fprintf: Fix recursively printing named structs in --expand_types
+139a3b337381 ostra: Initial python3 conversion
+01276a7e8966 spec: Sync spec with fedora's
+9f1f0628b9ad rpm: Add missing devel headers
+989dc3f1ba0d cmake: Install missing devel headers
+
+v1.13
+
+Tue Apr 16 2019
+
+See changes-v1.13 for a more verbose description of the changes.
+
+0fb727166a0e pfunct: Strip inlines in the code generated for --compile
+7b967744db7b emit: Emit the types for inline structs defined in pointer members
+30526e2848db fprintf: Print relative offsets for inner pointer structs
+cfa377c238f8 emit: Do not emit a forward declararion to a nameless struct
+cf459ca16fb2 fprintf: Pretty print struct members that are pointers to nameless structs
+09ed2e78befe pfunct: Emit definitions for pointers inside pointer to function args
+e9fc2f647026 fullcircle: Check that we found the CFLAGS
+05921c47f557 emit: Handle typedefs that are a pointer to typedefs
+87af9839bf63 fullcircle: Try building from pfunct --compile and check types
+c7fd9cc1fe97 pfunct: Fixup more return types
+56c50b8dbe9b emit: Make find_fwd_decl a bit more robust
+2bcb01fc2f9d fprintf: Handle single zero sized array member structs
+0987266cd9e2 fprintf: Deal with zero sized arrays in the midle of a union
+1101337a7450 fprintf: Deal with zero sized arrays in the middle of a struct
+13aa13eb0ca4 fprintf: Don't reuse 'type' in multiple scopes in the same function
+c8fc6f5a7a46 core: Use unnatural alignment of struct embedded in another to infer __packed__
+6f0f9a881589 fprintf: Fixup multi-dimensional zero sized arrays const handling
+9a4d71930467 fprintf: Allow suppressing the inferred __attribute__((__packed__))
+ec935ee422b0 fprintf: Allow suppressing the output of force paddings at the end of structs
+8471736f3c6c core: Cope with zero sized types when looking for natural alignment
+986a3b58a869 fprintf: Only add bitfield forced paddings when alignment info available
+49c27bdd6663 core: Allow the loaders to advertise features they have
+dc6b9437a3a0 emit: Handle structs with DW_AT_alignment=1 meaning __packed__
+f78633cfb949 core: Infer __packed__ for union struct members
+75c52de9c6df core: Move packed_attribute_inferred from 'class' to 'type' class
+1bb4527220f4 fprintf: Fixup const pointers
+dc3d44196103 core: Improve the natural alignment calculation
+ac32e5e908ba codiff: Fix comparision of multi-cu against single-cu files
+f2641ce169d6 core: Take arrays into account when inferring if a struct is packed
+85c99369631a fprintf: Do not add explicit padding when struct has __aligned__ attr
+b5e8fab596d3 emit: Cover void ** as a function parameter
+28a3bc7addad fprintf: Support packed enums
+f77a442f09e3 fprintf: Do not print the __aligned__ attribute if asked
+ea583dac52f0 fprintf: Print zero sized flat arrays as [], not [0]
+f909f13dd724 fprintf: Fixup handling of unnamed bitfields
+3247a777dcfc core: Infer if a struct is packed by the offsets/natural alignments
+13e5b9fc00ee fprintf: Add unnamed bitfield padding at the end to rebuild original type
+ccd67bdb205b fprintf: Print "const" for class members more early, in type__fprintf()
+b42d77b0bbda fprintf: Print __attribute__((__aligned__(N))) for structs/classes
+1c9c1d6bbd45 dwarf_loader: Store DW_AT_alignment if available in DW_TAG_{structure,union,class}_type
+41c55858daf4 codiff: Add --quiet option
+a104eb1ea11d fprintf: Notice explicit bitfield alignment modifications
+75f32a24c7c7 codiff: Improve the comparision of anonymous struct members
+6b1e43f2c1ac codiff: When comparing against a file with just one CU don't bother finding by name
+15a754f224f7 core: Add nr_entries member to 'struct cus'
+99750f244cb8 pfunct: Generate a valid return type for the --compile bodies
+881aabd6fc22 reorganize: Introduce class__for_each_member_from_safe()
+1b2e3389f304 reorganize: Introduce class__for_each_member_reverse()
+10fef2916dce reorganize: Introduce class__for_each_member_continue()
+e7a56ee8cc69 reorganize: Introduce class__for_each_member_from()
+9a79bb6ced23 tag: Introduce tag__is_pointer_to()
+45ad54594442 tag: Introduce tag__is_pointer()
+89ce57a02e3a pdwtags: Find holes in structs
+ce6f393bc9ea fprintf: Fixup the printing of const parameters
+7aec7dd6c29c pfunct: Do not reconstruct external functions
+163b873f81c8 pfunct: Do not reconstruct inline expansions of functions
+ea83b780eca0 pfunct: Handle unnamed struct typedefs
+e7ebc05d12e1 emit: Unwind the definitions for typedefs in type__emit_definitions()
+093135b0bfd5 pfunct: Do not emit a type multiple times
+3ce2c5216612 pfunct: Ask for generating compilable output that generates DWARF for types
+e7a786540d83 pfunct: Make --expand_types/-b without -f expand types for all functions
+9b2eadf97b44 pfunct: Follow const, restrict, volatile in --expand_types
+f3f86f2f89b0 pfunct: Reconstruct function return types for --expand_types
+a7d9c58cb81a fprintf: Add missing closing parens to the align attribute
+d83d9f578fa0 dwarf_loader: Handle DW_TAG_label in inline expansions
+73e545b144b4 dwarf_loader: Handle unsupported_tag in die__process_inline_expansion
+fe590758cb3f class__find_holes: Zero out bit_hole/hole on member
+863c2af6e9d7 reorganize: Disable the bitfield coalescing/moving steps
+b95961db697a fprintf: Show statistics about holes due to forced alignments
+ec772f21f681 fprintf: Show the number of forced alignments in a class
+52d1c75ea437 btfdiff: Use --suppress_aligned_attribute with -F dwarf
+6cd6a6bd8787 dwarves_fprintf: Allow suppressing the __attribute__((__aligned__(N))
+f31ea292e3cb dwarf_loader: Store the DW_AT_alignment if available
+c002873c4479 dwarves_fprintf: Move invariant printing of ; to outside if block
+8ce85a1ad7f0 reorganize: Use class__find_holes() to recalculate holes
+5d1c4029bd45 dwarves: Fix classification of byte/bit hole for aligned bitfield
+78c110a7ea24 dwarves: Revert semantics of member bit/byte hole
+b56fed297e5f dwarves_fprintf: Count bitfield member sizes separately
+c0fdc5e685e9 dwarf_loader: Use DWARF recommended uniform bit offset scheme
+5104d1bef384 loaders: Record CU's endianness in dwarf/btf/ctf loaders
+975757bc8867 dwarves: Use bit sizes and bit/byte hole info in __class__fprintf
+1838d3d7623e dwarves: Revamp bit/byte holes detection logic
+03d9b6ebcac7 dwarf_loader: Fix bitfield fixup logic for DWARF
+4abc59553918 btf_loader: Adjust negative bitfield offsets early on
+41cf0e3cba0c dwarf_loader: Don't recode enums and use real enum size in calculations
+55c96aaed8ce loaders: Strip away volatile/const/restrict when fixing bitfields
+7005757fd573 libbpf: Sync in latest libbpf sources
+69970fc77ec5 pahole: Filter out unions when looking for packable structs
+fa963e1a8698 dwarves_fprintf: Print the bit_offset for inline enum bitfield class members
+bb8350acf52f dwarves: Switch type_id_t from uint16_t to uint32_t
+5375d06faf26 dwarves: Introduce type_id_t for use with the type IDs
+f601f6725890 libctf: The type_ids returned are uint32_t fixup where it was uint16_t
+c9b2ef034f89 dwarf: Add cu__add_tag_with_id() to stop using id == -1 to allocate id
+762e7b58f447 dwarves: Change ptr_table__add() signature to allow for uint32_t returns
+079e6890b788 dwarf_loader: Mark tag__recode_dwarf_bitfield() static
+3526ebebd3ab pahole: Use 32-bit integers for type ID iterations within CU
+3bd8da5202e4 libbpf: update reference to bring in btf_dedup fixes
+a9afcc65fc8f btf_encoder: Don't special case packed enums
+8f4f280163b7 btf_loader: Simplify fixup code by relying on BTF data more
+be5173b4df0f dwarves: Fixup sizeof(long double) in bits in base_type_name_to_size table
+5081ed507095 dwarves: Add _Float128 base_type
+d52c9f9b9455 dwarf_loader: Fixup bitfield entry with same number of bits as its base_type
+6586e423d4fa btf_loader: Fix bitfield fixup code
+7daa4300d230 pahole: Complete list of base type names
+6bcf0bd70305 btfdiff: Support specifying custom pahole location
+88028b5d0c32 btfdiff: Use --show_private_classes with DWARF
+e6c59bd11d3d libbpf: Build as PIC and statically link into libdwarves
+cf4f3e282d64 cmake: Bump miminum required version to use OBJECT feature
+5148be53dc65 btfdiff: Rename tmp files to contain the format used
+dd3a7d3ab3e8 btf_encoder: run BTF deduplication before writing out to ELF
+54106025cd14 libbtf: Fixup temp filename to .btf, not .btfe
+e6dfd10bcbf3 libbpf: Build as shared lib
+c234b6ca6e55 libbpf: Pull latest libbpf
+fe4e1f799c55 btf_elf: Rename btf_elf__free() to btf_elf__delete()
+6780c4334d55 btf: Rename 'struct btf' to 'struct btf_elf'
+ca86e9416b8b pahole: use btf.h directly from libbpf
+21507cd3e97b pahole: add libbpf as submodule under lib/bpf
+c25ada500ddc pahole: Add build dir, config.h to .gitignore
+a58c746c4c7e Fixup copyright notices for BTF files authored by Facebook engineers
+e714d2eaa150 Adopt SPDX-License-Identifier
+c86960dce55d btf_loader: We can set class_member->type_offset earlier
+278b64c3eee0 btfdiff: Use diff's -p option to show the struct/union
+1182664d6aa6 dwarves_fprintf: Handle negative bit_offsets in packed structs with bitfields
+b0cf845e02c6 dwarves: Change type of bitfield_offset from uint8_t to int8_t
+06e364bc62e7 btfdiff: Add utility to compare pahole output produced from DWARF and BTF
+b79db4cab41c dwarves: add __int128 types in base_type_name_to_size
+de3459cc0ebe btf_loader: BTF encodes the size of enums as bytes not bits
+693347f8def7 btf_encoder: Fix void handling in FUNC_PROTO.
+2d0b70664f3e dwarves_fprintf: Separate basic type stats into separate type__fprintf() method
+18f5910f96e0 dwarves: Add type to tag helper
+f2092f56586a btf: recognize BTF_KIND_FUNC in btf_loader
+11766614096c btf: Fix kind_flag usage in btf_loader
+68b93e6858ae dutil: Add missing string.h header include
+851ef335e328 dutil: Drop 'noreturn' attribute for ____ilog2_NaN()
+ab0cb33e54e8 btf_loader: Fixup class_member->bit_offset for !big_endian files
+b24718fe27d3 dwarves: Fix documentation for class_memer->bitfield_size
+3ffe5ba93b63 pahole: Do not apply 'struct class' filters to 'struct type'
+da18bb340bee dwarves: Check if the tag is a 'struct class' in class__find_holes()
+2a82d593be81 btf: Add kind_flag support for btf_loader
+472256d3c57b btf_loader: Introduce a loader for the BTF format
+93d6d0016523 dwarves: No need to print the "signed ", the name has it already
+0a9bac9a3e8e dwarves: Relookup when searching for signed base types
+a2cdc6c2a0a3 dutil: Adopt strstart() from the linux perf tools sources
+3aa3fd506e6c btf: add func_proto support
+8630ce404287 btf: fix struct/union/fwd types with kind_flag
+65bd17abc72c btf: Allow multiple cu's in dwarf->btf conversion
+d843945ba514 pahole: Search for unions as well with '-C'
+da632a36862c dwarves: Introduce {cu,cus}__find_struct_or_union_by_name() methods
+31664d60ad41 pahole: Show tagged enums as well when no class is specified
+b18354f64cc2 btf: Generate correct struct bitfield member types
+70ef8c7f07ff dwarves_fprintf: Set conf.cachelinep in union__fprintf() too
+bfdea37668c6 dwarves_fprintf: Print the scope of variables
+465110ec99d3 dwarves: Add the DWARF location to struct variable
+c65f2cf4361e dwarves: Rename variable->location to ->scope
+0d2511fd1d8e btf: Fix bitfield encoding
+92417082aad3 MANIFEST: Add missing COPYING file
+eb6bd05766f5 dwarf_loader: Process DW_AT_count in DW_TAG_subrange_type
+
+v1.12
+
+Thu Aug 16 2018
+
+1ca2e351dfa1 README.btf: Add section on validating the .BTF section via the kernel
+9eda5e8163ce README.btf: No need to use 'llvm.opts = -mattr=dwarfris' with elfutils >= 0.173
+7818af53f64a dwarves: Add a README.btf file with steps to test the BTF encoder
+f727c22191d0 dwarf_loader: Initial support for DW_TAG_partial_unit
+e975ff247aa8 dwarves_fprintf: Print cacheline boundaries in multiple union members
+68645f7facc2 btf: Add BTF support
+81466af0d4f8 pahole: Show the file where a struct was used
+2dd87be78bb2 dwarves_fprintf: Show offsets at union members
+66cf3983e1ac README.DEBUG: Add an extra step to make the instructions cut'n'exec
+2a092d61453c dwarves: Fix cus__load_files() success return value
+02a456f5f54c pahole: Search and use running kernel vmlinux when no file is passed
+5f057919a0c0 man-pages: Add entry for --hex
+
+v1.11
+
+Wed Jun 2017
+
+5a57eb074170 man-pages: Update URL to the dwarves paper
+b52386d041fa dwarves_fprintf: Find holes when expanding types
+103e89bb257d dwarves_fprintf: Find holes on structs embedded in other structs
+ab97c07a7ebe dwarves_fprintf: Fixup cacheline boundary printing on expanded structs
+046ad67af383 dwarves_fprintf: Shorten class__fprintf() sig
+44130bf70e1c dwarves: Update e-mail address
+327757975b94 dwarf_loader: Add URL for template tags description
+f4d5e21fd1b2 dwarf_loader: Tidy up template tags usage
+e12bf9999944 dwarf_loader: Do not hash unsupported tags
+3afcfbec9e08 dwarf_loader: Add DW_TAG_GNU_formal_parameter_pack stub in process_function
+55d9b20dbaf6 dwarf_loader: Ignore DW_TAG_dwarf_procedure when processing functions
+45618c7ec122 dwarf_loader: Initial support for DW_TAG_unspecified_type
+658a238b9890 dwarf_loader: Stop emitting warnings about DW_TAG_call_site
+0fbb39291d59 dwarf_loader: Add support for DW_TAG_restrict_type
+9df42c68265d dwarves: Initial support for rvalue_reference_type
+8af5ccd86d21 dwarves: Use cus__fprintf_load_files_err() in the remaining tools
+10515a7c4db7 dwarves: Introduce cus__fprintf_load_files_err()
+0e6463635082 pahole: Show more informative message when errno is properly set on error
+2566cc2c8715 pdwtags: Show proper error messages for files it can't handle
+9f3f67e78679 dwarves: Fix cus__load_files() error return
+ae3a2720c3d3 dutil: Add ____ilog2_NaN declaration to silence compiler warning
+0b81b5ad4743 Update version in CMakeLists.txt
+79536f4f9587 cmake: Use INTERFACE_LINK_LIBRARIES
+1decb1bc4a41 dwarf_loader: Check cu__find_type_by_ref result
+956343d05a41 Add instructions on how to build with debug info
+e71353c3fa0a dwarf_loader: Ignore DW_TAG_dwarf_procedure
+189695907242 dwarves_fprintf: Add the missing GNU_ suffix to DWARF_TAG_ created by the GNU project
+d973b1d5daf0 dwarf_fprintf: Handle DW_TAG_GNU_call_site{_parameter}
+c23eab4b1253 dwarf_loader: Print unknown tags as an hex number
+943a0de0679a dwarves_fprintf: DW_TAG_mutable_type doesn't exist.
+a8e562a15767 dwarf_loader: Use obstack_zalloc when allocating tag
+fd3838ae9aa3 dwarves: Stop using 'self'
+5ecf1aab9e10 dwarf_loader: Support DW_FORM_data{4,8} for reading class member offsets
+c4ccdd5ae63b dwarves_reorganize: Fix member type fixup
+e31fda3063e3 dwarves_reorganize: Fixup calculation of bytes needed for bitfield
+1e461ec7e0e8 dwarves_fprintf: Fix printf types on 64bit linux
+222f0067a9c3 dwarves_fprintf: Don't ignore virtual data members
+e512e3f9b36b dwarves: Update git url
+8c6378fd8834 dwarves: Support static class data members
+a54515fa6ee4 dwarves: Stop using 'self'
+6035b0d91f19 rpm: Add missing BuildRequires: zlib-devel
+be7b691756ff dwarf_loader: Don't stop processing after finding unsupported tag
+
+v1.10
+
+Wed May 30 2012
+
+. Initial DWARF4 support, by Tom Tromey
+
+. Add stubs for some new GNU Tags
+
+. Fix build on older systems
+
+. Fix a crash when pahole is called with -R -S, from Tal Kelrich
+
+v1.9:
+
+Ignore DW_TAG_template_{type,value}_parameter, fixing a bug reported at:
+
+https://bugzilla.redhat.com/show_bug.cgi?id=654471
+
+More work is needed to properly support these tags.
+
+-----------------------------------------
+
+After a long time without a new release because I was trying to get the CTF
+support completed, and due to the very strong gravity force in the Linux kernel
+perf tools, here it is 1.8, with lots of performance improvements, bug fixes
+and changes to better use these tools in scripts.
+
+For full details please take a look at the git changesets, repo available at:
+
+http://git.kernel.org/cgit/devel/pahole/pahole.git
+
+- Arnaldo
+
+pahole:
+
+. Allow list of structs to be passed to pahole.
+
+ E.g.: 'pahole -C str_node,strings'
+
+ Suggested by Zack Weinberg <zweinberg@mozilla.com>, for scripting.
+
+. Introduce --hex to print offsets and sizes in hexadecimal
+
+codiff:
+
+. Improve detection of removal and addition of members in structs
+
+. Detect changes in padding and the number of holes/bit_holes
+
+pfunct:
+
+. --no_parm_names
+
+ Because CTF doesn't encodes the names of the parameters and I want to
+ test the upcoming CTF function section code in ctftwdiff.
+
+. pfunct --addr
+
+ Using an rbtree to find in which function the given addr is.
+
+libdwarves:
+
+. Greatly reduce the data structures footprint and lookup by recoding
+ the IDs as short integers, that was done to facilitate support for CTF
+ but benefited the core libraries greatly.
+
+. Handle GCC support for vector instructions
+
+ So now it recognizes, as printed by pdwtags:
+
+ 908 typedef int __m64 __attribute__ ((__vector_size__ (8))); size: 8
+ 909 int array __attribute__ ((__vector_size__ (8))); size: 8
+ 910 int array __attribute__ ((__vector_size__ (4))); size: 4
+ 911 short int array __attribute__ ((__vector_size__ (2))); size: 2
+ 912 char array __attribute__ ((__vector_size__ (1))); size: 1
+
+. Destructors were added so that no leaks are left if this library is to
+ be used in other tools that don't end the program when done using this lib.
+
+. Allow the tools to pass a callback that is used after loading each object
+ file (CU/Compile Unit), so that we can more quickly find tags and stop
+ the processing sooner, or at least delete the CU if it doesn't have anything
+ needed by the tool. This _greatly_ speeded up most of the tools.
+
+. Tools now can pass a debug format "path", specifying the order it wants to
+ try, so that if a file have both DWARF and CTF, specifying 'ctf,dwarf' will
+ use the CTF info.
+
+. Now the formatting routines are in a separate file, dwarves_fprintf.c. This
+ was done for organizational purposes but also to pave the way for multiple
+ formatting backends, so that we can print, for instance, in CSV the structs,
+ for easier scripting like done by several folks out there.
+
+. Handle volatile typedef bitfields, like:
+
+ struct _GClosure {
+ volatile guint ref_count:15; /* 0:17 4 */
+ volatile guint meta_marshal:1; /* 0:16 4 */
+ volatile guint n_guards:1; /* 0:15 4 */
+
+. Load java 'interfaces' as a struct/class.
+
+. Fix buffer expansion bug, detected thanks to boost that provided things like:
+
+ virtual int undefine(class
+ grammar_helper<boost::spirit::grammar<boost::detail::graph::dot_skipper,
+ boost::spirit::parser_context<boost::spirit::nil, class
+ grammar<boost::detail::graph::dot_skipper,
+ boost::spirit::parser_context<boost::spirit::nil_t> > *); /*
+ linkage=_ZN5boost6spirit4impl14grammar_helperINS0_7grammarINS_6detail5graph11dot_skipperENS0_14parser_contextINS0_5nil_tEEEEES6_NS0_7scannerINS0_10multi_passISt16istream_i
+ */
+
+. Allow optional addr information loading, speeding up some apps that don't
+ use such addresses (or in modes where addrs aren't used) such as pahole.
+
+. Use a obstacks, speeding up apps as measured with the perf tools.
+
+. Support zero sized arrays in the middle of a struct.
+
+. Fix padding calculation in the reorganize routines.
+
+. Fix bitfield demotion in the reorganize routines.
+
+. Support "using" pointing to data members (C++).
+
+. Properly support pointers to const, reported by Jan Engelhardt <jengelh@medozas.de>:
+
+. Support more compact DW_AT_data_member_location form, pointed out by Mark
+ Wielaard <mjw@redhat.com> and reported by Mike Snitzer <snitzer@redhat.com>
+
+Experimental CTF support:
+
+libdwarves was reorganized so that it can support multiple debugging formats,
+with the first one being supported being the Compact C Type Format that comes
+from the OpenSolaris world.
+
+David S. Miller contributed an initial CTF decoder and from there I wrote an
+encoder.
+
+To test this a regression testing harness (regtest in the sources) that will
+take files with DWARF info and from there encode its contents in CTF in another
+ELF section in the same file (.SUN_ctf). Then it will decode both the DWARF
+and CTF sections and compare the results for pahole running with some new
+flags that cope with some subtleties in the way CTF encodes things.
+
+ --flat_arrays
+
+ We have just one dimension in CTF, with the total number of entries,
+ in DWARF we can express this, but not in CTF:
+
+ __u8 addr[0][6]; /* 4 0 */
+
+ So --flat_arrays will show it as:
+
+ __u8 addr[0]; /* 4 0 */
+
+ --show_private_classes
+ --fixup_silly_bitfields
+
+ To cope with things like 'char foo:8' that since CTF has only the
+ number of bits, can't be expressed as we don't know if it is a
+ bitfield or just a char without the ':8' suffix.
+
+ --first_obj_only
+
+ Look only at the first object file in a file with multiple object
+ files, like vmlinux. This is because the CTF support is not complete yet,
+ needing the merging of types in multiple object files to be done.
+
+ --classes_as_structs
+
+ CTF has only a type for structs, not for classes like DWARF (DW_TAG_class_type
+ is not present in CTF), so print them all as 'struct'.
+
+Running with the above limitations produce just a few mismatches, related to
+packed structs and enumerations and bitfields.
diff --git a/README b/README
new file mode 100644
index 0000000..e711ba4
--- /dev/null
+++ b/README
@@ -0,0 +1,27 @@
+Build instructions:
+
+1. install cmake
+2. mkdir build
+3. cd build
+4. cmake -D__LIB=lib ..
+5. make install
+
+Default is to be installed on /usr/local, see rpm spec file for
+installing on other places.
+
+Known to work scenarios:
+
+Mandriva Cooker:
+
+cmake 2.4.5-1mdv2007.1
+libelfutils1-devel 0.123-1mdv2007.1
+
+Debian Unstable:
+
+cmake 2.4.5-1
+libdw-dev 0.123-2
+
+Fedora Core 6:
+
+cmake 2.4.5-2.fc6
+elfutils-devel 0.126-1.fc6
diff --git a/README.DEBUG b/README.DEBUG
new file mode 100644
index 0000000..4432d45
--- /dev/null
+++ b/README.DEBUG
@@ -0,0 +1,6 @@
+rm -rf build
+mkdir build
+cd build
+cmake -DCMAKE_BUILD_TYPE=Debug ..
+cd ..
+make -C build
diff --git a/README.btf b/README.btf
new file mode 100644
index 0000000..c81734e
--- /dev/null
+++ b/README.btf
@@ -0,0 +1,598 @@
+We'll test the BTF encoder using perf's eBPF integration, but really we can plain
+use clang directly, setting up all its options.
+
+Using perf's integration will save some time here, to see all it does, use
+'perf trace -vv' plus the options used below, then all the steps will be shown.
+
+Build perf from the latest kernel sources, use it with clang/llvm like:
+
+ [root@seventh ~]# clang --version
+ clang version 8.0.0 (http://llvm.org/git/clang.git 8587270a739ee30c926a76d5657e65e85b560f6e) (http://llvm.org/git/llvm.git 0566eefef9c3777bd780ec4cbb9efa764633b76c)
+ Target: x86_64-unknown-linux-gnu
+ Thread model: posix
+ InstalledDir: /usr/local/bin
+ [root@seventh ~]# llc --version | head -17
+ LLVM (http://llvm.org/):
+ LLVM version 8.0.0svn
+ DEBUG build with assertions.
+ Default target: x86_64-unknown-linux-gnu
+ Host CPU: skylake
+
+ Registered Targets:
+ aarch64 - AArch64 (little endian)
+ aarch64_be - AArch64 (big endian)
+ amdgcn - AMD GCN GPUs
+ arm - ARM
+ arm64 - ARM64 (little endian)
+ armeb - ARM (big endian)
+ bpf - BPF (host endian)
+ bpfeb - BPF (big endian)
+ bpfel - BPF (little endian)
+ hexagon - Hexagon
+ [root@seventh ~]#
+
+Then enable saving the object file build as part of perf's handling of foo.c type
+events, i.e. eBPF programs that will be compiled with clang and then loaded with
+sys_bpf() to possibly insert events in perf's ring buffer via bpf_perf_event_output(),
+or interact with the system via bpf_trace_printk() or just work as filters, etc:
+
+ # cat ~/.perfconfig
+ [llvm]
+ dump-obj = true
+
+Then run a simple example, found in the kernel sources:
+
+ # perf trace -e tools/perf/examples/bpf/hello.c cat /etc/passwd > /dev/null
+ LLVM: dumping tools/perf/examples/bpf/hello.o
+ 0.000 __bpf_stdout__:Hello, world
+ 0.028 __bpf_stdout__:Hello, world
+ 0.291 __bpf_stdout__:Hello, world
+ #
+
+Notice that "LLVM: dumping..." line, look at the ELF sections in that file:
+
+ [root@seventh perf]# readelf -SW tools/perf/examples/bpf/hello.o
+ There are 11 section headers, starting at offset 0x220:
+
+ Section Headers:
+ [Nr] Name Type Address Off Size ES Flg Lk Inf Al
+ [ 0] NULL 0000000000000000 000000 000000 00 0 0 0
+ [ 1] .strtab STRTAB 0000000000000000 00018c 00008d 00 0 0 1
+ [ 2] .text PROGBITS 0000000000000000 000040 000000 00 AX 0 0 4
+ [ 3] syscalls:sys_enter_openat PROGBITS 0000000000000000 000040 000088 00 AX 0 0 8
+ [ 4] .relsyscalls:sys_enter_openat REL 0000000000000000 000178 000010 10 10 3 8
+ [ 5] maps PROGBITS 0000000000000000 0000c8 00001c 00 WA 0 0 4
+ [ 6] .rodata.str1.1 PROGBITS 0000000000000000 0000e4 00000e 01 AMS 0 0 1
+ [ 7] license PROGBITS 0000000000000000 0000f2 000004 00 WA 0 0 1
+ [ 8] version PROGBITS 0000000000000000 0000f8 000004 00 WA 0 0 4
+ [ 9] .llvm_addrsig LOOS+0xfff4c03 0000000000000000 000188 000004 00 E 10 0 1
+ [10] .symtab SYMTAB 0000000000000000 000100 000078 18 1 1 8
+ Key to Flags:
+ W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
+ L (link order), O (extra OS processing required), G (group), T (TLS),
+ C (compressed), x (unknown), o (OS specific), E (exclude),
+ p (processor specific)
+ [root@seventh perf]#
+
+No DWARF debugging info, so we need to further customize ~/.perfconfig LLVM section:
+
+ [root@seventh perf]# cat ~/.perfconfig
+ [llvm]
+ dump-obj = true
+ clang-opt = -g
+ [root@seventh perf]# perf trace -e tools/perf/examples/bpf/hello.c cat /etc/passwd > /dev/null
+ LLVM: dumping tools/perf/examples/bpf/hello.o
+ 0.000 __bpf_stdout__:Hello, world
+ 0.015 __bpf_stdout__:Hello, world
+ 0.184 __bpf_stdout__:Hello, world
+ [root@seventh perf]#
+ [root@seventh perf]# readelf -SW tools/perf/examples/bpf/hello.o
+ There are 26 section headers, starting at offset 0xe20:
+
+ Section Headers:
+ [Nr] Name Type Address Off Size ES Flg Lk Inf Al
+ [ 0] NULL 0000000000000000 000000 000000 00 0 0 0
+ [ 1] .strtab STRTAB 0000000000000000 000cf4 000127 00 0 0 1
+ [ 2] .text PROGBITS 0000000000000000 000040 000000 00 AX 0 0 4
+ [ 3] syscalls:sys_enter_openat PROGBITS 0000000000000000 000040 000088 00 AX 0 0 8
+ [ 4] .relsyscalls:sys_enter_openat REL 0000000000000000 000a80 000010 10 25 3 8
+ [ 5] maps PROGBITS 0000000000000000 0000c8 00001c 00 WA 0 0 4
+ [ 6] .rodata.str1.1 PROGBITS 0000000000000000 0000e4 00000e 01 AMS 0 0 1
+ [ 7] license PROGBITS 0000000000000000 0000f2 000004 00 WA 0 0 1
+ [ 8] version PROGBITS 0000000000000000 0000f8 000004 00 WA 0 0 4
+ [ 9] .debug_str PROGBITS 0000000000000000 0000fc 0001d2 01 MS 0 0 1
+ [10] .debug_loc PROGBITS 0000000000000000 0002ce 000023 00 0 0 1
+ [11] .debug_abbrev PROGBITS 0000000000000000 0002f1 0000e3 00 0 0 1
+ [12] .debug_info PROGBITS 0000000000000000 0003d4 000182 00 0 0 1
+ [13] .rel.debug_info REL 0000000000000000 000a90 000210 10 25 12 8
+ [14] .debug_ranges PROGBITS 0000000000000000 000556 000030 00 0 0 1
+ [15] .debug_macinfo PROGBITS 0000000000000000 000586 000001 00 0 0 1
+ [16] .debug_pubnames PROGBITS 0000000000000000 000587 00006e 00 0 0 1
+ [17] .rel.debug_pubnames REL 0000000000000000 000ca0 000010 10 25 16 8
+ [18] .debug_pubtypes PROGBITS 0000000000000000 0005f5 000056 00 0 0 1
+ [19] .rel.debug_pubtypes REL 0000000000000000 000cb0 000010 10 25 18 8
+ [20] .debug_frame PROGBITS 0000000000000000 000650 000028 00 0 0 8
+ [21] .rel.debug_frame REL 0000000000000000 000cc0 000020 10 25 20 8
+ [22] .debug_line PROGBITS 0000000000000000 000678 0000a7 00 0 0 1
+ [23] .rel.debug_line REL 0000000000000000 000ce0 000010 10 25 22 8
+ [24] .llvm_addrsig LOOS+0xfff4c03 0000000000000000 000cf0 000004 00 E 25 0 1
+ [25] .symtab SYMTAB 0000000000000000 000720 000360 18 1 32 8
+ Key to Flags:
+ W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
+ L (link order), O (extra OS processing required), G (group), T (TLS),
+ C (compressed), x (unknown), o (OS specific), E (exclude),
+ p (processor specific)
+ [root@seventh perf]#
+
+Now lets use 'pahole --btf_encode' (or 'pahole -J') to add an ELF section to that object
+file with the conversion from the DWARF sections to a new one, for BTF:
+
+ [root@seventh perf]# pahole --btf_encode tools/perf/examples/bpf/hello.o
+
+ [root@seventh perf]# readelf -SW tools/perf/examples/bpf/hello.o
+ There are 27 section headers, starting at offset 0x1080:
+
+ Section Headers:
+ [Nr] Name Type Address Off Size ES Flg Lk Inf Al
+ [ 0] NULL 0000000000000000 000000 000000 00 0 0 0
+ [ 1] .text PROGBITS 0000000000000000 000040 000000 00 AX 0 0 4
+ [ 2] syscalls:sys_enter_openat PROGBITS 0000000000000000 000040 000088 00 AX 0 0 8
+ [ 3] maps PROGBITS 0000000000000000 0000c8 00001c 00 WA 0 0 4
+ [ 4] .rodata.str1.1 PROGBITS 0000000000000000 0000e4 00000e 01 AMS 0 0 1
+ [ 5] license PROGBITS 0000000000000000 0000f2 000004 00 WA 0 0 1
+ [ 6] version PROGBITS 0000000000000000 0000f8 000004 00 WA 0 0 4
+ [ 7] .debug_str PROGBITS 0000000000000000 0000fc 0001d2 01 MS 0 0 1
+ [ 8] .debug_loc PROGBITS 0000000000000000 0002ce 000023 00 0 0 1
+ [ 9] .debug_abbrev PROGBITS 0000000000000000 0002f1 0000e3 00 0 0 1
+ [10] .debug_info PROGBITS 0000000000000000 0003d4 000182 00 0 0 1
+ [11] .debug_ranges PROGBITS 0000000000000000 000556 000030 00 0 0 1
+ [12] .debug_macinfo PROGBITS 0000000000000000 000586 000001 00 0 0 1
+ [13] .debug_pubnames PROGBITS 0000000000000000 000587 00006e 00 0 0 1
+ [14] .debug_pubtypes PROGBITS 0000000000000000 0005f5 000056 00 0 0 1
+ [15] .debug_frame PROGBITS 0000000000000000 000650 000028 00 0 0 8
+ [16] .debug_line PROGBITS 0000000000000000 000678 0000a7 00 0 0 1
+ [17] .symtab SYMTAB 0000000000000000 000720 000360 18 25 32 8
+ [18] .relsyscalls:sys_enter_openat REL 0000000000000000 000a80 000010 10 17 2 8
+ [19] .rel.debug_info REL 0000000000000000 000a90 000210 10 17 10 8
+ [20] .rel.debug_pubnames REL 0000000000000000 000ca0 000010 10 17 13 8
+ [21] .rel.debug_pubtypes REL 0000000000000000 000cb0 000010 10 17 14 8
+ [22] .rel.debug_frame REL 0000000000000000 000cc0 000020 10 17 15 8
+ [23] .rel.debug_line REL 0000000000000000 000ce0 000010 10 17 16 8
+ [24] .llvm_addrsig LOOS+0xfff4c03 0000000000000000 000cf0 000004 00 E 0 0 1
+ [25] .strtab STRTAB 0000000000000000 000cf4 00019c 00 0 0 1
+ [26] .BTF PROGBITS 0000000000000000 000e90 0001ea 00 0 0 1
+ Key to Flags:
+ W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
+ L (link order), O (extra OS processing required), G (group), T (TLS),
+ C (compressed), x (unknown), o (OS specific), E (exclude),
+ p (processor specific)
+ readelf: tools/perf/examples/bpf/hello.o: Warning: possibly corrupt ELF header - it has a non-zero program header offset, but no program headers
+ [root@seventh perf]#
+
+That new ".BTF" section should then be parseable by the kernel, that has a BTF
+decoder, something not available for pahole at this time, but that will come in
+a later version.
+
+When pahole tries to read the DWARF info in that BPF ELF file, hello.o, we can se
+a problem that will require us to add another option to the .perfconfig llvm section:
+
+ # pahole tools/perf/examples/bpf/hello.o
+ struct clang version 8.0.0 (http://llvm.org/git/clang.git 8587270a739ee30c926a76d5657e65e85b560f6e) (http://llvm.org/git/llvm.git 0566eefef9c3777bd780ec4cbb9efa764633b76c) {
+ clang version 8.0.0 (http://llvm.org/git/clang.git 8587270a739ee30c926a76d5657e65e85b560f6e) (http://llvm.org/git/llvm.git 0566ec377 clang version 8.0.0 (http://llvm.org/git/clang.git 8587270a739ee30c926a76d5657e65e85b560f6e) (http://llvm.org/git/llvm.git 0566eefef9c3777bd780ec4cbb9efa764633b76c); /* 0 4 */
+ clang version 8.0.0 (http://llvm.org/git/clang.git 8587270a739ee30c926a76d5657e65e85b560f6e) (http://llvm.org/git/llvm.git 0566ec377 clang version 8.0.0 (http://llvm.org/git/clang.git 8587270a739ee30c926a76d5657e65e85b560f6e) (http://llvm.org/git/llvm.git 0566eefef9c3777bd780ec4cbb9efa764633b76c); /* 4 4 */
+ clang version 8.0.0 (http://llvm.org/git/clang.git 8587270a739ee30c926a76d5657e65e85b560f6e) (http://llvm.org/git/llvm.git 0566ec377 clang version 8.0.0 (http://llvm.org/git/clang.git 8587270a739ee30c926a76d5657e65e85b560f6e) (http://llvm.org/git/llvm.git 0566eefef9c3777bd780ec4cbb9efa764633b76c); /* 8 4 */
+ clang version 8.0.0 (http://llvm.org/git/clang.git 8587270a739ee30c926a76d5657e65e85b560f6e) (http://llvm.org/git/llvm.git 0566ec377 clang version 8.0.0 (http://llvm.org/git/clang.git 8587270a739ee30c926a76d5657e65e85b560f6e) (http://llvm.org/git/llvm.git 0566eefef9c3777bd780ec4cbb9efa764633b76c); /* 12 4 */
+ clang version 8.0.0 (http://llvm.org/git/clang.git 8587270a739ee30c926a76d5657e65e85b560f6e) (http://llvm.org/git/llvm.git 0566ec377 clang version 8.0.0 (http://llvm.org/git/clang.git 8587270a739ee30c926a76d5657e65e85b560f6e) (http://llvm.org/git/llvm.git 0566eefef9c3777bd780ec4cbb9efa764633b76c); /* 16 4 */
+ clang version 8.0.0 (http://llvm.org/git/clang.git 8587270a739ee30c926a76d5657e65e85b560f6e) (http://llvm.org/git/llvm.git 0566ec377 clang version 8.0.0 (http://llvm.org/git/clang.git 8587270a739ee30c926a76d5657e65e85b560f6e) (http://llvm.org/git/llvm.git 0566eefef9c3777bd780ec4cbb9efa764633b76c); /* 20 4 */
+ clang version 8.0.0 (http://llvm.org/git/clang.git 8587270a739ee30c926a76d5657e65e85b560f6e) (http://llvm.org/git/llvm.git 0566ec377 clang version 8.0.0 (http://llvm.org/git/clang.git 8587270a739ee30c926a76d5657e65e85b560f6e) (http://llvm.org/git/llvm.git 0566eefef9c3777bd780ec4cbb9efa764633b76c); /* 24 4 */
+
+ /* size: 28, cachelines: 1, members: 7 */
+ /* last cacheline: 28 bytes */
+ };
+#
+
+We need to pass some options to llvm, via the llvm.opts variable in ~/.perfconfig:
+
+ [root@seventh perf]# cat ~/.perfconfig
+ [llvm]
+ dump-obj = true
+ clang-opt = -g
+ opts = -mattr=dwarfris
+ [root@seventh perf]# perf trace -e tools/perf/examples/bpf/hello.c cat /etc/passwd > /dev/null
+ LLVM: dumping tools/perf/examples/bpf/hello.o
+ 0.000 __bpf_stdout__:Hello, world
+ 0.018 __bpf_stdout__:Hello, world
+ 0.209 __bpf_stdout__:Hello, world
+ [root@seventh perf]# pahole tools/perf/examples/bpf/hello.o
+ struct bpf_map {
+ unsigned int type; /* 0 4 */
+ unsigned int key_size; /* 4 4 */
+ unsigned int value_size; /* 8 4 */
+ unsigned int max_entries; /* 12 4 */
+ unsigned int map_flags; /* 16 4 */
+ unsigned int inner_map_idx; /* 20 4 */
+ unsigned int numa_node; /* 24 4 */
+
+ /* size: 28, cachelines: 1, members: 7 */
+ /* last cacheline: 28 bytes */
+ };
+ [root@seventh perf]#
+
+This is not needed when using elfutils >= 0.173, pahole will just work as above.
+
+Now we need to go test the kernel, and to load that file with a BTF section we
+can also use perf, passing the .o file instead of the .c one, skipping the
+compilation phase and using the modified .o file, we will also run in system
+wide mode, so taht we can keep that BPF object loaded and attached to the
+tracepoint, so that we can use the kernel facilities to inspect the BTF file as
+read and processed by the kernel:
+
+ # perf trace -e tools/perf/examples/bpf/hello.c 2> /dev/null
+
+Now to look if the kernel has the bpf filesystem:
+
+ [acme@jouet perf]$ grep bpf /proc/filesystems
+ nodev bpf
+ [acme@jouet perf]$
+ [root@jouet ~]# mount -t bpf nodev /sys/fs/bpf
+ [root@jouet ~]# mount | grep bpf
+ nodev on /sys/fs/bpf type bpf (rw,relatime)
+ [root@jouet ~]# cd /sys/fs/bpf
+ [root@jouet bpf]# ls -la
+ total 0
+ drwxrwxrwt. 2 root root 0 Aug 15 17:42 .
+ drwxr-xr-x. 10 root root 0 Aug 13 15:04 ..
+ [root@jouet bpf]#
+
+Work is planned to allow using BTF info to pretty print from the bpf fs, see:
+
+ https://www.spinics.net/lists/netdev/msg518606.html
+ Date: Sat, 11 Aug 2018
+
+<quote>
+For bpftool, BTF pretty print support is missing
+for per-cpu maps. bpffs print for per-cpu hash/array maps
+need to be added as well. Will add them later.
+
+Acked-by: Yonghong Song <yhs@xxxxxx>
+</quote>
+
+To see what libbpf and its users, like perf, does when a ".BTF" ELF section is
+found in a BPF object being loaded via sys_bpf(), we can use 'perf ftrace' to
+show the sequence of events inside the kernel to load, validade and initialize
+data structures related to the request:
+
+ # perf ftrace -G *btf* perf trace -e tools/perf/examples/bpf/hello.o cat /etc/passwd
+ 3) | bpf_btf_load() {
+ 3) | capable() {
+ 3) | ns_capable_common() {
+ 3) | security_capable() {
+ 3) 0.048 us | cap_capable();
+ 3) | selinux_capable() {
+ 3) 0.092 us | cred_has_capability();
+ 3) 0.444 us | }
+ 3) 1.387 us | }
+ 3) 1.764 us | }
+ 3) 2.168 us | }
+ 3) | btf_new_fd() {
+ 3) | kmem_cache_alloc_trace() {
+ 3) | _cond_resched() {
+ 3) 0.041 us | rcu_all_qs();
+ 3) 0.407 us | }
+ 3) 0.040 us | should_failslab();
+ 3) 0.161 us | prefetch_freepointer();
+ 3) 0.097 us | memcg_kmem_put_cache();
+ 3) 2.719 us | }
+ 3) | kmem_cache_alloc_trace() {
+ 3) | _cond_resched() {
+ 3) 0.040 us | rcu_all_qs();
+ 3) 0.409 us | }
+ 3) 0.040 us | should_failslab();
+ 3) 0.110 us | prefetch_freepointer();
+ 3) 0.099 us | memcg_kmem_put_cache();
+ 3) 2.296 us | }
+ 3) 0.054 us | bpf_check_uarg_tail_zero();
+ 3) | __check_object_size() {
+ 3) 0.152 us | __virt_addr_valid();
+ 3) 0.047 us | __check_heap_object();
+ 3) 0.040 us | check_stack_object();
+ 3) 1.465 us | }
+ 3) 0.041 us | btf_sec_info_cmp();
+ 3) | kvmalloc_node() {
+ 3) | __kmalloc_node() {
+ 3) 0.051 us | kmalloc_slab();
+ 3) | _cond_resched() {
+ 3) 0.042 us | rcu_all_qs();
+ 3) 0.401 us | }
+ 3) 0.038 us | should_failslab();
+ 3) 0.040 us | memcg_kmem_put_cache();
+ 3) 2.168 us | }
+ 3) 2.591 us | }
+ 3) | __check_object_size() {
+ 3) 0.108 us | __virt_addr_valid();
+ 3) 0.050 us | __check_heap_object();
+ 3) 0.039 us | check_stack_object();
+ 3) 1.469 us | }
+ 3) | btf_struct_check_meta() {
+ 3) 0.057 us | __btf_verifier_log_type();
+ 3) 0.057 us | btf_verifier_log_member();
+ 3) 0.043 us | btf_verifier_log_member();
+ 3) 0.042 us | btf_verifier_log_member();
+ 3) 0.043 us | btf_verifier_log_member();
+ 3) 0.043 us | btf_verifier_log_member();
+ 3) | btf_verifier_log_member() {
+ 3) ==========> |
+ 3) | smp_irq_work_interrupt() {
+ 3) | irq_enter() {
+ 3) | rcu_irq_enter() {
+ 3) 0.038 us | rcu_nmi_enter();
+ 3) 0.412 us | }
+ 3) 0.054 us | irqtime_account_irq();
+ 3) 1.409 us | }
+ 3) | __wake_up() {
+ 3) | __wake_up_common_lock() {
+ 3) 0.040 us | _raw_spin_lock_irqsave();
+ 3) 0.051 us | __wake_up_common();
+ 3) 0.044 us | _raw_spin_unlock_irqrestore();
+ 3) 1.155 us | }
+ 3) 1.508 us | }
+ 3) | irq_exit() {
+ 3) 0.062 us | irqtime_account_irq();
+ 3) 0.038 us | idle_cpu();
+ 3) | rcu_irq_exit() {
+ 3) 0.038 us | rcu_nmi_exit();
+ 3) 0.419 us | }
+ 3) 1.601 us | }
+ 3) 6.230 us | }
+ 3) <========== |
+ 3) 0.088 us | } /* btf_verifier_log_member */
+ 3) 0.041 us | btf_verifier_log_member();
+ 3) + 10.759 us | }
+ 3) | kvmalloc_node() {
+ 3) | __kmalloc_node() {
+ 3) 0.043 us | kmalloc_slab();
+ 3) | _cond_resched() {
+ 3) 0.037 us | rcu_all_qs();
+ 3) 0.455 us | }
+ 3) 0.040 us | should_failslab();
+ 3) 0.037 us | memcg_kmem_put_cache();
+ 3) 2.227 us | }
+ 3) 2.624 us | } /* kvmalloc_node */
+ 3) | kvfree() {
+ 3) 0.048 us | kfree();
+ 3) 0.662 us | }
+ 3) | btf_int_check_meta() {
+ 3) 0.043 us | __btf_verifier_log_type();
+ 3) 0.457 us | }
+ 3) | btf_array_check_meta() {
+ 3) 0.041 us | __btf_verifier_log_type();
+ 3) 0.393 us | }
+ 3) | btf_int_check_meta() {
+ 3) 0.094 us | __btf_verifier_log_type();
+ 3) 0.447 us | }
+ 3) | btf_int_check_meta() {
+ 3) 0.043 us | __btf_verifier_log_type();
+ 3) 0.573 us | }
+ 3) | btf_int_check_meta() {
+ 3) 0.085 us | __btf_verifier_log_type();
+ 3) 0.446 us | }
+ 3) | btf_ref_type_check_meta() {
+ 3) 0.042 us | __btf_verifier_log_type();
+ 3) 0.451 us | }
+ 3) | btf_ref_type_check_meta() {
+ 3) 0.042 us | __btf_verifier_log_type();
+ 3) 0.427 us | }
+ 3) | btf_ref_type_check_meta() {
+ 3) 0.042 us | __btf_verifier_log_type();
+ 3) 0.397 us | }
+ 3) | btf_ref_type_check_meta() {
+ 3) 0.041 us | __btf_verifier_log_type();
+ 3) 0.399 us | }
+ 3) | btf_int_check_meta() {
+ 3) 0.043 us | __btf_verifier_log_type();
+ 3) 0.602 us | }
+ 3) | btf_ref_type_check_meta() {
+ 3) 0.040 us | __btf_verifier_log_type();
+ 3) 0.733 us | }
+ 3) | btf_array_check_meta() {
+ 3) 0.094 us | __btf_verifier_log_type();
+ 3) 0.452 us | }
+ 3) | kvmalloc_node() {
+ 3) | __kmalloc_node() {
+ 3) 0.039 us | kmalloc_slab();
+ 3) | _cond_resched() {
+ 3) 0.041 us | rcu_all_qs();
+ 3) 0.579 us | }
+ 3) 0.039 us | should_failslab();
+ 3) 0.042 us | memcg_kmem_put_cache();
+ 3) 2.538 us | }
+ 3) 2.886 us | }
+ 3) | kvmalloc_node() {
+ 3) | __kmalloc_node() {
+ 3) 0.041 us | kmalloc_slab();
+ 3) | _cond_resched() {
+ 3) 0.038 us | rcu_all_qs();
+ 3) 0.708 us | }
+ 3) 0.038 us | should_failslab();
+ 3) 0.040 us | memcg_kmem_put_cache();
+ 3) 2.483 us | }
+ 3) 2.829 us | }
+ 3) | kvmalloc_node() {
+ 3) | __kmalloc_node() {
+ 3) 0.057 us | kmalloc_slab();
+ 3) | _cond_resched() {
+ 3) 0.040 us | rcu_all_qs();
+ 3) 0.533 us | }
+ 3) 0.039 us | should_failslab();
+ 3) 0.038 us | memcg_kmem_put_cache();
+ 3) 2.680 us | }
+ 3) 3.171 us | }
+ 3) 0.054 us | env_stack_push();
+ 3) | btf_struct_resolve() {
+ 3) 0.051 us | env_type_is_resolve_sink.isra.19();
+ 3) 0.039 us | btf_int_check_member();
+ 3) 0.039 us | env_type_is_resolve_sink.isra.19();
+ 3) 0.039 us | btf_int_check_member();
+ 3) 0.040 us | env_type_is_resolve_sink.isra.19();
+ 3) 0.040 us | btf_int_check_member();
+ 3) 0.039 us | env_type_is_resolve_sink.isra.19();
+ 3) 0.099 us | btf_int_check_member();
+ 3) 0.040 us | env_type_is_resolve_sink.isra.19();
+ 3) 0.042 us | btf_int_check_member();
+ 3) 0.040 us | env_type_is_resolve_sink.isra.19();
+ 3) 0.038 us | btf_int_check_member();
+ 3) 0.038 us | env_type_is_resolve_sink.isra.19();
+ 3) 0.039 us | btf_int_check_member();
+ 3) 6.545 us | }
+ 3) 0.053 us | env_stack_push();
+ 3) | btf_array_resolve() {
+ 3) 0.039 us | env_type_is_resolve_sink.isra.19();
+ 3) 0.090 us | btf_type_id_size();
+ 3) 0.060 us | btf_type_int_is_regular();
+ 3) 0.058 us | env_type_is_resolve_sink.isra.19();
+ 3) 0.051 us | btf_type_id_size();
+ 3) 0.055 us | btf_type_int_is_regular();
+ 3) 3.414 us | }
+ 3) 0.041 us | btf_type_id_size();
+ 3) 0.057 us | env_stack_push();
+ 3) | btf_ptr_resolve() {
+ 3) 0.056 us | env_type_is_resolve_sink.isra.19();
+ 3) 0.054 us | env_stack_push();
+ 3) 1.056 us | }
+ 3) 0.063 us | btf_ptr_resolve();
+ 3) | btf_ptr_resolve() {
+ 3) 0.049 us | env_type_is_resolve_sink.isra.19();
+ 3) 0.086 us | btf_type_id_size();
+ 3) 1.052 us | }
+ 3) 0.045 us | env_stack_push();
+ 3) 0.060 us | btf_ptr_resolve();
+ 3) 0.045 us | env_stack_push();
+ 3) | btf_ptr_resolve() {
+ 3) 0.039 us | env_type_is_resolve_sink.isra.19();
+ 3) 0.062 us | btf_type_id_size();
+ 3) 1.325 us | }
+ 3) 0.054 us | env_stack_push();
+ 3) | btf_modifier_resolve() {
+ 3) 0.061 us | env_type_is_resolve_sink.isra.19();
+ 3) 0.043 us | btf_type_id_size();
+ 3) 0.877 us | }
+ 3) 0.052 us | env_stack_push();
+ 3) | btf_array_resolve() {
+ 3) 0.060 us | env_type_is_resolve_sink.isra.19();
+ 3) 0.051 us | btf_type_id_size();
+ 3) 0.042 us | btf_type_int_is_regular();
+ 3) 0.040 us | env_type_is_resolve_sink.isra.19();
+ 3) 0.042 us | btf_type_id_size();
+ 3) 0.041 us | btf_type_int_is_regular();
+ 3) 2.822 us | }
+ 3) 0.048 us | btf_type_id_size();
+ 3) | kvfree() {
+ 3) 0.148 us | kfree();
+ 3) 0.685 us | }
+ 3) 0.287 us | kfree();
+ 3) 0.042 us | _raw_spin_lock_bh();
+ 3) | kmem_cache_alloc() {
+ 3) 0.040 us | should_failslab();
+ 3) 0.111 us | prefetch_freepointer();
+ 3) 0.094 us | memcg_kmem_put_cache();
+ 3) 2.139 us | }
+ 3) | _raw_spin_unlock_bh() {
+ 3) 0.079 us | __local_bh_enable_ip();
+ 3) 0.460 us | }
+ 3) | anon_inode_getfd() {
+ 3) | get_unused_fd_flags() {
+ 3) | __alloc_fd() {
+ 3) 0.040 us | _raw_spin_lock();
+ 3) 0.041 us | expand_files();
+ 3) 1.374 us | }
+ 3) 1.759 us | }
+ 3) | anon_inode_getfile() {
+ 3) | d_alloc_pseudo() {
+ 3) | __d_alloc() {
+ 3) | kmem_cache_alloc() {
+ 3) | _cond_resched() {
+ 3) 0.035 us | rcu_all_qs();
+ 3) 0.507 us | }
+ 3) 0.040 us | should_failslab();
+ 3) | memcg_kmem_get_cache() {
+ 3) 0.091 us | get_mem_cgroup_from_mm();
+ 3) 0.633 us | }
+ 3) 0.111 us | prefetch_freepointer();
+ 3) 0.082 us | memcg_kmem_put_cache();
+ 3) 4.178 us | }
+ 3) 0.162 us | d_set_d_op();
+ 3) 5.545 us | }
+ 3) 6.270 us | }
+ 3) 0.112 us | mntget();
+ 3) 0.125 us | ihold();
+ 3) | d_instantiate() {
+ 3) 0.120 us | security_d_instantiate();
+ 3) 0.106 us | _raw_spin_lock();
+ 3) | __d_instantiate() {
+ 3) 0.069 us | d_flags_for_inode();
+ 3) 0.090 us | _raw_spin_lock();
+ 3) 1.483 us | }
+ 3) 2.767 us | }
+ 3) | alloc_file() {
+ 3) | get_empty_filp() {
+ 3) | kmem_cache_alloc() {
+ 3) | _cond_resched() {
+ 3) 0.039 us | rcu_all_qs();
+ 3)root:x:0:0:root:/root:/bin/bash
+ bin:x:1:1:bin:/bin:/sbin/nologin
+ daemon:x:2:2:daemon:/sbin:/sbin/nologin
+ adm:x:3:4:adm:/var/adm:/sbin/nologin
+ <SNIP rest of /proc/passwd contents>
+ 0.382 us | }
+ 3) 0.040 us | should_failslab();
+ 3) | memcg_kmem_get_cache() {
+ 3) 0.039 us | get_mem_cgroup_from_mm();
+ 3) 0.626 us | }
+ 3) 0.050 us | prefetch_freepointer();
+ 3) 0.059 us | memcg_kmem_put_cache();
+ 3) 3.280 us | }
+ 3) | security_file_alloc() {
+ 3) | selinux_file_alloc_security() {
+ 3) | kmem_cache_alloc() {
+ 3) | _cond_resched() {
+ 3) 0.038 us | rcu_all_qs();
+ 3) 0.422 us | }
+ 3) 0.040 us | should_failslab();
+ 3) 0.051 us | prefetch_freepointer();
+ 3) 0.054 us | memcg_kmem_put_cache();
+ 3) 2.660 us | }
+ 3) 3.062 us | }
+ 3) 3.548 us | }
+ 3) 0.039 us | __mutex_init();
+ 3) 8.091 us | }
+ 3) 8.617 us | }
+ 3) + 20.810 us | }
+ 3) | fd_install() {
+ 3) 0.054 us | __fd_install();
+ 3) 0.723 us | }
+ 3) + 24.438 us | }
+ 3) ! 109.639 us | }
+ 3) ! 112.925 us | }
+ 3) | btf_release() {
+ 3) | btf_put() {
+ 3) 0.145 us | _raw_spin_lock_irqsave();
+ 3) | call_rcu_sched() {
+ 3) | __call_rcu() {
+ 3) 0.082 us | rcu_segcblist_enqueue();
+ 3) 1.323 us | }
+ 3) 1.782 us | }
+ 3) 0.069 us | _raw_spin_unlock_irqrestore();
+ 3) | call_rcu_sched() {
+ 3) | __call_rcu() {
+ 3) 0.069 us | rcu_segcblist_enqueue();
+ 3) 0.541 us | }
+ 3) 0.984 us | }
+ 3) 5.210 us | }
+ 3) 5.954 us | }
+
+This should be enough for us to validate pahole's BTF encoder, and now one can
+use 'pahole -F btf' to obtain mostly the same results as with the default use
+of '-F dwarf', modulo things like explicit alignments that are not present in
+BTF and need some work to be inferred from existing non-natural alignment holes.
+
+- Arnaldo
diff --git a/README.cross b/README.cross
new file mode 100644
index 0000000..1faa0e9
--- /dev/null
+++ b/README.cross
@@ -0,0 +1,7 @@
+CC=s390x-linux-gnu-gcc \
+ cmake -DDWARF_INCLUDE_DIR=/usr/s390x-linux-gnu/include/ \
+ -DLIBDW_INCLUDE_DIR=/usr/s390x-linux-gnu/include/ \
+ -DDWARF_LIBRARY=/usr/s390x-linux-gnu/lib/libdw.so.1 \
+ -DELF_LIBRARY=/usr/s390x-linux-gnu/lib/libelf.so.1 \
+ -DZLIB_LIBRARY=/usr/s390x-linux-gnu/lib/libz.so.1 \
+ -DZLIB_INCLUDE_DIR=/usr/s390x-linux-gnu/include/ ..
diff --git a/README.ctracer b/README.ctracer
new file mode 100644
index 0000000..33c4014
--- /dev/null
+++ b/README.ctracer
@@ -0,0 +1,57 @@
+Basic instructions to use ctracer:
+
+1. Install dwarves, if you are not that excited about building it I'm
+ keeping rpms for Fedora Core 6 here:
+
+ http://oops.ghostprotocols.net:81/acme/dwarves/rpm/
+
+ The .src.rpm is there in case you want to rebuild it for another
+ rpm based distro.
+
+ Since fedora 9 you just have to run:
+
+ yum install dwarves
+
+2. build the kernel with CONFIG_DEBUG_INFO=y, i.e. gcc -g, that will
+ insert the DWARF info needed by all the pahole tools, ctracer, etc, or
+ just install the kernel-debuginfo rpm package on FC6, other distros
+ have it with a different name, its just the kernel built with debug
+ info.
+
+3. Assuming you installed the kernel-debuginfo package, to run ctracer
+ on your workstation, just do the following steps:
+
+mkdir foo
+cd foo
+ln -s /usr/share/dwarves/runtime/* .
+make CLASS=sock # to trace struct sock methods, this one is safe, try others
+ # and tell me your horror (or success :-) ) story.
+
+(kbuild gurus, send suggestions to simplify this procedure! :-) )
+
+4. load the resulting module:
+
+insmod ctracer.ko
+
+dmesg will show how many probes were successfully installed
+
+5. Do some related activity (ssh, in the above example should do)
+
+6. Make sure debugfs is mounted
+
+[root@filo ~]# mount -t debugfs none_debugfs /sys/kernel/debug/
+
+7. Get the log:
+
+cat /sys/kernel/debug/ctracer0 > /tmp/ctracer.log
+
+8. Generate the callgraph!
+
+make callgraph
+
+9. rmmod ctracer
+
+Change the shipped Makefile accordingly to build a module for qemu or another test
+machine.
+
+The relay transport is mostly ready and will be included in the upcoming changesets.
diff --git a/README.tarball b/README.tarball
new file mode 100644
index 0000000..a3db289
--- /dev/null
+++ b/README.tarball
@@ -0,0 +1 @@
+tar cvfJ ~/rpmbuild/SOURCES/dwarves-1.17.tar.xz --transform 's,^,dwarves-1.17/,' `cat MANIFEST`
diff --git a/btf_encoder.c b/btf_encoder.c
new file mode 100644
index 0000000..c40f059
--- /dev/null
+++ b/btf_encoder.c
@@ -0,0 +1,721 @@
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2019 Facebook
+
+ Derived from ctf_encoder.c, which is:
+
+ Copyright (C) Arnaldo Carvalho de Melo <acme@redhat.com>
+ Copyright (C) Red Hat Inc
+ */
+
+#include "dwarves.h"
+#include "libbtf.h"
+#include "lib/bpf/include/uapi/linux/btf.h"
+#include "lib/bpf/src/libbpf.h"
+#include "hash.h"
+#include "elf_symtab.h"
+#include "btf_encoder.h"
+
+#include <ctype.h> /* for isalpha() and isalnum() */
+#include <stdlib.h> /* for qsort() and bsearch() */
+#include <inttypes.h>
+
+/*
+ * This corresponds to the same macro defined in
+ * include/linux/kallsyms.h
+ */
+#define KSYM_NAME_LEN 128
+
+struct funcs_layout {
+ unsigned long mcount_start;
+ unsigned long mcount_stop;
+ unsigned long mcount_sec_idx;
+};
+
+struct elf_function {
+ const char *name;
+ unsigned long addr;
+ bool generated;
+};
+
+static struct elf_function *functions;
+static int functions_alloc;
+static int functions_cnt;
+
+static int functions_cmp(const void *_a, const void *_b)
+{
+ const struct elf_function *a = _a;
+ const struct elf_function *b = _b;
+
+ return strcmp(a->name, b->name);
+}
+
+static void delete_functions(void)
+{
+ free(functions);
+ functions_alloc = functions_cnt = 0;
+ functions = NULL;
+}
+
+#ifndef max
+#define max(x, y) ((x) < (y) ? (y) : (x))
+#endif
+
+static int collect_function(struct btf_elf *btfe, GElf_Sym *sym)
+{
+ struct elf_function *new;
+
+ if (elf_sym__type(sym) != STT_FUNC)
+ return 0;
+ if (!elf_sym__value(sym))
+ return 0;
+
+ if (functions_cnt == functions_alloc) {
+ functions_alloc = max(1000, functions_alloc * 3 / 2);
+ new = realloc(functions, functions_alloc * sizeof(*functions));
+ if (!new) {
+ /*
+ * The cleanup - delete_functions is called
+ * in cu__encode_btf error path.
+ */
+ return -1;
+ }
+ functions = new;
+ }
+
+ functions[functions_cnt].name = elf_sym__name(sym, btfe->symtab);
+ functions[functions_cnt].addr = elf_sym__value(sym);
+ functions[functions_cnt].generated = false;
+ functions_cnt++;
+ return 0;
+}
+
+static int addrs_cmp(const void *_a, const void *_b)
+{
+ const unsigned long *a = _a;
+ const unsigned long *b = _b;
+
+ if (*a == *b)
+ return 0;
+ return *a < *b ? -1 : 1;
+}
+
+static int filter_functions(struct btf_elf *btfe, struct funcs_layout *fl)
+{
+ unsigned long *addrs, count, offset, i;
+ int functions_valid = 0;
+ Elf_Data *data;
+ GElf_Shdr shdr;
+ Elf_Scn *sec;
+
+ /*
+ * Find mcount addressed marked by __start_mcount_loc
+ * and __stop_mcount_loc symbols and load them into
+ * sorted array.
+ */
+ sec = elf_getscn(btfe->elf, fl->mcount_sec_idx);
+ if (!sec || !gelf_getshdr(sec, &shdr)) {
+ fprintf(stderr, "Failed to get section(%lu) header.\n",
+ fl->mcount_sec_idx);
+ return -1;
+ }
+
+ offset = fl->mcount_start - shdr.sh_addr;
+ count = (fl->mcount_stop - fl->mcount_start) / 8;
+
+ data = elf_getdata(sec, 0);
+ if (!data) {
+ fprintf(stderr, "Failed to get section(%lu) data.\n",
+ fl->mcount_sec_idx);
+ return -1;
+ }
+
+ addrs = malloc(count * sizeof(addrs[0]));
+ if (!addrs) {
+ fprintf(stderr, "Failed to allocate memory for ftrace addresses.\n");
+ return -1;
+ }
+
+ memcpy(addrs, data->d_buf + offset, count * sizeof(addrs[0]));
+ qsort(addrs, count, sizeof(addrs[0]), addrs_cmp);
+
+ /*
+ * Let's got through all collected functions and filter
+ * out those that are not in ftrace.
+ */
+ for (i = 0; i < functions_cnt; i++) {
+ struct elf_function *func = &functions[i];
+
+ /* Make sure function is within ftrace addresses. */
+ if (bsearch(&func->addr, addrs, count, sizeof(addrs[0]), addrs_cmp)) {
+ /*
+ * We iterate over sorted array, so we can easily skip
+ * not valid item and move following valid field into
+ * its place, and still keep the 'new' array sorted.
+ */
+ if (i != functions_valid)
+ functions[functions_valid] = functions[i];
+ functions_valid++;
+ }
+ }
+
+ functions_cnt = functions_valid;
+ free(addrs);
+ return 0;
+}
+
+static struct elf_function *find_function(const struct btf_elf *btfe,
+ const char *name)
+{
+ struct elf_function key = { .name = name };
+
+ return bsearch(&key, functions, functions_cnt, sizeof(functions[0]),
+ functions_cmp);
+}
+
+static bool btf_name_char_ok(char c, bool first)
+{
+ if (c == '_' || c == '.')
+ return true;
+
+ return first ? isalpha(c) : isalnum(c);
+}
+
+/* Check whether the given name is valid in vmlinux btf. */
+static bool btf_name_valid(const char *p)
+{
+ const char *limit;
+
+ if (!btf_name_char_ok(*p, true))
+ return false;
+
+ /* set a limit on identifier length */
+ limit = p + KSYM_NAME_LEN;
+ p++;
+ while (*p && p < limit) {
+ if (!btf_name_char_ok(*p, false))
+ return false;
+ p++;
+ }
+
+ return !*p;
+}
+
+static void dump_invalid_symbol(const char *msg, const char *sym,
+ int verbose, bool force)
+{
+ if (force) {
+ if (verbose)
+ fprintf(stderr, "PAHOLE: Warning: %s, ignored (sym: '%s').\n",
+ msg, sym);
+ return;
+ }
+
+ fprintf(stderr, "PAHOLE: Error: %s (sym: '%s').\n", msg, sym);
+ fprintf(stderr, "PAHOLE: Error: Use '--btf_encode_force' to ignore such symbols and force emit the btf.\n");
+}
+
+extern struct debug_fmt_ops *dwarves__active_loader;
+
+static int tag__check_id_drift(const struct tag *tag,
+ uint32_t core_id, uint32_t btf_type_id,
+ uint32_t type_id_off)
+{
+ if (btf_type_id != (core_id + type_id_off)) {
+ fprintf(stderr,
+ "%s: %s id drift, core_id: %u, btf_type_id: %u, type_id_off: %u\n",
+ __func__, dwarf_tag_name(tag->tag),
+ core_id, btf_type_id, type_id_off);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int32_t structure_type__encode(struct btf_elf *btfe, struct cu *cu, struct tag *tag, uint32_t type_id_off)
+{
+ struct type *type = tag__type(tag);
+ struct class_member *pos;
+ const char *name;
+ int32_t type_id;
+ uint8_t kind;
+
+ kind = (tag->tag == DW_TAG_union_type) ?
+ BTF_KIND_UNION : BTF_KIND_STRUCT;
+
+ name = dwarves__active_loader->strings__ptr(cu, type->namespace.name);
+ type_id = btf_elf__add_struct(btfe, kind, name, type->size);
+ if (type_id < 0)
+ return type_id;
+
+ type__for_each_data_member(type, pos) {
+ /*
+ * dwarf_loader uses DWARF's recommended bit offset addressing
+ * scheme, which conforms to BTF requirement, so no conversion
+ * is required.
+ */
+ name = dwarves__active_loader->strings__ptr(cu, pos->name);
+ if (btf_elf__add_member(btfe, name, type_id_off + pos->tag.type, pos->bitfield_size, pos->bit_offset))
+ return -1;
+ }
+
+ return type_id;
+}
+
+static uint32_t array_type__nelems(struct tag *tag)
+{
+ int i;
+ uint32_t nelem = 1;
+ struct array_type *array = tag__array_type(tag);
+
+ for (i = array->dimensions - 1; i >= 0; --i)
+ nelem *= array->nr_entries[i];
+
+ return nelem;
+}
+
+static int32_t enumeration_type__encode(struct btf_elf *btfe, struct cu *cu, struct tag *tag)
+{
+ struct type *etype = tag__type(tag);
+ struct enumerator *pos;
+ const char *name;
+ int32_t type_id;
+
+ name = dwarves__active_loader->strings__ptr(cu, etype->namespace.name);
+ type_id = btf_elf__add_enum(btfe, name, etype->size);
+ if (type_id < 0)
+ return type_id;
+
+ type__for_each_enumerator(etype, pos) {
+ name = dwarves__active_loader->strings__ptr(cu, pos->name);
+ if (btf_elf__add_enum_val(btfe, name, pos->value))
+ return -1;
+ }
+
+ return type_id;
+}
+
+static bool need_index_type;
+
+static int tag__encode_btf(struct cu *cu, struct tag *tag, uint32_t core_id, struct btf_elf *btfe,
+ uint32_t array_index_id, uint32_t type_id_off)
+{
+ /* single out type 0 as it represents special type "void" */
+ uint32_t ref_type_id = tag->type == 0 ? 0 : type_id_off + tag->type;
+ const char *name;
+
+ switch (tag->tag) {
+ case DW_TAG_base_type:
+ name = dwarves__active_loader->strings__ptr(cu, tag__base_type(tag)->name);
+ return btf_elf__add_base_type(btfe, tag__base_type(tag), name);
+ case DW_TAG_const_type:
+ return btf_elf__add_ref_type(btfe, BTF_KIND_CONST, ref_type_id, NULL, false);
+ case DW_TAG_pointer_type:
+ return btf_elf__add_ref_type(btfe, BTF_KIND_PTR, ref_type_id, NULL, false);
+ case DW_TAG_restrict_type:
+ return btf_elf__add_ref_type(btfe, BTF_KIND_RESTRICT, ref_type_id, NULL, false);
+ case DW_TAG_volatile_type:
+ return btf_elf__add_ref_type(btfe, BTF_KIND_VOLATILE, ref_type_id, NULL, false);
+ case DW_TAG_typedef:
+ name = dwarves__active_loader->strings__ptr(cu, tag__namespace(tag)->name);
+ return btf_elf__add_ref_type(btfe, BTF_KIND_TYPEDEF, ref_type_id, name, false);
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ case DW_TAG_class_type:
+ name = dwarves__active_loader->strings__ptr(cu, tag__namespace(tag)->name);
+ if (tag__type(tag)->declaration)
+ return btf_elf__add_ref_type(btfe, BTF_KIND_FWD, 0, name, tag->tag == DW_TAG_union_type);
+ else
+ return structure_type__encode(btfe, cu, tag, type_id_off);
+ case DW_TAG_array_type:
+ /* TODO: Encode one dimension at a time. */
+ need_index_type = true;
+ return btf_elf__add_array(btfe, ref_type_id, array_index_id, array_type__nelems(tag));
+ case DW_TAG_enumeration_type:
+ return enumeration_type__encode(btfe, cu, tag);
+ case DW_TAG_subroutine_type:
+ return btf_elf__add_func_proto(btfe, cu, tag__ftype(tag), type_id_off);
+ default:
+ fprintf(stderr, "Unsupported DW_TAG_%s(0x%x)\n",
+ dwarf_tag_name(tag->tag), tag->tag);
+ return -1;
+ }
+}
+
+static struct btf_elf *btfe;
+static uint32_t array_index_id;
+static bool has_index_type;
+
+int btf_encoder__encode()
+{
+ int err;
+
+ if (gobuffer__size(&btfe->percpu_secinfo) != 0)
+ btf_elf__add_datasec_type(btfe, PERCPU_SECTION, &btfe->percpu_secinfo);
+
+ err = btf_elf__encode(btfe, 0);
+ delete_functions();
+ btf_elf__delete(btfe);
+ btfe = NULL;
+
+ return err;
+}
+
+#define MAX_PERCPU_VAR_CNT 4096
+
+struct var_info {
+ uint64_t addr;
+ uint32_t sz;
+ const char *name;
+};
+
+static struct var_info percpu_vars[MAX_PERCPU_VAR_CNT];
+static int percpu_var_cnt;
+
+static int percpu_var_cmp(const void *_a, const void *_b)
+{
+ const struct var_info *a = _a;
+ const struct var_info *b = _b;
+
+ if (a->addr == b->addr)
+ return 0;
+ return a->addr < b->addr ? -1 : 1;
+}
+
+static bool percpu_var_exists(uint64_t addr, uint32_t *sz, const char **name)
+{
+ const struct var_info *p;
+ struct var_info key = { .addr = addr };
+
+ p = bsearch(&key, percpu_vars, percpu_var_cnt,
+ sizeof(percpu_vars[0]), percpu_var_cmp);
+
+ if (!p)
+ return false;
+
+ *sz = p->sz;
+ *name = p->name;
+ return true;
+}
+
+static int collect_percpu_var(struct btf_elf *btfe, GElf_Sym *sym)
+{
+ const char *sym_name;
+ uint64_t addr;
+ uint32_t size;
+
+ /* compare a symbol's shndx to determine if it's a percpu variable */
+ if (elf_sym__section(sym) != btfe->percpu_shndx)
+ return 0;
+ if (elf_sym__type(sym) != STT_OBJECT)
+ return 0;
+
+ addr = elf_sym__value(sym);
+ /*
+ * Store only those symbols that have allocated space in the percpu section.
+ * This excludes the following three types of symbols:
+ *
+ * 1. __ADDRESSABLE(sym), which are forcely emitted as symbols.
+ * 2. __UNIQUE_ID(prefix), which are introduced to generate unique ids.
+ * 3. __exitcall(fn), functions which are labeled as exit calls.
+ *
+ * In addition, the variables defined using DEFINE_PERCPU_FIRST are
+ * also not included, which currently includes:
+ *
+ * 1. fixed_percpu_data
+ */
+ if (!addr)
+ return 0;
+
+ size = elf_sym__size(sym);
+ if (!size)
+ return 0; /* ignore zero-sized symbols */
+
+ sym_name = elf_sym__name(sym, btfe->symtab);
+ if (!btf_name_valid(sym_name)) {
+ dump_invalid_symbol("Found symbol of invalid name when encoding btf",
+ sym_name, btf_elf__verbose, btf_elf__force);
+ if (btf_elf__force)
+ return 0;
+ return -1;
+ }
+
+ if (btf_elf__verbose)
+ printf("Found per-CPU symbol '%s' at address 0x%lx\n", sym_name, addr);
+
+ if (percpu_var_cnt == MAX_PERCPU_VAR_CNT) {
+ fprintf(stderr, "Reached the limit of per-CPU variables: %d\n",
+ MAX_PERCPU_VAR_CNT);
+ return -1;
+ }
+ percpu_vars[percpu_var_cnt].addr = addr;
+ percpu_vars[percpu_var_cnt].sz = size;
+ percpu_vars[percpu_var_cnt].name = sym_name;
+ percpu_var_cnt++;
+
+ return 0;
+}
+
+static void collect_symbol(GElf_Sym *sym, struct funcs_layout *fl)
+{
+ if (!fl->mcount_start &&
+ !strcmp("__start_mcount_loc", elf_sym__name(sym, btfe->symtab))) {
+ fl->mcount_start = sym->st_value;
+ fl->mcount_sec_idx = sym->st_shndx;
+ }
+
+ if (!fl->mcount_stop &&
+ !strcmp("__stop_mcount_loc", elf_sym__name(sym, btfe->symtab)))
+ fl->mcount_stop = sym->st_value;
+}
+
+static int has_all_symbols(struct funcs_layout *fl)
+{
+ return fl->mcount_start && fl->mcount_stop;
+}
+
+static int collect_symbols(struct btf_elf *btfe, bool collect_percpu_vars)
+{
+ struct funcs_layout fl = { };
+ uint32_t core_id;
+ GElf_Sym sym;
+
+ /* cache variables' addresses, preparing for searching in symtab. */
+ percpu_var_cnt = 0;
+
+ /* search within symtab for percpu variables */
+ elf_symtab__for_each_symbol(btfe->symtab, core_id, sym) {
+ if (collect_percpu_vars && collect_percpu_var(btfe, &sym))
+ return -1;
+ if (collect_function(btfe, &sym))
+ return -1;
+ collect_symbol(&sym, &fl);
+ }
+
+ if (collect_percpu_vars) {
+ if (percpu_var_cnt)
+ qsort(percpu_vars, percpu_var_cnt, sizeof(percpu_vars[0]), percpu_var_cmp);
+
+ if (btf_elf__verbose)
+ printf("Found %d per-CPU variables!\n", percpu_var_cnt);
+ }
+
+ if (functions_cnt && has_all_symbols(&fl)) {
+ qsort(functions, functions_cnt, sizeof(functions[0]), functions_cmp);
+ if (filter_functions(btfe, &fl)) {
+ fprintf(stderr, "Failed to filter dwarf functions\n");
+ return -1;
+ }
+ if (btf_elf__verbose)
+ printf("Found %d functions!\n", functions_cnt);
+ } else {
+ if (btf_elf__verbose)
+ printf("ftrace symbols not detected, falling back to DWARF data\n");
+ delete_functions();
+ }
+
+ return 0;
+}
+
+static bool has_arg_names(struct cu *cu, struct ftype *ftype)
+{
+ struct parameter *param;
+ const char *name;
+
+ ftype__for_each_parameter(ftype, param) {
+ name = dwarves__active_loader->strings__ptr(cu, param->name);
+ if (name == NULL)
+ return false;
+ }
+ return true;
+}
+
+int cu__encode_btf(struct cu *cu, int verbose, bool force,
+ bool skip_encoding_vars)
+{
+ uint32_t type_id_off;
+ uint32_t core_id;
+ struct variable *var;
+ struct function *fn;
+ struct tag *pos;
+ int err = 0;
+
+ btf_elf__verbose = verbose;
+ btf_elf__force = force;
+
+ if (btfe && strcmp(btfe->filename, cu->filename)) {
+ err = btf_encoder__encode();
+ if (err)
+ goto out;
+
+ /* Finished one file, add one empty line */
+ if (verbose)
+ printf("\n");
+ }
+
+ if (!btfe) {
+ btfe = btf_elf__new(cu->filename, cu->elf, base_btf);
+ if (!btfe)
+ return -1;
+
+ err = collect_symbols(btfe, !skip_encoding_vars);
+ if (err)
+ goto out;
+
+ has_index_type = false;
+ need_index_type = false;
+ array_index_id = 0;
+
+ if (verbose)
+ printf("File %s:\n", btfe->filename);
+ }
+
+ type_id_off = btf__get_nr_types(btfe->btf);
+
+ if (!has_index_type) {
+ /* cu__find_base_type_by_name() takes "type_id_t *id" */
+ type_id_t id;
+ if (cu__find_base_type_by_name(cu, "int", &id)) {
+ has_index_type = true;
+ array_index_id = type_id_off + id;
+ } else {
+ has_index_type = false;
+ array_index_id = type_id_off + cu->types_table.nr_entries;
+ }
+ }
+
+ cu__for_each_type(cu, core_id, pos) {
+ int32_t btf_type_id = tag__encode_btf(cu, pos, core_id, btfe, array_index_id, type_id_off);
+
+ if (btf_type_id < 0 ||
+ tag__check_id_drift(pos, core_id, btf_type_id, type_id_off)) {
+ err = -1;
+ goto out;
+ }
+ }
+
+ if (need_index_type && !has_index_type) {
+ struct base_type bt = {};
+
+ bt.name = 0;
+ bt.bit_size = 32;
+ btf_elf__add_base_type(btfe, &bt, "__ARRAY_SIZE_TYPE__");
+ has_index_type = true;
+ }
+
+ cu__for_each_function(cu, core_id, fn) {
+ int btf_fnproto_id, btf_fn_id;
+ const char *name;
+
+ /*
+ * Skip functions that:
+ * - are marked as declarations
+ * - do not have full argument names
+ * - are not in ftrace list (if it's available)
+ * - are not external (in case ftrace filter is not available)
+ */
+ if (fn->declaration)
+ continue;
+ if (!has_arg_names(cu, &fn->proto))
+ continue;
+ if (functions_cnt) {
+ struct elf_function *func;
+
+ func = find_function(btfe, function__name(fn, cu));
+ if (!func || func->generated)
+ continue;
+ func->generated = true;
+ } else {
+ if (!fn->external)
+ continue;
+ }
+
+ btf_fnproto_id = btf_elf__add_func_proto(btfe, cu, &fn->proto, type_id_off);
+ name = dwarves__active_loader->strings__ptr(cu, fn->name);
+ btf_fn_id = btf_elf__add_ref_type(btfe, BTF_KIND_FUNC, btf_fnproto_id, name, false);
+ if (btf_fnproto_id < 0 || btf_fn_id < 0) {
+ err = -1;
+ printf("error: failed to encode function '%s'\n", function__name(fn, cu));
+ goto out;
+ }
+ }
+
+ if (skip_encoding_vars)
+ goto out;
+
+ if (btfe->percpu_shndx == 0 || !btfe->symtab)
+ goto out;
+
+ if (verbose)
+ printf("search cu '%s' for percpu global variables.\n", cu->name);
+
+ cu__for_each_variable(cu, core_id, pos) {
+ uint32_t size, type, linkage, offset;
+ const char *name;
+ uint64_t addr;
+ int id;
+
+ var = tag__variable(pos);
+ if (var->declaration && !var->spec)
+ continue;
+ /* percpu variables are allocated in global space */
+ if (variable__scope(var) != VSCOPE_GLOBAL && !var->spec)
+ continue;
+
+ /* addr has to be recorded before we follow spec */
+ addr = var->ip.addr;
+ if (var->spec)
+ var = var->spec;
+
+ if (!percpu_var_exists(addr, &size, &name))
+ continue; /* not a per-CPU variable */
+
+ if (var->ip.tag.type == 0) {
+ fprintf(stderr, "error: found variable '%s' in CU '%s' that has void type\n",
+ name, cu->name);
+ if (force)
+ continue;
+ err = -1;
+ break;
+ }
+
+ type = var->ip.tag.type + type_id_off;
+ linkage = var->external ? BTF_VAR_GLOBAL_ALLOCATED : BTF_VAR_STATIC;
+
+ if (btf_elf__verbose) {
+ printf("Variable '%s' from CU '%s' at address 0x%lx encoded\n",
+ name, cu->name, addr);
+ }
+
+ /* add a BTF_KIND_VAR in btfe->types */
+ id = btf_elf__add_var_type(btfe, type, name, linkage);
+ if (id < 0) {
+ err = -1;
+ fprintf(stderr, "error: failed to encode variable '%s' at addr 0x%lx\n",
+ name, addr);
+ break;
+ }
+
+ /*
+ * add a BTF_VAR_SECINFO in btfe->percpu_secinfo, which will be added into
+ * btfe->types later when we add BTF_VAR_DATASEC.
+ */
+ offset = addr - btfe->percpu_base_addr;
+ id = btf_elf__add_var_secinfo(&btfe->percpu_secinfo, id, offset, size);
+ if (id < 0) {
+ err = -1;
+ fprintf(stderr, "error: failed to encode section info for variable '%s' at addr 0x%lx\n",
+ name, addr);
+ break;
+ }
+ }
+
+out:
+ if (err) {
+ delete_functions();
+ btf_elf__delete(btfe);
+ btfe = NULL;
+ }
+ return err;
+}
diff --git a/btf_encoder.h b/btf_encoder.h
new file mode 100644
index 0000000..46fb231
--- /dev/null
+++ b/btf_encoder.h
@@ -0,0 +1,19 @@
+#ifndef _BTF_ENCODER_H_
+#define _BTF_ENCODER_H_ 1
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2019 Facebook
+
+ Derived from ctf_encoder.h, which is:
+ Copyright (C) Arnaldo Carvalho de Melo <acme@redhat.com>
+ */
+
+struct cu;
+
+int btf_encoder__encode();
+
+int cu__encode_btf(struct cu *cu, int verbose, bool force,
+ bool skip_encoding_vars);
+
+#endif /* _BTF_ENCODER_H_ */
diff --git a/btf_loader.c b/btf_loader.c
new file mode 100644
index 0000000..ec286f4
--- /dev/null
+++ b/btf_loader.c
@@ -0,0 +1,579 @@
+/*
+ * btf_loader.c
+ *
+ * Copyright (C) 2018 Arnaldo Carvalho de Melo <acme@kernel.org>
+ *
+ * Based on ctf_loader.c that, in turn, was based on ctfdump.c: CTF dumper.
+ *
+ * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <malloc.h>
+#include <string.h>
+#include <limits.h>
+#include <libgen.h>
+#include <zlib.h>
+
+#include <gelf.h>
+
+#include "libbtf.h"
+#include "lib/bpf/include/uapi/linux/btf.h"
+#include "dutil.h"
+#include "dwarves.h"
+
+/*
+ * FIXME: We should just get the table from the BTF ELF section
+ * and use it directly
+ */
+extern struct strings *strings;
+
+static void *tag__alloc(const size_t size)
+{
+ struct tag *tag = zalloc(size);
+
+ if (tag != NULL)
+ tag->top_level = 1;
+
+ return tag;
+}
+
+static int btf_elf__load_ftype(struct btf_elf *btfe, struct ftype *proto, uint32_t tag,
+ const struct btf_type *tp, uint32_t id)
+{
+ const struct btf_param *param = btf_params(tp);
+ int i, vlen = btf_vlen(tp);
+
+ proto->tag.tag = tag;
+ proto->tag.type = tp->type;
+ INIT_LIST_HEAD(&proto->parms);
+
+ for (i = 0; i < vlen; ++i, param++) {
+ if (param->type == 0)
+ proto->unspec_parms = 1;
+ else {
+ struct parameter *p = tag__alloc(sizeof(*p));
+
+ if (p == NULL)
+ goto out_free_parameters;
+ p->tag.tag = DW_TAG_formal_parameter;
+ p->tag.type = param->type;
+ p->name = param->name_off;
+ ftype__add_parameter(proto, p);
+ }
+ }
+
+ cu__add_tag_with_id(btfe->priv, &proto->tag, id);
+
+ return 0;
+out_free_parameters:
+ ftype__delete(proto, btfe->priv);
+ return -ENOMEM;
+}
+
+static int create_new_function(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id)
+{
+ struct function *func = tag__alloc(sizeof(*func));
+
+ if (func == NULL)
+ return -ENOMEM;
+
+ // for BTF this is not really the type of the return of the function,
+ // but the prototype, the return type is the one in type_id
+ func->btf = 1;
+ func->proto.tag.tag = DW_TAG_subprogram;
+ func->proto.tag.type = tp->type;
+ func->name = tp->name_off;
+ INIT_LIST_HEAD(&func->lexblock.tags);
+ cu__add_tag_with_id(btfe->priv, &func->proto.tag, id);
+
+ return 0;
+}
+
+static struct base_type *base_type__new(strings_t name, uint32_t attrs,
+ uint8_t float_type, size_t size)
+{
+ struct base_type *bt = tag__alloc(sizeof(*bt));
+
+ if (bt != NULL) {
+ bt->name = name;
+ bt->bit_size = size;
+ bt->is_signed = attrs & BTF_INT_SIGNED;
+ bt->is_bool = attrs & BTF_INT_BOOL;
+ bt->name_has_encoding = false;
+ bt->float_type = float_type;
+ }
+ return bt;
+}
+
+static void type__init(struct type *type, uint32_t tag,
+ strings_t name, size_t size)
+{
+ __type__init(type);
+ INIT_LIST_HEAD(&type->namespace.tags);
+ type->size = size;
+ type->namespace.tag.tag = tag;
+ type->namespace.name = name;
+ type->namespace.sname = 0;
+}
+
+static struct type *type__new(uint16_t tag, strings_t name, size_t size)
+{
+ struct type *type = tag__alloc(sizeof(*type));
+
+ if (type != NULL)
+ type__init(type, tag, name, size);
+
+ return type;
+}
+
+static struct class *class__new(strings_t name, size_t size, bool is_union)
+{
+ struct class *class = tag__alloc(sizeof(*class));
+ uint32_t tag = is_union ? DW_TAG_union_type : DW_TAG_structure_type;
+
+ if (class != NULL) {
+ type__init(&class->type, tag, name, size);
+ INIT_LIST_HEAD(&class->vtable);
+ }
+
+ return class;
+}
+
+static struct variable *variable__new(strings_t name, uint32_t linkage)
+{
+ struct variable *var = tag__alloc(sizeof(*var));
+
+ if (var != NULL) {
+ var->external = linkage == BTF_VAR_GLOBAL_ALLOCATED;
+ var->name = name;
+ var->ip.tag.tag = DW_TAG_variable;
+ }
+
+ return var;
+}
+
+static int create_new_base_type(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id)
+{
+ uint32_t attrs = btf_int_encoding(tp);
+ strings_t name = tp->name_off;
+ struct base_type *base = base_type__new(name, attrs, 0, btf_int_bits(tp));
+
+ if (base == NULL)
+ return -ENOMEM;
+
+ base->tag.tag = DW_TAG_base_type;
+ cu__add_tag_with_id(btfe->priv, &base->tag, id);
+
+ return 0;
+}
+
+static int create_new_array(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id)
+{
+ struct btf_array *ap = btf_array(tp);
+ struct array_type *array = tag__alloc(sizeof(*array));
+
+ if (array == NULL)
+ return -ENOMEM;
+
+ /* FIXME: where to get the number of dimensions?
+ * it it flattened? */
+ array->dimensions = 1;
+ array->nr_entries = malloc(sizeof(uint32_t));
+
+ if (array->nr_entries == NULL) {
+ free(array);
+ return -ENOMEM;
+ }
+
+ array->nr_entries[0] = ap->nelems;
+ array->tag.tag = DW_TAG_array_type;
+ array->tag.type = ap->type;
+
+ cu__add_tag_with_id(btfe->priv, &array->tag, id);
+
+ return 0;
+}
+
+static int create_members(struct btf_elf *btfe, const struct btf_type *tp,
+ struct type *class)
+{
+ struct btf_member *mp = btf_members(tp);
+ int i, vlen = btf_vlen(tp);
+
+ for (i = 0; i < vlen; i++) {
+ struct class_member *member = zalloc(sizeof(*member));
+
+ if (member == NULL)
+ return -ENOMEM;
+
+ member->tag.tag = DW_TAG_member;
+ member->tag.type = mp[i].type;
+ member->name = mp[i].name_off;
+ member->bit_offset = btf_member_bit_offset(tp, i);
+ member->bitfield_size = btf_member_bitfield_size(tp, i);
+ member->byte_offset = member->bit_offset / 8;
+ /* sizes and offsets will be corrected at class__fixup_btf_bitfields */
+ type__add_member(class, member);
+ }
+
+ return 0;
+}
+
+static int create_new_class(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id)
+{
+ struct class *class = class__new(tp->name_off, tp->size, false);
+ int member_size = create_members(btfe, tp, &class->type);
+
+ if (member_size < 0)
+ goto out_free;
+
+ cu__add_tag_with_id(btfe->priv, &class->type.namespace.tag, id);
+
+ return 0;
+out_free:
+ class__delete(class, btfe->priv);
+ return -ENOMEM;
+}
+
+static int create_new_union(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id)
+{
+ struct type *un = type__new(DW_TAG_union_type, tp->name_off, tp->size);
+ int member_size = create_members(btfe, tp, un);
+
+ if (member_size < 0)
+ goto out_free;
+
+ cu__add_tag_with_id(btfe->priv, &un->namespace.tag, id);
+
+ return 0;
+out_free:
+ type__delete(un, btfe->priv);
+ return -ENOMEM;
+}
+
+static struct enumerator *enumerator__new(strings_t name, uint32_t value)
+{
+ struct enumerator *en = tag__alloc(sizeof(*en));
+
+ if (en != NULL) {
+ en->name = name;
+ en->value = value;
+ en->tag.tag = DW_TAG_enumerator;
+ }
+
+ return en;
+}
+
+static int create_new_enumeration(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id)
+{
+ struct btf_enum *ep = btf_enum(tp);
+ uint16_t i, vlen = btf_vlen(tp);
+ struct type *enumeration = type__new(DW_TAG_enumeration_type,
+ tp->name_off,
+ tp->size ? tp->size * 8 : (sizeof(int) * 8));
+
+ if (enumeration == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < vlen; i++) {
+ strings_t name = ep[i].name_off;
+ uint32_t value = ep[i].val;
+ struct enumerator *enumerator = enumerator__new(name, value);
+
+ if (enumerator == NULL)
+ goto out_free;
+
+ enumeration__add(enumeration, enumerator);
+ }
+
+ cu__add_tag_with_id(btfe->priv, &enumeration->namespace.tag, id);
+
+ return 0;
+out_free:
+ enumeration__delete(enumeration, btfe->priv);
+ return -ENOMEM;
+}
+
+static int create_new_subroutine_type(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id)
+{
+ struct ftype *proto = tag__alloc(sizeof(*proto));
+
+ if (proto == NULL)
+ return -ENOMEM;
+
+ return btf_elf__load_ftype(btfe, proto, DW_TAG_subroutine_type, tp, id);
+}
+
+static int create_new_forward_decl(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id)
+{
+ struct class *fwd = class__new(tp->name_off, 0, btf_kflag(tp));
+
+ if (fwd == NULL)
+ return -ENOMEM;
+ fwd->type.declaration = 1;
+ cu__add_tag_with_id(btfe->priv, &fwd->type.namespace.tag, id);
+ return 0;
+}
+
+static int create_new_typedef(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id)
+{
+ struct type *type = type__new(DW_TAG_typedef, tp->name_off, 0);
+
+ if (type == NULL)
+ return -ENOMEM;
+
+ type->namespace.tag.type = tp->type;
+ cu__add_tag_with_id(btfe->priv, &type->namespace.tag, id);
+
+ return 0;
+}
+
+static int create_new_variable(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id)
+{
+ struct btf_var *bvar = btf_var(tp);
+ struct variable *var = variable__new(tp->name_off, bvar->linkage);
+
+ if (var == NULL)
+ return -ENOMEM;
+
+ var->ip.tag.type = tp->type;
+ cu__add_tag_with_id(btfe->priv, &var->ip.tag, id);
+ return 0;
+}
+
+static int create_new_datasec(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id)
+{
+ //strings_t name = btf_elf__get32(btfe, &tp->name_off);
+
+ //cu__add_tag_with_id(btfe->priv, &datasec->tag, id);
+
+ /*
+ * FIXME: this will not be used to reconstruct some original C code,
+ * its about runtime placement of variables so just ignore this for now
+ */
+ return 0;
+}
+
+static int create_new_tag(struct btf_elf *btfe, int type, const struct btf_type *tp, uint32_t id)
+{
+ struct tag *tag = zalloc(sizeof(*tag));
+
+ if (tag == NULL)
+ return -ENOMEM;
+
+ switch (type) {
+ case BTF_KIND_CONST: tag->tag = DW_TAG_const_type; break;
+ case BTF_KIND_PTR: tag->tag = DW_TAG_pointer_type; break;
+ case BTF_KIND_RESTRICT: tag->tag = DW_TAG_restrict_type; break;
+ case BTF_KIND_VOLATILE: tag->tag = DW_TAG_volatile_type; break;
+ default:
+ free(tag);
+ printf("%s: Unknown type %d\n\n", __func__, type);
+ return 0;
+ }
+
+ tag->type = tp->type;
+ cu__add_tag_with_id(btfe->priv, tag, id);
+
+ return 0;
+}
+
+static int btf_elf__load_types(struct btf_elf *btfe)
+{
+ uint32_t type_index;
+ int err;
+
+ for (type_index = 1; type_index <= btf__get_nr_types(btfe->btf); type_index++) {
+ const struct btf_type *type_ptr = btf__type_by_id(btfe->btf, type_index);
+ uint32_t type = btf_kind(type_ptr);
+
+ switch (type) {
+ case BTF_KIND_INT:
+ err = create_new_base_type(btfe, type_ptr, type_index);
+ break;
+ case BTF_KIND_ARRAY:
+ err = create_new_array(btfe, type_ptr, type_index);
+ break;
+ case BTF_KIND_STRUCT:
+ err = create_new_class(btfe, type_ptr, type_index);
+ break;
+ case BTF_KIND_UNION:
+ err = create_new_union(btfe, type_ptr, type_index);
+ break;
+ case BTF_KIND_ENUM:
+ err = create_new_enumeration(btfe, type_ptr, type_index);
+ break;
+ case BTF_KIND_FWD:
+ err = create_new_forward_decl(btfe, type_ptr, type_index);
+ break;
+ case BTF_KIND_TYPEDEF:
+ err = create_new_typedef(btfe, type_ptr, type_index);
+ break;
+ case BTF_KIND_VAR:
+ err = create_new_variable(btfe, type_ptr, type_index);
+ break;
+ case BTF_KIND_DATASEC:
+ err = create_new_datasec(btfe, type_ptr, type_index);
+ break;
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_PTR:
+ case BTF_KIND_CONST:
+ case BTF_KIND_RESTRICT:
+ err = create_new_tag(btfe, type, type_ptr, type_index);
+ break;
+ case BTF_KIND_UNKN:
+ cu__table_nullify_type_entry(btfe->priv, type_index);
+ fprintf(stderr, "BTF: idx: %d, Unknown kind %d\n", type_index, type);
+ fflush(stderr);
+ err = 0;
+ break;
+ case BTF_KIND_FUNC_PROTO:
+ err = create_new_subroutine_type(btfe, type_ptr, type_index);
+ break;
+ case BTF_KIND_FUNC:
+ // BTF_KIND_FUNC corresponding to a defined subprogram.
+ err = create_new_function(btfe, type_ptr, type_index);
+ break;
+ default:
+ fprintf(stderr, "BTF: idx: %d, Unknown kind %d\n", type_index, type);
+ fflush(stderr);
+ err = 0;
+ break;
+ }
+
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int btf_elf__load_sections(struct btf_elf *btfe)
+{
+ return btf_elf__load_types(btfe);
+}
+
+static int class__fixup_btf_bitfields(struct tag *tag, struct cu *cu, struct btf_elf *btfe)
+{
+ struct class_member *pos;
+ struct type *tag_type = tag__type(tag);
+
+ type__for_each_data_member(tag_type, pos) {
+ struct tag *type = tag__strip_typedefs_and_modifiers(&pos->tag, cu);
+
+ if (type == NULL) /* FIXME: C++ BTF... */
+ continue;
+
+ pos->bitfield_offset = 0;
+ pos->byte_size = tag__size(type, cu);
+ pos->bit_size = pos->byte_size * 8;
+
+ /* bitfield fixup is needed for enums and base types only */
+ if (type->tag != DW_TAG_base_type && type->tag != DW_TAG_enumeration_type)
+ continue;
+
+ /* if BTF data is incorrect and has size == 0, skip field,
+ * instead of crashing */
+ if (pos->byte_size == 0) {
+ continue;
+ }
+
+ if (pos->bitfield_size) {
+ /* bitfields seem to be always aligned, no matter the packing */
+ pos->byte_offset = pos->bit_offset / pos->bit_size * pos->bit_size / 8;
+ pos->bitfield_offset = pos->bit_offset - pos->byte_offset * 8;
+ /* re-adjust bitfield offset if it is negative */
+ if (pos->bitfield_offset < 0) {
+ pos->bitfield_offset += pos->bit_size;
+ pos->byte_offset -= pos->byte_size;
+ pos->bit_offset = pos->byte_offset * 8 + pos->bitfield_offset;
+ }
+ } else {
+ pos->byte_offset = pos->bit_offset / 8;
+ }
+ }
+
+ return 0;
+}
+
+static int cu__fixup_btf_bitfields(struct cu *cu, struct btf_elf *btfe)
+{
+ int err = 0;
+ struct tag *pos;
+
+ list_for_each_entry(pos, &cu->tags, node)
+ if (tag__is_struct(pos) || tag__is_union(pos)) {
+ err = class__fixup_btf_bitfields(pos, cu, btfe);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+static void btf_elf__cu_delete(struct cu *cu)
+{
+ btf_elf__delete(cu->priv);
+ cu->priv = NULL;
+}
+
+static const char *btf_elf__strings_ptr(const struct cu *cu, strings_t s)
+{
+ return btf_elf__string(cu->priv, s);
+}
+
+struct debug_fmt_ops btf_elf__ops;
+
+int btf_elf__load_file(struct cus *cus, struct conf_load *conf, const char *filename)
+{
+ int err;
+ struct btf_elf *btfe = btf_elf__new(filename, NULL, base_btf);
+
+ if (btfe == NULL)
+ return -1;
+
+ struct cu *cu = cu__new(filename, btfe->wordsize, NULL, 0, filename);
+ if (cu == NULL)
+ return -1;
+
+ cu->language = LANG_C;
+ cu->uses_global_strings = false;
+ cu->little_endian = !btfe->is_big_endian;
+ cu->dfops = &btf_elf__ops;
+ cu->priv = btfe;
+ btfe->priv = cu;
+ if (btf_elf__load(btfe) != 0)
+ return -1;
+
+ err = btf_elf__load_sections(btfe);
+
+ if (err != 0) {
+ cu__delete(cu);
+ return err;
+ }
+
+ err = cu__fixup_btf_bitfields(cu, btfe);
+ /*
+ * The app stole this cu, possibly deleting it,
+ * so forget about it
+ */
+ if (conf && conf->steal && conf->steal(cu, conf))
+ return 0;
+
+ cus__add(cus, cu);
+ return err;
+}
+
+struct debug_fmt_ops btf_elf__ops = {
+ .name = "btf",
+ .load_file = btf_elf__load_file,
+ .strings__ptr = btf_elf__strings_ptr,
+ .cu__delete = btf_elf__cu_delete,
+};
diff --git a/btfdiff b/btfdiff
new file mode 100755
index 0000000..4db7032
--- /dev/null
+++ b/btfdiff
@@ -0,0 +1,33 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright © 2019 Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+# Use pahole to produce output from BTF and from DWARF, then do a diff
+# Use --flat_arrays with DWARF as BTF, like CTF, flattens arrays.
+# Use --show_private_classes as BTF shows all structs, while pahole knows
+# if some struct is defined only inside another struct/class or in a function,
+# this information is not available when loading from BTF.
+
+if [ $# -eq 0 ] ; then
+ echo "Usage: btfdiff <filename_with_BTF_and_DWARF_info>"
+ exit 1
+fi
+
+file=$1
+btf_output=$(mktemp /tmp/btfdiff.btf.XXXXXX)
+dwarf_output=$(mktemp /tmp/btfdiff.dwarf.XXXXXX)
+pahole_bin=${PAHOLE-"pahole"}
+
+${pahole_bin} -F dwarf \
+ --flat_arrays \
+ --suppress_aligned_attribute \
+ --suppress_force_paddings \
+ --suppress_packed \
+ --show_private_classes $file > $dwarf_output
+${pahole_bin} -F btf \
+ --suppress_packed \
+ $file > $btf_output
+
+diff -up $dwarf_output $btf_output
+
+rm -f $btf_output $dwarf_output
+exit 0
diff --git a/changes-v1.13 b/changes-v1.13
new file mode 100644
index 0000000..b41ad7e
--- /dev/null
+++ b/changes-v1.13
@@ -0,0 +1,209 @@
+Here is a summary of changes for the 1.13 version of pahole and its friends:
+
+- BTF
+
+ - Use of the recently introduced BTF deduplication algorithm present in the
+ Linux kernel's libbpf library, which allows for all the types in a multi
+ compile unit binary such as vmlinux to be compactly stored, without duplicates.
+
+ E.g.: from roughly:
+
+ $ readelf -SW ../build/v5.1-rc4+/vmlinux | grep .debug_info.*PROGBITS
+ [63] .debug_info PROGBITS 0000000000000000 1d80be0 c3c18b9 00 0 0 1
+ $
+ 195 MiB
+
+ to:
+
+ $ time pahole --btf_encode ../build/v5.1-rc4+/vmlinux
+ real 0m19.168s
+ user 0m17.707s # On a Lenovo t480s (i7-8650U) SSD
+ sys 0m1.337s
+ $
+
+ $ readelf -SW ../build/v5.1-rc4+/vmlinux | grep .BTF.*PROGBITS
+ [78] .BTF PROGBITS 0000000000000000 27b49f61 1e23c3 00 0 0 1
+ $
+ ~2 MiB
+
+ - Introduce a 'btfdiff' utility that prints the output from DWARF and from
+ BTF, comparing the pretty printed outputs, running it on various linux
+ kernel images, such as an allyesconfig for ppc64.
+
+ Running it on the above 5.1-rc4+ vmlinux:
+
+ $ btfdiff ../build/v5.1-rc4+/vmlinux
+ $
+
+ No differences from the types generated from the DWARF ELF sections to the
+ ones generated from the BTF ELF section.
+
+ - Add a BTF loader, i.e. 'pahole -F btf' allows pretty printing of structs
+ and unions in the same fashion as with DWARF info, and since BTF is way
+ more compact, using it is much faster than using DWARF.
+
+ $ cat ../build/v5.1-rc4+/vmlinux > /dev/null
+ $ perf stat -e cycles pahole -F btf ../build/v5.1-rc4+/vmlinux > /dev/null
+
+ Performance counter stats for 'pahole -F btf ../build/v5.1-rc4+/vmlinux':
+
+ 229,712,692 cycles:u
+ 0.063379597 seconds time elapsed
+ 0.056265000 seconds user
+ 0.006911000 seconds sys
+
+ $ perf stat -e cycles pahole -F dwarf ../build/v5.1-rc4+/vmlinux > /dev/null
+
+ Performance counter stats for 'pahole -F dwarf ../build/v5.1-rc4+/vmlinux':
+
+ 49,579,679,466 cycles:u
+ 13.063487352 seconds time elapsed
+ 12.612512000 seconds user
+ 0.426226000 seconds sys
+ $
+
+- Better union support:
+
+ - Allow unions to be specified in pahole in the same fashion as structs
+
+ $ pahole -C thread_union ../build/v5.1-rc4+/net/ipv4/tcp.o
+ union thread_union {
+ struct task_struct task __attribute__((__aligned__(64))); /* 0 11008 */
+ long unsigned int stack[2048]; /* 0 16384 */
+ };
+ $
+
+- Infer __attribute__((__packed__)) when structs have no alignment holes
+ and violate basic types (integer, longs, short integer) natural alignment
+ requirements. Several heuristics are used to infer the __packed__
+ attribute, see the changeset log for descriptions.
+
+ $ pahole -F btf -C boot_e820_entry ../build/v5.1-rc4+/vmlinux
+ struct boot_e820_entry {
+ __u64 addr; /* 0 8 */
+ __u64 size; /* 8 8 */
+ __u32 type; /* 16 4 */
+
+ /* size: 20, cachelines: 1, members: 3 */
+ /* last cacheline: 20 bytes */
+ } __attribute__((__packed__));
+ $
+
+ $ pahole -F btf -C lzma_header ../build/v5.1-rc4+/vmlinux
+ struct lzma_header {
+ uint8_t pos; /* 0 1 */
+ uint32_t dict_size; /* 1 4 */
+ uint64_t dst_size; /* 5 8 */
+
+ /* size: 13, cachelines: 1, members: 3 */
+ /* last cacheline: 13 bytes */
+ } __attribute__((__packed__));
+
+- Support DWARF5's DW_AT_alignment, which, together with the __packed__
+ attribute inference algorithms produce output that, when compiled, should
+ produce structures with layouts that match the original source code.
+
+ See it in action with 'struct task_struct', which will also show some of the
+ new information at the struct summary, at the end of the struct:
+
+ $ pahole -C task_struct ../build/v5.1-rc4+/vmlinux | tail -19
+ /* --- cacheline 103 boundary (6592 bytes) --- */
+ struct vm_struct * stack_vm_area; /* 6592 8 */
+ refcount_t stack_refcount; /* 6600 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
+ void * security; /* 6608 8 */
+
+ /* XXX 40 bytes hole, try to pack */
+
+ /* --- cacheline 104 boundary (6656 bytes) --- */
+ struct thread_struct thread __attribute__((__aligned__(64))); /* 6656 4352 */
+
+ /* size: 11008, cachelines: 172, members: 207 */
+ /* sum members: 10902, holes: 16, sum holes: 98 */
+ /* sum bitfield members: 10 bits, bit holes: 2, sum bit holes: 54 bits */
+ /* paddings: 3, sum paddings: 14 */
+ /* forced alignments: 6, forced holes: 1, sum forced holes: 40 */
+ } __attribute__((__aligned__(64)));
+ $
+
+- Add a '--compile' option to 'pfunct' that produces compileable output for the
+ function prototypes in an object file. There are still some bugs but the vast
+ majority of the kernel single compilation unit files the ones produced from a
+ single .c file are working, see the new 'fullcircle' utility that uses this
+ feature.
+
+ Example of it in action:
+
+ $ pfunct --compile=static_key_false ../build/v5.1-rc4+/net/ipv4/tcp.o
+ typedef _Bool bool;
+ typedef struct {
+ int counter; /* 0 4 */
+
+ /* size: 4, cachelines: 1, members: 1 */
+ /* last cacheline: 4 bytes */
+ } atomic_t;
+
+ struct jump_entry;
+
+ struct static_key_mod;
+
+
+ struct static_key {
+ atomic_t enabled; /* 0 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
+ union {
+ long unsigned int type; /* 8 8 */
+ struct jump_entry * entries; /* 8 8 */
+ struct static_key_mod * next; /* 8 8 */
+ }; /* 8 8 */
+
+ /* size: 16, cachelines: 1, members: 2 */
+ /* sum members: 12, holes: 1, sum holes: 4 */
+ /* last cacheline: 16 bytes */
+ };
+
+ bool static_key_false(struct static_key * key)
+ {
+ return *(bool *)1;
+ }
+
+ $
+
+The generation of compilable code from the type information and its use in the
+new tool 'fullcircle, helps validate all the parts of this codebase, finding
+bugs that were lurking forever, go read the csets to find all sorts of curious
+C language features that are rarely seen, like unnamed zero sized bitfields and
+the way people have been using it over the years in a codebase like the linux
+kernel.
+
+Certainly there are several other features, changes and fixes that I forgot to
+mention! Now lemme release this version so that we can use it more extensively
+together with a recent patch merged for 5.2:
+
+ [PATCH bpf-next] kbuild: add ability to generate BTF type info for vmlinux
+
+With it BTF will be always available for all the types of the kernel, which will
+open a pandora box of cool new features that are in the works, and, for people
+already using pahole, will greatly speed up its usage.
+
+Please try to alias it to use btf, i.e.
+
+ alias pahole='pahole -F btf'
+
+Please report any problems you may find with this new version or with the BTF
+loader or any errors in the layout generated/pretty printed.
+
+Thanks to the fine BTF guys at Facebook for the patches and help in testing,
+fixing bugs and getting this out of the door, the stats for this release are:
+
+ Changesets: 157
+
+ 113 Arnaldo Carvalho de Melo Red Hat
+ 32 Andrii Nakryiko Facebook
+ 10 Yonghong Song Facebook
+ 1 Martin Lau Facebook
+ 1 Domenico Andreoli
diff --git a/changes-v1.16 b/changes-v1.16
new file mode 100644
index 0000000..0954157
--- /dev/null
+++ b/changes-v1.16
@@ -0,0 +1,104 @@
+v1.16 changes:
+
+BTF encoder:
+
+ Andrii Nakryiko <andriin@fb.com>:
+
+ - Preserve and encode exported functions as BTF_KIND_FUNC.
+
+ Add encoding of DWARF's DW_TAG_subprogram_type into BTF's BTF_KIND_FUNC
+ (plus corresponding BTF_KIND_FUNC_PROTO). Only exported functions are converted
+ for now. This allows to capture all the exported kernel functions, same subset
+ that's exposed through /proc/kallsyms.
+
+BTF loader:
+
+ Arnaldo Carvalho de Melo <acme@redhat.com>
+
+ - Add support for BTF_KIND_FUNC
+
+ Some changes to the fprintf routines were needed, as BTF has as the
+ function type just a BTF_KIND_FUNC_PROTO, while DWARF has as the type for a
+ function its return value type. With a function->btf flag this was overcome and
+ all the other goodies in pfunct are present.
+
+Pretty printer:
+
+ Arnaldo Carvalho de Melo:
+
+ - Account inline type __aligned__ member types for spacing:
+
+ union {
+ refcount_t rcu_users; /* 2568 4 */
+ struct callback_head rcu __attribute__((__aligned__(8))); /* 2568 16 */
+ - } __attribute__((__aligned__(8))); /* 2568 16 */
+ + } __attribute__((__aligned__(8))); /* 2568 16 */
+ struct pipe_inode_info * splice_pipe; /* 2584 8 */
+
+ - Fix alignment of class members that are structs/enums/unions
+
+ E.g. look at that 'completion' member in this struct:
+
+ struct cpu_stop_done {
+ atomic_t nr_todo; /* 0 4 */
+ int ret; /* 4 4 */
+ - struct completion completion; /* 8 32 */
+ + struct completion completion; /* 8 32 */
+
+ /* size: 40, cachelines: 1, members: 3 */
+ /* last cacheline: 40 bytes */
+
+ - Fixup handling classes with no members, solving a NULL deref.
+
+ Gareth Lloyd <gareth.lloyd@uk.ibm.com>:
+
+ - Avoid infinite loop trying to determine type with static data member of its own type.
+
+RPM spec file.
+
+Jiri Olsa <jolsa@redhat.com>
+
+ Add dwarves dependency on libdwarves1.
+
+pfunct:
+
+ Arnaldo Carvalho de Melo <acme@redhat.com>
+
+ - type->type == 0 is void, fix --compile for that
+
+ We were using the fall back for that, i.e. 'return 0;' was being emitted
+ for a function returning void, noticed with using BTF as the format.
+
+pdwtags:
+
+ - Print DW_TAG_subroutine_type as well
+
+ So that we can see at least via pdwtags those tags, be it from DWARF of BTF.
+
+core:
+
+ Arnaldo Carvalho de Melo <acme@redhat.com>
+
+ Fix ptr_table__add_with_id() handling of pt->nr_entries, covering how
+ BTF variables IDs are encoded.
+
+
+pglobal:
+
+ Arnaldo Carvalho de Melo <acme@redhat.com>:
+
+ - Allow passing the format path specifier, to use with BTF
+
+ I.e. now we can, just like with pahole, use:
+
+ pglobal -F btf --variable foo.o
+
+ To get the global variables.
+
+Tree wide:
+
+ Arnaldo Carvalho de Melo <acme@redhat.com>:
+
+ - Fixup issues pointed out by various coverity reports.
+
+Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
diff --git a/changes-v1.17 b/changes-v1.17
new file mode 100644
index 0000000..8999bdf
--- /dev/null
+++ b/changes-v1.17
@@ -0,0 +1,540 @@
+v1.17 changes:
+
+tl;dr:
+
+BTF loader:
+
+ - Support raw BTF as available in /sys/kernel/btf/vmlinux.
+
+pahole:
+
+ - When the sole argument passed isn't a file, take it as a class name:
+
+ $ pahole sk_buff
+
+ - Do not require a class name to operate without a file name.
+
+ $ pahole # is equivalent to:
+ $ pahole vmlinux
+
+ - Make --find_pointers_to consider unions:
+
+ $ pahole --find_pointers_to ehci_qh
+
+ - Make --contains and --find_pointers_to honour --unions
+
+ $ pahole --unions --contains inet_sock
+
+ - Add support for finding pointers to void:
+
+ $ pahole --find_pointers_to void
+
+ - Make --contains and --find_pointers_to to work with base types:
+
+ $ pahole --find_pointers_to 'short unsigned int'
+
+ - Make --contains look for more than just unions, structs:
+
+ $ pahole --contains raw_spinlock_t
+
+ - Consider unions when looking for classes containing some class:
+
+ $ pahole --contains tpacket_req
+
+ - Introduce --unions to consider just unions:
+
+ $ pahole --unions --sizes
+ $ pahole --unions --prefix tcp
+ $ pahole --unions --nr_members
+
+ - Fix -m/--nr_methods - Number of functions operating on a type pointer
+
+ $ pahole --nr_methods
+
+man-pages:
+
+ - Add section about --hex + -E to locate offsets deep into sub structs.
+
+ - Add more information about BTF.
+
+ - Add some examples.
+
+----------------------------------
+
+I want the details:
+
+btf loader:
+
+ - Support raw BTF as available in /sys/kernel/btf/vmlinux
+
+ Be it automatically when no -F option is passed and
+ /sys/kernel/btf/vmlinux is available, or when /sys/kernel/btf/vmlinux is
+ passed as the filename to the tool, i.e.:
+
+ $ pahole -C list_head
+ struct list_head {
+ struct list_head * next; /* 0 8 */
+ struct list_head * prev; /* 8 8 */
+
+ /* size: 16, cachelines: 1, members: 2 */
+ /* last cacheline: 16 bytes */
+ };
+ $ strace -e openat pahole -C list_head |& grep /sys/kernel/btf/
+ openat(AT_FDCWD, "/sys/kernel/btf/vmlinux", O_RDONLY) = 3
+ $
+ $ pahole -C list_head /sys/kernel/btf/vmlinux
+ struct list_head {
+ struct list_head * next; /* 0 8 */
+ struct list_head * prev; /* 8 8 */
+
+ /* size: 16, cachelines: 1, members: 2 */
+ /* last cacheline: 16 bytes */
+ };
+ $
+
+ If one wants to grab the matching vmlinux to use its DWARF info instead,
+ which is useful to compare the results with what we have from BTF, for
+ instance, its just a matter of using '-F dwarf'.
+
+ This in turn shows something that at first came as a surprise, but then
+ has a simple explanation:
+
+ For very common data structures, that will probably appear in all of the
+ DWARF CUs (Compilation Units), like 'struct list_head', using '-F dwarf'
+ is faster:
+
+ $ perf stat -e cycles pahole -F btf -C list_head > /dev/null
+
+ Performance counter stats for 'pahole -F btf -C list_head':
+
+ 45,722,518 cycles:u
+
+ 0.023717300 seconds time elapsed
+
+ 0.016474000 seconds user
+ 0.007212000 seconds sys
+
+ $ perf stat -e cycles pahole -F dwarf -C list_head > /dev/null
+
+ Performance counter stats for 'pahole -F dwarf -C list_head':
+
+ 14,170,321 cycles:u
+
+ 0.006668904 seconds time elapsed
+
+ 0.005562000 seconds user
+ 0.001109000 seconds sys
+
+ $
+
+ But for something that is more specific to a subsystem, the DWARF loader
+ will have to process way more stuff till it gets to that struct:
+
+ $ perf stat -e cycles pahole -F dwarf -C tcp_sock > /dev/null
+
+ Performance counter stats for 'pahole -F dwarf -C tcp_sock':
+
+ 31,579,795,238 cycles:u
+
+ 8.332272930 seconds time elapsed
+
+ 8.032124000 seconds user
+ 0.286537000 seconds sys
+
+ $
+
+ While using the BTF loader the time should be constant, as it loads
+ everything from /sys/kernel/btf/vmlinux:
+
+ $ perf stat -e cycles pahole -F btf -C tcp_sock > /dev/null
+
+ Performance counter stats for 'pahole -F btf -C tcp_sock':
+
+ 48,823,488 cycles:u
+
+ 0.024102760 seconds time elapsed
+
+ 0.012035000 seconds user
+ 0.012046000 seconds sys
+
+ $
+
+ Above I used '-F btf' just to show that it can be used, but its not
+ really needed, i.e. those are equivalent:
+
+ $ strace -e openat pahole -F btf -C list_head |& grep /sys/kernel/btf/vmlinux
+ openat(AT_FDCWD, "/sys/kernel/btf/vmlinux", O_RDONLY) = 3
+ $ strace -e openat pahole -C list_head |& grep /sys/kernel/btf/vmlinux
+ openat(AT_FDCWD, "/sys/kernel/btf/vmlinux", O_RDONLY) = 3
+ $
+
+ The btf_raw__load() function that ends up being grafted into the
+ preexisting btf_elf routines was based on libbpf's btf_load_raw().
+
+pahole:
+
+ - When the sole argument passed isn't a file, take it as a class name.
+
+ With that it becomes as compact as it gets for kernel data structures,
+ just state the name of the struct and it'll try to find that as a file,
+ not being a file it'll use /sys/kernel/btf/vmlinux and the argument as a
+ list of structs, i.e.:
+
+ $ pahole skb_ext,list_head
+ struct list_head {
+ struct list_head * next; /* 0 8 */
+ struct list_head * prev; /* 8 8 */
+
+ /* size: 16, cachelines: 1, members: 2 */
+ /* last cacheline: 16 bytes */
+ };
+ struct skb_ext {
+ refcount_t refcnt; /* 0 4 */
+ u8 offset[3]; /* 4 3 */
+ u8 chunks; /* 7 1 */
+ char data[]; /* 8 0 */
+
+ /* size: 8, cachelines: 1, members: 4 */
+ /* last cacheline: 8 bytes */
+ };
+ $ pahole hlist_node
+ struct hlist_node {
+ struct hlist_node * next; /* 0 8 */
+ struct hlist_node * * pprev; /* 8 8 */
+
+ /* size: 16, cachelines: 1, members: 2 */
+ /* last cacheline: 16 bytes */
+ };
+ $
+
+ Of course -C continues to work:
+
+ $ pahole -C inode | tail
+ __u32 i_fsnotify_mask; /* 556 4 */
+ struct fsnotify_mark_connector * i_fsnotify_marks; /* 560 8 */
+ struct fscrypt_info * i_crypt_info; /* 568 8 */
+ /* --- cacheline 9 boundary (576 bytes) --- */
+ struct fsverity_info * i_verity_info; /* 576 8 */
+ void * i_private; /* 584 8 */
+
+ /* size: 592, cachelines: 10, members: 53 */
+ /* last cacheline: 16 bytes */
+ };
+ $
+
+ - Add support for finding pointers to void, e.g.:
+
+ $ pahole --find_pointers_to void --prefix tcp
+ tcp_md5sig_pool: scratch
+ $ pahole tcp_md5sig_pool
+ struct tcp_md5sig_pool {
+ struct ahash_request * md5_req; /* 0 8 */
+ void * scratch; /* 8 8 */
+
+ /* size: 16, cachelines: 1, members: 2 */
+ /* last cacheline: 16 bytes */
+ };
+ $
+
+ - Make --contains and --find_pointers_to to work with base types
+
+ I.e. with plain 'int', 'long', 'short int', etc:
+
+ $ pahole --find_pointers_to 'short unsigned int'
+ uv_hub_info_s: socket_to_node
+ uv_hub_info_s: socket_to_pnode
+ uv_hub_info_s: pnode_to_socket
+ vc_data: vc_screenbuf
+ vc_data: vc_translate
+ filter_pred: ops
+ ext4_sb_info: s_mb_offsets
+ $ pahole ext4_sb_info | 'sort unsigned int'
+ bash: sort unsigned int: command not found...
+ ^[^C
+ $
+ $ pahole ext4_sb_info | grep 'sort unsigned int'
+ $ pahole ext4_sb_info | grep 'short unsigned int'
+ short unsigned int s_mount_state; /* 160 2 */
+ short unsigned int s_pad; /* 162 2 */
+ short unsigned int * s_mb_offsets; /* 664 8 */
+ $ pahole --contains 'short unsigned int'
+ apm_info
+ desc_ptr
+ thread_struct
+ mpc_table
+ mpc_intsrc
+ fsnotify_mark_connector
+ <SNIP>
+ sock_fprog
+ blk_mq_hw_ctx
+ skb_shared_info
+ $
+
+ - Make --contains look for more than just unions, structs, look for
+ typedefs, enums and types that descend from 'struct type':
+
+ So now we can do more interesting queries, lets see, what are the data
+ structures that embed a raw spinlock in the linux kernel?
+
+ $ pahole --contains raw_spinlock_t
+ task_struct
+ rw_semaphore
+ hrtimer_cpu_base
+ prev_cputime
+ percpu_counter
+ ratelimit_state
+ perf_event_context
+ task_delay_info
+ <SNIP>
+ lpm_trie
+ bpf_queue_stack
+ $
+
+ Look at the csets comments to see more examples.
+
+ - Make --contains and --find_pointers_to honour --unions
+
+ I.e. when looking for unions or structs that contains/embeds or looking
+ for unions/structs that have pointers to a given type.
+
+ E.g:
+
+ $ pahole --contains inet_sock
+ sctp_sock
+ inet_connection_sock
+ raw_sock
+ udp_sock
+ raw6_sock
+ $ pahole --unions --contains inet_sock
+ $
+
+ We have structs embedding 'struct inet_sock', but no unions doing that.
+
+ - Make --find_pointers_to consider unions
+
+ I.e.:
+
+ $ pahole --find_pointers_to ehci_qh
+ ehci_hcd: qh_scan_next
+ ehci_hcd: async
+ ehci_hcd: dummy
+ $
+
+ Wasn't considering:
+
+ $ pahole -C ehci_shadow
+ union ehci_shadow {
+ struct ehci_qh * qh; /* 0 8 */
+ struct ehci_itd * itd; /* 0 8 */
+ struct ehci_sitd * sitd; /* 0 8 */
+ struct ehci_fstn * fstn; /* 0 8 */
+ __le32 * hw_next; /* 0 8 */
+ void * ptr; /* 0 8 */
+ };
+ $
+
+ Fix it:
+
+ $ pahole --find_pointers_to ehci_qh
+ ehci_hcd: qh_scan_next
+ ehci_hcd: async
+ ehci_hcd: dummy
+ ehci_shadow: qh
+ $
+
+ - Consider unions when looking for classes containing some class:
+
+ I.e.:
+
+ $ pahole --contains tpacket_req
+ tpacket_req_u
+ $
+
+ Wasn't working, but should be considered with --contains/-i:
+
+ $ pahole -C tpacket_req_u
+ union tpacket_req_u {
+ struct tpacket_req req; /* 0 16 */
+ struct tpacket_req3 req3; /* 0 28 */
+ };
+ $
+
+ - Introduce --unions to consider just unions
+
+ Most filters can be used together with it, for instance to see the
+ biggest unions in the kernel:
+
+ $ pahole --unions --sizes | sort -k2 -nr | head
+ thread_union 16384 0
+ swap_header 4096 0
+ fpregs_state 4096 0
+ autofs_v5_packet_union 304 0
+ autofs_packet_union 272 0
+ pptp_ctrl_union 208 0
+ acpi_parse_object 200 0
+ acpi_descriptor 200 0
+ bpf_attr 120 0
+ phy_configure_opts 112 0
+ $
+
+ Or just some unions that have some specific prefix:
+
+ $ pahole --unions --prefix tcp
+ union tcp_md5_addr {
+ struct in_addr a4; /* 0 4 */
+ struct in6_addr a6; /* 0 16 */
+ };
+ union tcp_word_hdr {
+ struct tcphdr hdr; /* 0 20 */
+ __be32 words[5]; /* 0 20 */
+ };
+ union tcp_cc_info {
+ struct tcpvegas_info vegas; /* 0 16 */
+ struct tcp_dctcp_info dctcp; /* 0 16 */
+ struct tcp_bbr_info bbr; /* 0 20 */
+ };
+ $
+
+ What are the biggest unions in terms of number of members?
+
+ $ pahole --unions --nr_members | sort -k2 -nr | head
+ security_list_options 218
+ aml_resource 36
+ acpi_resource_data 29
+ acpi_operand_object 26
+ iwreq_data 18
+ sctp_params 15
+ ib_flow_spec 14
+ ethtool_flow_union 14
+ pptp_ctrl_union 13
+ bpf_attr 12
+ $
+
+ If you want to script most of the queries can change the separator:
+
+ $ pahole --unions --nr_members -t, | sort -t, -k2 -nr | head
+ security_list_options,218
+ aml_resource,36
+ acpi_resource_data,29
+ acpi_operand_object,26
+ iwreq_data,18
+ sctp_params,15
+ ib_flow_spec,14
+ ethtool_flow_union,14
+ pptp_ctrl_union,13
+ bpf_attr,12
+ $
+
+ - Fix -m/--nr_methods - Number of functions operating on a type pointer
+
+ We had to use the same hack as in pfunct, as implemented in ccf3eebfcd9c
+ ("btf_loader: Add support for BTF_KIND_FUNC"), will hide that 'struct
+ ftype' (aka function prototype) indirection behind the parameter
+ iterator (function__for_each_parameter).
+
+ For now, here is the top 10 Linux kernel data structures in terms of
+ number of functions receiving as one of its parameters a pointer to it,
+ using /sys/kernel/btf/vmlinux to look at all the vmlinux types and
+ functions (the ones visible in kallsyms, but with the parameters and its
+ types):
+
+ $ pahole -m | sort -k2 -nr | head
+ device 955
+ sock 568
+ sk_buff 541
+ task_struct 437
+ inode 421
+ pci_dev 390
+ page 351
+ net_device 347
+ file 315
+ net 312
+ $
+ $ pahole --help |& grep -- -m
+ -m, --nr_methods show number of methods
+ $
+
+ - Do not require a class name to operate without a file name
+
+ Since we default to operating on the running kernel data structures, we
+ should make the default to, with no options passed, to pretty print all
+ the running kernel data structures, or do what was asked in terms of
+ number of members, size of structs, etc, i.e.:
+
+ # pahole --help |& head
+ Usage: pahole [OPTION...] FILE
+
+ -a, --anon_include include anonymous classes
+ -A, --nested_anon_include include nested (inside other structs) anonymous
+ classes
+ -B, --bit_holes=NR_HOLES Show only structs at least NR_HOLES bit holes
+ -c, --cacheline_size=SIZE set cacheline size to SIZE
+ --classes_as_structs Use 'struct' when printing classes
+ -C, --class_name=CLASS_NAME Show just this class
+ -d, --recursive recursive mode, affects several other flags
+ #
+
+ Continues working as before, but if you do:
+
+ pahole
+
+ It will work just as if you did:
+
+ pahole vmlinux
+
+ and that vmlinux file is the running kernel vmlinux.
+
+ And since the default now is to read BTF info, then it will do all its
+ operations on /sys/kernel/btf/vmlinux, when present, i.e. want to know
+ what are the fattest data structures in the running kernel:
+
+ # pahole -s | sort -k2 -nr | head
+ cmp_data 290904 1
+ dec_data 274520 1
+ cpu_entry_area 217088 0
+ pglist_data 172928 4
+ saved_cmdlines_buffer 131104 1
+ debug_store_buffers 131072 0
+ hid_parser 110848 1
+ hid_local 110608 0
+ zonelist 81936 0
+ e820_table 64004 0
+ #
+
+ How many data structures in the running kernel vmlinux area embbed
+ 'struct list_head'?
+
+ # pahole -i list_head | wc -l
+ 260
+ #
+
+ Lets see some of those?
+
+ # pahole -C fsnotify_event
+ struct fsnotify_event {
+ struct list_head list; /* 0 16 */
+ struct inode * inode; /* 16 8 */
+
+ /* size: 24, cachelines: 1, members: 2 */
+ /* last cacheline: 24 bytes */
+ };
+ # pahole -C audit_chunk
+ struct audit_chunk {
+ struct list_head hash; /* 0 16 */
+ long unsigned int key; /* 16 8 */
+ struct fsnotify_mark * mark; /* 24 8 */
+ struct list_head trees; /* 32 16 */
+ int count; /* 48 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
+ atomic_long_t refs; /* 56 8 */
+ /* --- cacheline 1 boundary (64 bytes) --- */
+ struct callback_head head; /* 64 16 */
+ struct node owners[]; /* 80 0 */
+
+ /* size: 80, cachelines: 2, members: 8 */
+ /* sum members: 76, holes: 1, sum holes: 4 */
+ /* last cacheline: 16 bytes */
+ };
+ #
diff --git a/changes-v1.18 b/changes-v1.18
new file mode 100644
index 0000000..9b52efa
--- /dev/null
+++ b/changes-v1.18
@@ -0,0 +1,117 @@
+v1.18:
+
+- Use type information to pretty print raw data from stdin, all
+ documented in the man pages, further information in the csets.
+
+ TLDRish: this almost completely deciphers a perf.data file:
+
+ $ pahole ~/bin/perf --header=perf_file_header \
+ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data
+
+ What the above command line does:
+
+ This will state that a 'struct perf_file_header' is available in BTF or DWARF
+ in the ~/bin/perf file and that at the start of stdin it should be used to decode
+ sizeof(struct perf_file_header) bytes, pretty printing it according to its members.
+
+ Furthermore, that header can be referenced later in the command line, for instance
+ that 'range=data' means that in the header, it expects a 'range' member in
+ 'struct perf_file_header' to be used:
+
+ $ pahole ~/bin/perf --header=perf_file_header < perf.data
+ {
+ .magic = 3622385352885552464,
+ .size = 104,
+ .attr_size = 136,
+ .attrs = {
+ .offset = 296,
+ .size = 136,
+ },
+ .data = {
+ .offset = 432,
+ .size = 14688,
+ },
+ .event_types = {
+ .offset = 0,
+ .size = 0,
+ },
+ .adds_features = { 376537084, 0, 0, 0 },
+ },
+ $
+
+ That 'range' field is expected to have 'offset' and 'size' fields, so that
+ it can go on decoding a number of 'struct perf_event_header' entries.
+
+ That 'sizeof' in turn expects that in 'struct perf_event_header' there is a
+ 'size' field stating how long that particular record is, one can also use
+ 'sizeof=some_other_member_name'.
+
+ This supports variable sized records and then the 'type' field expects there
+ is a 'struct perf_event_type' member named 'type' (again, type=something_else
+ may be used. Finally, the value in the 'type' field is used to lookup an entry
+ in the set formed by the two enumerations specified in the 'type_enum=' argument.
+
+ If we look at these enums we'll see that its entries have names that can be,
+ when lowercased, point to structs containing the layout for the variable sized
+ record, which allows it to cast and produce the right pretty printed output.
+
+ I.e. using the kernel BTF info we get:
+
+ $ pahole perf_event_type
+ enum perf_event_type {
+ PERF_RECORD_MMAP = 1,
+ PERF_RECORD_LOST = 2,
+ PERF_RECORD_COMM = 3,
+ PERF_RECORD_EXIT = 4,
+ PERF_RECORD_THROTTLE = 5,
+ PERF_RECORD_UNTHROTTLE = 6,
+ PERF_RECORD_FORK = 7,
+ PERF_RECORD_READ = 8,
+ PERF_RECORD_SAMPLE = 9,
+ PERF_RECORD_MMAP2 = 10,
+ PERF_RECORD_AUX = 11,
+ PERF_RECORD_ITRACE_START = 12,
+ PERF_RECORD_LOST_SAMPLES = 13,
+ PERF_RECORD_SWITCH = 14,
+ PERF_RECORD_SWITCH_CPU_WIDE = 15,
+ PERF_RECORD_NAMESPACES = 16,
+ PERF_RECORD_KSYMBOL = 17,
+ PERF_RECORD_BPF_EVENT = 18,
+ PERF_RECORD_CGROUP = 19,
+ PERF_RECORD_TEXT_POKE = 20,
+ PERF_RECORD_MAX = 21,
+ };
+ $
+
+ That is the same as in ~/bin/perf, and, if we get one of these and ask for
+ that struct:
+
+ $ pahole -C perf_record_mmap ~/bin/perf
+ struct perf_record_mmap {
+ struct perf_event_header header; /* 0 8 */
+ __u32 pid; /* 8 4 */
+ __u32 tid; /* 12 4 */
+ __u64 start; /* 16 8 */
+ __u64 len; /* 24 8 */
+ __u64 pgoff; /* 32 8 */
+ char filename[4096]; /* 40 4096 */
+
+ /* size: 4136, cachelines: 65, members: 7 */
+ /* last cacheline: 40 bytes */
+ };
+ $
+
+ Many other options were introduced to work with this, including --count,
+ --skip, etc, look at the man page for details.
+
+- Store percpu variables in vmlinux BTF. This can be disabled when debugging
+ kernel features being developed to use it.
+
+- pahole now should be segfault free when handling gdb test suit DWARF
+ files, including ADA, FORTRAN, rust and dwp compressed files, the
+ later being just flatly refused, that got left for v1.19.
+
+- Bail out on partial units for now, avoiding segfaults and providing warning
+ to user, hopefully will be addressed in v1.19.
+
+Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
diff --git a/changes-v1.19 b/changes-v1.19
new file mode 100644
index 0000000..9861654
--- /dev/null
+++ b/changes-v1.19
@@ -0,0 +1,134 @@
+v1.19:
+
+- Support split BTF, where a main BTF file, vmlinux, can be used to find types
+ and then a kernel module, for instance, can have just what is unique to it.
+
+ For instance, looking for a type in the main vmlinux BTF info:
+
+ $ pahole wmi_notify_handler
+ pahole: type 'wmi_notify_handler' not found
+ $
+
+ If we look at the 'wmi' module BTF info that is in:
+
+ $ ls -la /sys/kernel/btf/wmi
+ -r--r--r--. 1 root root 2866 Nov 18 13:35 /sys/kernel/btf/wmi
+ $
+
+ $ pahole /sys/kernel/btf/wmi -C wmi_notify_handler
+ typedef void (*wmi_notify_handler)(u32, void *);
+ $
+
+ '--btf_base=/sys/kernel/btf/vmlinux' was automatically added in this last
+ example, an option that was also introduced in this version where types used in
+ the wmi.ko module but present in vmlinux can be found so that there is no
+ duplicity of types.
+
+- Update libbpf to get the split BTF support and use some of its functions to
+ load BTF and speed up DWARF loading and BTF encoding.
+
+- Support cross-compiled ELF binaries with different endianness
+
+- Support showing typedefs for anonymous types, like structs, unions and enums,
+ see the "Align enumerators" entry below for an example, another:
+
+ $ pahole rwlock_t
+ typedef struct {
+ arch_rwlock_t raw_lock; /* 0 8 */
+
+ /* size: 8, cachelines: 1, members: 1 */
+ /* last cacheline: 8 bytes */
+ } rwlock_t;
+ $
+
+- Align enumerators:
+
+ $ pahole ZSTD_strategy
+ typedef enum {
+ ZSTD_fast = 0,
+ ZSTD_dfast = 1,
+ ZSTD_greedy = 2,
+ ZSTD_lazy = 3,
+ ZSTD_lazy2 = 4,
+ ZSTD_btlazy2 = 5,
+ ZSTD_btopt = 6,
+ ZSTD_btopt2 = 7,
+ } ZSTD_strategy;
+ $
+
+- Workaround bugs in the generation of DWARF records for functions in some gcc
+ versions that were causing breakage in the encoding of BTF:
+
+ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97060 "Missing DW_AT_declaration=1 in dwarf data"
+
+- Ignore zero-sized ELF symbols instead of erroring out.
+
+- Handle union forward declaration properly in the BTF loader.
+
+- Introduce --numeric_version for use in scripts and Makefiles:
+
+ $ pahole --version
+ v1.19
+ $ pahole --numeric_version
+ 119
+ $
+
+ To avoid things like this in the kernel's scripts/link-vmlinux.sh:
+
+ pahole_ver=$(${PAHOLE} --version | sed -E 's/v([0-9]+)\.([0-9]+)/\1\2/')
+
+- Try sole pfunct argument as a function name, just like pahole with type names:
+
+ $ pfunct tcp_v4_rcv
+ int tcp_v4_rcv(struct sk_buff * skb);
+ $
+
+- Speed up pfunct using some of the load techniques used in pahole.
+
+- Discard CUs after BTF encoding as they're not used anymore, greatly reducing
+ memory usage and speeding up vmlinux BTF encoding.
+
+- Revamp how per-CPU variables are encoded in BTF.
+
+- Include BTF info for static functions.
+
+- Use BTF's string APIs for strings management, greatly improving performance
+ over the tsearch().
+
+- Increase size of DWARF lookup hash table, shaving off about 1 second out of
+ about 20 seconds total for Linux BTF dedup.
+
+- Stop BTF encoding when errors are found in some DWARF CU.
+
+- Implement --packed, to show just packed structures, for instance, here are
+ the top 5 packed data structures in the Linux kernel:
+
+ $ pahole --sizes --packed | sort -k2 -nr | head -5
+ e820_table 64004 0
+ boot_params 4096 0
+ efi_variable 2084 0
+ snd_soc_tplg_pcm 912 0
+ ntb_info_regs 800 0
+ $
+
+ And here is one of them:
+
+ $ pahole efi_variable
+ struct efi_variable {
+ efi_char16_t VariableName[512]; /* 0 1024 */
+ /* --- cacheline 16 boundary (1024 bytes) --- */
+ efi_guid_t VendorGuid; /* 1024 16 */
+ long unsigned int DataSize; /* 1040 8 */
+ __u8 Data[1024]; /* 1048 1024 */
+ /* --- cacheline 32 boundary (2048 bytes) was 24 bytes ago --- */
+ efi_status_t Status; /* 2072 8 */
+ __u32 Attributes; /* 2080 4 */
+
+ /* size: 2084, cachelines: 33, members: 6 */
+ /* last cacheline: 36 bytes */
+ } __attribute__((__packed__));
+ $
+
+- Fix bug in distros such as OpenSUSE:15.2 where DW_AT_alignment isn't defined.
+
+Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
diff --git a/cmake/modules/FindDWARF.cmake b/cmake/modules/FindDWARF.cmake
new file mode 100644
index 0000000..027d06e
--- /dev/null
+++ b/cmake/modules/FindDWARF.cmake
@@ -0,0 +1,101 @@
+# - Find Dwarf
+# Find the dwarf.h header from elf utils
+#
+# DWARF_INCLUDE_DIR - where to find dwarf.h, etc.
+# DWARF_LIBRARIES - List of libraries when using elf utils.
+# DWARF_FOUND - True if fdo found.
+
+message(STATUS "Checking availability of DWARF and ELF development libraries")
+
+INCLUDE(CheckLibraryExists)
+
+if (DWARF_INCLUDE_DIR AND LIBDW_INCLUDE_DIR AND DWARF_LIBRARY AND ELF_LIBRARY)
+ # Already in cache, be silent
+ set(DWARF_FIND_QUIETLY TRUE)
+endif (DWARF_INCLUDE_DIR AND LIBDW_INCLUDE_DIR AND DWARF_LIBRARY AND ELF_LIBRARY)
+
+find_path(DWARF_INCLUDE_DIR dwarf.h
+ /usr/include
+ /usr/local/include
+ /usr/include/libdwarf
+ ~/usr/local/include
+)
+
+find_path(LIBDW_INCLUDE_DIR elfutils/libdw.h
+ /usr/include
+ /usr/local/include
+ ~/usr/local/include
+)
+
+find_library(DWARF_LIBRARY
+ NAMES dw dwarf
+ PATHS /usr/lib /usr/local/lib /usr/lib64 /usr/local/lib64 ~/usr/local/lib ~/usr/local/lib64
+)
+
+find_library(ELF_LIBRARY
+ NAMES elf
+ PATHS /usr/lib /usr/local/lib /usr/lib64 /usr/local/lib64 ~/usr/local/lib ~/usr/local/lib64
+)
+
+if (DWARF_INCLUDE_DIR AND LIBDW_INCLUDE_DIR AND DWARF_LIBRARY AND ELF_LIBRARY)
+ set(DWARF_FOUND TRUE)
+ set(DWARF_LIBRARIES ${DWARF_LIBRARY} ${ELF_LIBRARY})
+
+ set(CMAKE_REQUIRED_LIBRARIES ${DWARF_LIBRARIES})
+ # check if libdw have the dwfl_module_build_id routine, i.e. if it supports the buildid
+ # mechanism to match binaries to detached debug info sections (the -debuginfo packages
+ # in distributions such as fedora). We do it against libelf because, IIRC, some distros
+ # include libdw linked statically into libelf.
+ check_library_exists(elf dwfl_module_build_id "" HAVE_DWFL_MODULE_BUILD_ID)
+else (DWARF_INCLUDE_DIR AND LIBDW_INCLUDE_DIR AND DWARF_LIBRARY AND ELF_LIBRARY)
+ set(DWARF_FOUND FALSE)
+ set(DWARF_LIBRARIES)
+endif (DWARF_INCLUDE_DIR AND LIBDW_INCLUDE_DIR AND DWARF_LIBRARY AND ELF_LIBRARY)
+
+if (DWARF_FOUND)
+ if (NOT DWARF_FIND_QUIETLY)
+ message(STATUS "Found dwarf.h header: ${DWARF_INCLUDE_DIR}")
+ message(STATUS "Found elfutils/libdw.h header: ${LIBDW_INCLUDE_DIR}")
+ message(STATUS "Found libdw library: ${DWARF_LIBRARY}")
+ message(STATUS "Found libelf library: ${ELF_LIBRARY}")
+ endif (NOT DWARF_FIND_QUIETLY)
+else (DWARF_FOUND)
+ if (DWARF_FIND_REQUIRED)
+ # Check if we are in a Red Hat (RHEL) or Fedora system to tell
+ # exactly which packages should be installed. Please send
+ # patches for other distributions.
+ find_path(FEDORA fedora-release /etc)
+ find_path(REDHAT redhat-release /etc)
+ if (FEDORA OR REDHAT)
+ if (NOT DWARF_INCLUDE_DIR OR NOT LIBDW_INCLUDE_DIR)
+ message(STATUS "Please install the elfutils-devel package")
+ endif (NOT DWARF_INCLUDE_DIR OR NOT LIBDW_INCLUDE_DIR)
+ if (NOT DWARF_LIBRARY)
+ message(STATUS "Please install the elfutils-libs package")
+ endif (NOT DWARF_LIBRARY)
+ if (NOT ELF_LIBRARY)
+ message(STATUS "Please install the elfutils-libelf package")
+ endif (NOT ELF_LIBRARY)
+ else (FEDORA OR REDHAT)
+ if (NOT DWARF_INCLUDE_DIR)
+ message(STATUS "Could NOT find dwarf include dir")
+ endif (NOT DWARF_INCLUDE_DIR)
+ if (NOT LIBDW_INCLUDE_DIR)
+ message(STATUS "Could NOT find libdw include dir")
+ endif (NOT LIBDW_INCLUDE_DIR)
+ if (NOT DWARF_LIBRARY)
+ message(STATUS "Could NOT find libdw library")
+ endif (NOT DWARF_LIBRARY)
+ if (NOT ELF_LIBRARY)
+ message(STATUS "Could NOT find libelf library")
+ endif (NOT ELF_LIBRARY)
+ endif (FEDORA OR REDHAT)
+ message(FATAL_ERROR "Could NOT find some ELF and DWARF libraries, please install the missing packages")
+ endif (DWARF_FIND_REQUIRED)
+endif (DWARF_FOUND)
+
+mark_as_advanced(DWARF_INCLUDE_DIR LIBDW_INCLUDE_DIR DWARF_LIBRARY ELF_LIBRARY)
+include_directories(${DWARF_INCLUDE_DIR} ${LIBDW_INCLUDE_DIR})
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_SOURCE_DIR}/config.h)
+
+message(STATUS "Checking availability of DWARF and ELF development libraries - done")
diff --git a/codiff.c b/codiff.c
new file mode 100644
index 0000000..264b0af
--- /dev/null
+++ b/codiff.c
@@ -0,0 +1,859 @@
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2006 Mandriva Conectiva S.A.
+ Copyright (C) 2006 Arnaldo Carvalho de Melo <acme@mandriva.com>
+*/
+
+#include <argp.h>
+#include <assert.h>
+#include <dwarf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "dwarves.h"
+#include "dutil.h"
+
+static int show_struct_diffs;
+static int show_function_diffs;
+static int verbose;
+static int quiet;
+static int show_terse_type_changes;
+
+static struct conf_load conf_load = {
+ .get_addr_info = true,
+};
+
+static struct strlist *structs_printed;
+
+#define TCHANGEF__SIZE (1 << 0)
+#define TCHANGEF__NR_MEMBERS (1 << 1)
+#define TCHANGEF__TYPE (1 << 2)
+#define TCHANGEF__OFFSET (1 << 3)
+#define TCHANGEF__BIT_OFFSET (1 << 4)
+#define TCHANGEF__BIT_SIZE (1 << 5)
+#define TCHANGEF__PADDING (1 << 6)
+#define TCHANGEF__NR_HOLES (1 << 7)
+#define TCHANGEF__NR_BIT_HOLES (1 << 8)
+
+static uint32_t terse_type_changes;
+
+static uint32_t total_cus_changed;
+static uint32_t total_nr_functions_changed;
+static uint32_t total_function_bytes_added;
+static uint32_t total_function_bytes_removed;
+
+struct diff_info {
+ const struct tag *tag;
+ const struct cu *cu;
+ int32_t diff;
+};
+
+static struct diff_info *diff_info__new(const struct tag *twin,
+ const struct cu *cu,
+ int32_t diff)
+{
+ struct diff_info *dinfo = malloc(sizeof(*dinfo));
+
+ if (dinfo == NULL) {
+ puts("out of memory!");
+ exit(1);
+ }
+ dinfo->tag = twin;
+ dinfo->cu = cu;
+ dinfo->diff = diff;
+ return dinfo;
+}
+
+static void cu__check_max_len_changed_item(struct cu *cu, const char *name,
+ uint8_t addend)
+{
+ const uint32_t len = strlen(name) + addend;
+
+ if (len > cu->max_len_changed_item)
+ cu->max_len_changed_item = len;
+}
+
+static void diff_function(const struct cu *new_cu, struct function *function,
+ struct cu *cu)
+{
+ struct tag *new_tag;
+ const char *name;
+
+ if (function->inlined || function->abstract_origin != 0)
+ return;
+
+ name = function__name(function, cu);
+ new_tag = cu__find_function_by_name(new_cu, name);
+ if (new_tag != NULL) {
+ struct function *new_function = tag__function(new_tag);
+ int32_t diff = (function__size(new_function) -
+ function__size(function));
+ if (diff != 0) {
+ function->priv = diff_info__new(&new_function->proto.tag, new_cu,
+ diff);
+ cu__check_max_len_changed_item(cu, name, 0);
+
+ ++cu->nr_functions_changed;
+ if (diff > 0)
+ cu->function_bytes_added += diff;
+ else
+ cu->function_bytes_removed += -diff;
+ } else {
+ char proto[1024], twin_proto[1024];
+
+ if (strcmp(function__prototype(function, cu,
+ proto, sizeof(proto)),
+ function__prototype(new_function, new_cu,
+ twin_proto,
+ sizeof(twin_proto))) != 0) {
+ ++cu->nr_functions_changed;
+ function->priv = diff_info__new(function__tag(new_function),
+ new_cu, 0);
+ }
+ }
+ } else {
+ const uint32_t diff = -function__size(function);
+
+ cu__check_max_len_changed_item(cu, name, 0);
+ function->priv = diff_info__new(NULL, NULL, diff);
+ ++cu->nr_functions_changed;
+ cu->function_bytes_removed += -diff;
+ }
+}
+
+static int check_print_change(const struct class_member *old,
+ const struct cu *old_cu,
+ const struct class_member *new,
+ const struct cu *new_cu,
+ int print)
+{
+ size_t old_size, new_size;
+ char old_type_name[128], new_type_name[128];
+ const struct tag *old_type = cu__type(old_cu, old->tag.type);
+ const struct tag *new_type = cu__type(new_cu, new->tag.type);
+ int changes = 0;
+
+ if (old_type == NULL || new_type == NULL)
+ return 0;
+
+ old_size = old->byte_size;
+ new_size = new->byte_size;
+ if (old_size != new_size)
+ changes = 1;
+
+ if (old->byte_offset != new->byte_offset) {
+ changes = 1;
+ terse_type_changes |= TCHANGEF__OFFSET;
+ }
+
+ if (old->bitfield_offset != new->bitfield_offset) {
+ changes = 1;
+ terse_type_changes |= TCHANGEF__BIT_OFFSET;
+ }
+
+ if (old->bitfield_size != new->bitfield_size) {
+ changes = 1;
+ terse_type_changes |= TCHANGEF__BIT_SIZE;
+ }
+
+ if (strcmp(tag__name(old_type, old_cu, old_type_name,
+ sizeof(old_type_name), NULL),
+ tag__name(new_type, new_cu, new_type_name,
+ sizeof(new_type_name), NULL)) != 0) {
+ changes = 1;
+ terse_type_changes |= TCHANGEF__TYPE;
+ }
+
+ if (changes && print && !show_terse_type_changes)
+ printf(" %s\n"
+ " from: %-21s /* %5u(%2u) %5zd(%2d) */\n"
+ " to: %-21s /* %5u(%2u) %5zd(%2u) */\n",
+ class_member__name(old, old_cu),
+ old_type_name, old->byte_offset, old->bitfield_offset,
+ old_size, old->bitfield_size,
+ new_type_name, new->byte_offset, new->bitfield_offset,
+ new_size, new->bitfield_size);
+
+ return changes;
+}
+
+static struct class_member *class__find_pair_member(const struct class *structure, const struct cu *cu,
+ const struct class_member *pair_member, const struct cu *pair_cu,
+ int *nr_anonymousp)
+{
+ const char *member_name = class_member__name(pair_member, pair_cu);
+ struct class_member *member;
+
+ if (member_name)
+ return class__find_member_by_name(structure, cu, member_name);
+
+ int nr_anonymous = ++*nr_anonymousp;
+
+ /* Unnamed struct or union, lets look for the first unammed matchin tag.type */
+
+ type__for_each_member(&structure->type, member) {
+ if (member->tag.tag == pair_member->tag.tag && /* Both are class/union/struct (unnamed) */
+ class_member__name(member, cu) == member_name && /* Both are NULL? */
+ --nr_anonymous == 0)
+ return member;
+ }
+
+ return NULL;
+}
+
+static int check_print_members_changes(const struct class *structure,
+ const struct cu *cu,
+ const struct class *new_structure,
+ const struct cu *new_cu,
+ int print)
+{
+ int changes = 0, nr_anonymous = 0;
+ struct class_member *member;
+ uint16_t nr_twins_found = 0;
+
+ type__for_each_member(&structure->type, member) {
+ struct class_member *twin = class__find_pair_member(new_structure, new_cu, member, cu, &nr_anonymous);
+ if (twin != NULL) {
+ twin->tag.visited = 1;
+ ++nr_twins_found;
+ if (check_print_change(member, cu, twin, new_cu, print))
+ changes = 1;
+ } else {
+ changes = 1;
+ if (print) {
+ char name[128];
+ struct tag *type;
+ type = cu__type(cu, member->tag.type);
+ printf(" %s\n"
+ " removed: %-21s /* %5u(%2u) %5zd(%2d) */\n",
+ class_member__name(member, cu),
+ tag__name(type, cu, name, sizeof(name), NULL),
+ member->byte_offset, member->bitfield_offset,
+ member->byte_size, member->bitfield_size);
+ }
+ }
+ }
+
+ if (nr_twins_found == (new_structure->type.nr_members +
+ new_structure->type.nr_static_members))
+ goto out;
+
+ changes = 1;
+ if (!print)
+ goto out;
+
+ type__for_each_member(&new_structure->type, member) {
+ if (!member->tag.visited) {
+ char name[128];
+ struct tag *type;
+ type = cu__type(new_cu, member->tag.type);
+ printf(" %s\n"
+ " added: %-21s /* %5u(%2u) %5zd(%2d) */\n",
+ class_member__name(member, new_cu),
+ tag__name(type, new_cu, name, sizeof(name), NULL),
+ member->byte_offset, member->bitfield_offset,
+ member->byte_size, member->bitfield_size);
+ }
+ }
+out:
+ return changes;
+}
+
+static void diff_struct(const struct cu *new_cu, struct class *structure,
+ struct cu *cu)
+{
+ struct tag *new_tag;
+ struct class *new_structure = NULL;
+ int32_t diff;
+
+ assert(class__is_struct(structure));
+
+ if (class__size(structure) == 0 || class__name(structure, cu) == NULL)
+ return;
+
+ new_tag = cu__find_struct_by_name(new_cu,
+ class__name(structure, cu), 0, NULL);
+ if (new_tag == NULL)
+ return;
+
+ new_structure = tag__class(new_tag);
+ if (class__size(new_structure) == 0)
+ return;
+
+ assert(class__is_struct(new_structure));
+
+ diff = class__size(structure) != class__size(new_structure) ||
+ class__nr_members(structure) != class__nr_members(new_structure) ||
+ check_print_members_changes(structure, cu,
+ new_structure, new_cu, 0) ||
+ structure->padding != new_structure->padding ||
+ structure->nr_holes != new_structure->nr_holes ||
+ structure->nr_bit_holes != new_structure->nr_bit_holes;
+
+ if (diff == 0)
+ return;
+
+ ++cu->nr_structures_changed;
+ cu__check_max_len_changed_item(cu, class__name(structure, cu),
+ sizeof("struct"));
+ structure->priv = diff_info__new(class__tag(new_structure),
+ new_cu, diff);
+}
+
+static struct cu *cus__find_pair(struct cus *cus, const char *name)
+{
+ if (cus->nr_entries == 1)
+ return list_first_entry(&cus->cus, struct cu, node);
+
+ return cus__find_cu_by_name(cus, name);
+}
+
+static int cu_find_new_tags_iterator(struct cu *new_cu, void *old_cus)
+{
+ struct cu *old_cu = cus__find_pair(old_cus, new_cu->name);
+
+ if (old_cu != NULL && cu__same_build_id(old_cu, new_cu))
+ return 0;
+
+ struct function *function;
+ uint32_t id;
+ cu__for_each_function(new_cu, id, function) {
+ /*
+ * We're not interested in aliases, just real function definitions,
+ * where we'll know if the kind of inlining
+ */
+ if (function->abstract_origin || function->inlined)
+ continue;
+
+ const char *name = function__name(function, new_cu);
+ struct tag *old_function = cu__find_function_by_name(old_cu,
+ name);
+ if (old_function != NULL && !tag__function(old_function)->inlined)
+ continue;
+
+ const int32_t diff = function__size(function);
+
+ cu__check_max_len_changed_item(new_cu, name, 0);
+ ++new_cu->nr_functions_changed;
+ new_cu->function_bytes_added += diff;
+ function->priv = diff_info__new(old_function, new_cu, diff);
+ }
+
+ struct class *class;
+ cu__for_each_struct(new_cu, id, class) {
+ const char *name = class__name(class, new_cu);
+ if (name == NULL || class__size(class) == 0 ||
+ cu__find_struct_by_name(old_cu, name, 0, NULL))
+ continue;
+
+ class->priv = diff_info__new(NULL, NULL, 1);
+ ++new_cu->nr_structures_changed;
+
+ cu__check_max_len_changed_item(new_cu, name, sizeof("struct"));
+ }
+
+ return 0;
+}
+
+static int cu_diff_iterator(struct cu *cu, void *new_cus)
+{
+ struct cu *new_cu = cus__find_pair(new_cus, cu->name);
+
+ if (new_cu != NULL && cu__same_build_id(cu, new_cu))
+ return 0;
+
+ uint32_t id;
+ struct class *class;
+ cu__for_each_struct(cu, id, class)
+ diff_struct(new_cu, class, cu);
+
+ struct function *function;
+ cu__for_each_function(cu, id, function)
+ diff_function(new_cu, function, cu);
+
+ return 0;
+}
+
+static void show_diffs_function(struct function *function, const struct cu *cu,
+ const void *cookie)
+{
+ const struct diff_info *di = function->priv;
+
+ printf(" %-*.*s | %+4d",
+ (int)cu->max_len_changed_item, (int)cu->max_len_changed_item,
+ function__name(function, cu), di->diff);
+
+ if (!verbose) {
+ putchar('\n');
+ return;
+ }
+
+ if (di->tag == NULL)
+ puts(cookie ? " (added)" : " (removed)");
+ else {
+ struct function *twin = tag__function(di->tag);
+
+ if (twin->inlined)
+ puts(cookie ? " (uninlined)" : " (inlined)");
+ else if (strcmp(function__name(function, cu),
+ function__name(twin, di->cu)) != 0)
+ printf("%s: BRAIN FART ALERT: comparing %s to %s, "
+ "should be the same name\n", __FUNCTION__,
+ function__name(function, cu),
+ function__name(twin, di->cu));
+ else {
+ char proto[1024], twin_proto[1024];
+
+ printf(" # %d -> %d", function__size(function),
+ function__size(twin));
+ if (function->lexblock.nr_lexblocks !=
+ twin->lexblock.nr_lexblocks)
+ printf(", lexblocks: %d -> %d",
+ function->lexblock.nr_lexblocks,
+ twin->lexblock.nr_lexblocks);
+ if (function->lexblock.nr_inline_expansions !=
+ twin->lexblock.nr_inline_expansions)
+ printf(", # inlines: %d -> %d",
+ function->lexblock.nr_inline_expansions,
+ twin->lexblock.nr_inline_expansions);
+ if (function->lexblock.size_inline_expansions !=
+ twin->lexblock.size_inline_expansions)
+ printf(", size inlines: %d -> %d",
+ function->lexblock.size_inline_expansions,
+ twin->lexblock.size_inline_expansions);
+
+ if (strcmp(function__prototype(function, cu,
+ proto, sizeof(proto)),
+ function__prototype(twin, di->cu,
+ twin_proto, sizeof(twin_proto))) != 0)
+ printf(", prototype: %s -> %s", proto, twin_proto);
+ putchar('\n');
+ }
+ }
+}
+
+static void show_changed_member(char change, const struct class_member *member,
+ const struct cu *cu)
+{
+ const struct tag *type = cu__type(cu, member->tag.type);
+ char bf[128];
+
+ tag__assert_search_result(type);
+ printf(" %c%-26s %-21s /* %5u %5zd */\n",
+ change, tag__name(type, cu, bf, sizeof(bf), NULL),
+ class_member__name(member, cu),
+ member->byte_offset, member->byte_size);
+}
+
+static void show_nr_members_changes(const struct class *structure,
+ const struct cu *cu,
+ const struct class *new_structure,
+ const struct cu *new_cu)
+{
+ struct class_member *member;
+ int nr_anonymous = 0;
+
+ /* Find the removed ones */
+ type__for_each_member(&structure->type, member) {
+ struct class_member *twin = class__find_pair_member(new_structure, new_cu, member, cu, &nr_anonymous);
+ if (twin == NULL)
+ show_changed_member('-', member, cu);
+ }
+
+ nr_anonymous = 0;
+ /* Find the new ones */
+ type__for_each_member(&new_structure->type, member) {
+ struct class_member *twin = class__find_pair_member(structure, cu, member, new_cu, &nr_anonymous);
+ if (twin == NULL)
+ show_changed_member('+', member, new_cu);
+ }
+}
+
+static void print_terse_type_changes(struct class *structure,
+ const struct cu *cu)
+{
+ const char *sep = "";
+
+ printf("struct %s: ", class__name(structure, cu));
+
+ if (terse_type_changes & TCHANGEF__SIZE) {
+ fputs("size", stdout);
+ sep = ", ";
+ }
+ if (terse_type_changes & TCHANGEF__NR_MEMBERS) {
+ printf("%snr_members", sep);
+ sep = ", ";
+ }
+ if (terse_type_changes & TCHANGEF__TYPE) {
+ printf("%stype", sep);
+ sep = ", ";
+ }
+ if (terse_type_changes & TCHANGEF__OFFSET) {
+ printf("%soffset", sep);
+ sep = ", ";
+ }
+ if (terse_type_changes & TCHANGEF__BIT_OFFSET) {
+ printf("%sbit_offset", sep);
+ sep = ", ";
+ }
+ if (terse_type_changes & TCHANGEF__BIT_SIZE) {
+ printf("%sbit_size", sep);
+ sep = ", ";
+ }
+ if (terse_type_changes & TCHANGEF__PADDING) {
+ printf("%spadding", sep);
+ sep = ", ";
+ }
+ if (terse_type_changes & TCHANGEF__NR_HOLES) {
+ printf("%snr_holes", sep);
+ sep = ", ";
+ }
+ if (terse_type_changes & TCHANGEF__NR_BIT_HOLES)
+ printf("%snr_bit_holes", sep);
+
+ putchar('\n');
+}
+
+static void show_diffs_structure(struct class *structure,
+ const struct cu *cu)
+{
+ const struct diff_info *di = structure->priv;
+ const struct class *new_structure;
+ int diff;
+ /*
+ * This is when the struct was not present in the new object file.
+ * Meaning that it either was not referenced or that it was completely
+ * removed.
+ */
+ if (di == NULL)
+ return;
+
+ new_structure = tag__class(di->tag);
+ /*
+ * If there is a diff_info but its di->tag is NULL we have a new structure,
+ * one that didn't appears in the old object. See find_new_classes_iterator.
+ */
+ if (new_structure == NULL)
+ diff = class__size(structure);
+ else
+ diff = class__size(new_structure) - class__size(structure);
+
+ terse_type_changes = 0;
+
+ if (!show_terse_type_changes)
+ printf(" struct %-*.*s | %+4d\n",
+ (int)(cu->max_len_changed_item - sizeof("struct")),
+ (int)(cu->max_len_changed_item - sizeof("struct")),
+ class__name(structure, cu), diff);
+
+ if (diff != 0)
+ terse_type_changes |= TCHANGEF__SIZE;
+
+ if (!verbose && !show_terse_type_changes)
+ return;
+
+ if (new_structure == NULL)
+ diff = -class__nr_members(structure);
+ else
+ diff = (class__nr_members(new_structure) -
+ class__nr_members(structure));
+ if (diff != 0) {
+ terse_type_changes |= TCHANGEF__NR_MEMBERS;
+ if (!show_terse_type_changes) {
+ printf(" nr_members: %+d\n", diff);
+ if (new_structure != NULL)
+ show_nr_members_changes(structure, cu,
+ new_structure, di->cu);
+ }
+ }
+ if (new_structure != NULL) {
+ diff = (int)new_structure->padding - (int)structure->padding;
+ if (diff) {
+ terse_type_changes |= TCHANGEF__PADDING;
+ if (!show_terse_type_changes)
+ printf(" padding: %+d\n", diff);
+ }
+ diff = (int)new_structure->nr_holes - (int)structure->nr_holes;
+ if (diff) {
+ terse_type_changes |= TCHANGEF__NR_HOLES;
+ if (!show_terse_type_changes)
+ printf(" nr_holes: %+d\n", diff);
+ }
+ diff = ((int)new_structure->nr_bit_holes -
+ (int)structure->nr_bit_holes);
+ if (structure->nr_bit_holes != new_structure->nr_bit_holes) {
+ terse_type_changes |= TCHANGEF__NR_BIT_HOLES;
+ if (!show_terse_type_changes)
+ printf(" nr_bit_holes: %+d\n", diff);
+ }
+ check_print_members_changes(structure, cu,
+ new_structure, di->cu, 1);
+ }
+ if (show_terse_type_changes)
+ print_terse_type_changes(structure, cu);
+}
+
+static void show_structure_diffs_iterator(struct class *class, struct cu *cu)
+{
+ if (class->priv != NULL) {
+ const char *name = class__name(class, cu);
+ if (!strlist__has_entry(structs_printed, name)) {
+ show_diffs_structure(class, cu);
+ strlist__add(structs_printed, name);
+ }
+ }
+}
+
+static int cu_show_diffs_iterator(struct cu *cu, void *cookie)
+{
+ static int first_cu_printed;
+
+ if (cu->nr_functions_changed == 0 &&
+ cu->nr_structures_changed == 0)
+ return 0;
+
+ if (first_cu_printed) {
+ if (!quiet)
+ putchar('\n');
+ } else {
+ first_cu_printed = 1;
+ }
+
+ ++total_cus_changed;
+
+ if (!quiet)
+ printf("%s:\n", cu->name);
+
+ uint32_t id;
+ struct class *class;
+
+ if (show_terse_type_changes) {
+ cu__for_each_struct(cu, id, class)
+ show_structure_diffs_iterator(class, cu);
+ return 0;
+ }
+
+ if (cu->nr_structures_changed != 0 && show_struct_diffs) {
+ cu__for_each_struct(cu, id, class)
+ show_structure_diffs_iterator(class, cu);
+ printf(" %u struct%s changed\n", cu->nr_structures_changed,
+ cu->nr_structures_changed > 1 ? "s" : "");
+ }
+
+ if (cu->nr_functions_changed != 0 && show_function_diffs) {
+ total_nr_functions_changed += cu->nr_functions_changed;
+
+ struct function *function;
+ cu__for_each_function(cu, id, function) {
+ if (function->priv != NULL)
+ show_diffs_function(function, cu, cookie);
+ }
+
+ printf(" %u function%s changed", cu->nr_functions_changed,
+ cu->nr_functions_changed > 1 ? "s" : "");
+ if (cu->function_bytes_added != 0) {
+ total_function_bytes_added += cu->function_bytes_added;
+ printf(", %zd bytes added", cu->function_bytes_added);
+ }
+ if (cu->function_bytes_removed != 0) {
+ total_function_bytes_removed += cu->function_bytes_removed;
+ printf(", %zd bytes removed",
+ cu->function_bytes_removed);
+ }
+ printf(", diff: %+zd",
+ cu->function_bytes_added - cu->function_bytes_removed);
+ putchar('\n');
+ }
+ return 0;
+}
+
+static int cu_delete_priv(struct cu *cu, void *cookie __unused)
+{
+ struct class *c;
+ struct function *f;
+ uint32_t id;
+
+ cu__for_each_struct(cu, id, c)
+ free(c->priv);
+
+ cu__for_each_function(cu, id, f)
+ free(f->priv);
+
+ return 0;
+}
+
+static void print_total_function_diff(const char *filename)
+{
+ printf("\n%s:\n", filename);
+
+ printf(" %u function%s changed", total_nr_functions_changed,
+ total_nr_functions_changed > 1 ? "s" : "");
+
+ if (total_function_bytes_added != 0)
+ printf(", %u bytes added", total_function_bytes_added);
+
+ if (total_function_bytes_removed != 0)
+ printf(", %u bytes removed", total_function_bytes_removed);
+
+ printf(", diff: %+d",
+ (total_function_bytes_added -
+ total_function_bytes_removed));
+ putchar('\n');
+}
+
+/* Name and version of program. */
+ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version;
+
+static const struct argp_option codiff__options[] = {
+ {
+ .key = 's',
+ .name = "structs",
+ .doc = "show struct diffs",
+ },
+ {
+ .key = 'f',
+ .name = "functions",
+ .doc = "show function diffs",
+ },
+ {
+ .name = "format_path",
+ .key = 'F',
+ .arg = "FORMAT_LIST",
+ .doc = "List of debugging formats to try"
+ },
+ {
+ .key = 't',
+ .name = "terse_type_changes",
+ .doc = "show terse type changes",
+ },
+ {
+ .key = 'V',
+ .name = "verbose",
+ .doc = "show diffs details",
+ },
+ {
+ .key = 'q',
+ .name = "quiet",
+ .doc = "Show only differences, no difference? No output",
+ },
+ {
+ .name = NULL,
+ }
+};
+
+static error_t codiff__options_parser(int key, char *arg __unused,
+ struct argp_state *state __unused)
+{
+ switch (key) {
+ case 'f': show_function_diffs = 1; break;
+ case 'F': conf_load.format_path = arg; break;
+ case 's': show_struct_diffs = 1; break;
+ case 't': show_terse_type_changes = 1; break;
+ case 'V': verbose = 1; break;
+ case 'q': quiet = 1; break;
+ default: return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static const char codiff__args_doc[] = "OLD_FILE NEW_FILE";
+
+static struct argp codiff__argp = {
+ .options = codiff__options,
+ .parser = codiff__options_parser,
+ .args_doc = codiff__args_doc,
+};
+
+int main(int argc, char *argv[])
+{
+ int remaining, err, rc = EXIT_FAILURE;
+ char *old_filename, *new_filename;
+ struct stat st;
+
+ if (argp_parse(&codiff__argp, argc, argv, 0, &remaining, NULL) ||
+ remaining < argc) {
+ switch (argc - remaining) {
+ case 2: old_filename = argv[remaining++];
+ new_filename = argv[remaining++]; break;
+ case 1:
+ default: goto failure;
+ }
+ } else {
+failure:
+ argp_help(&codiff__argp, stderr, ARGP_HELP_SEE, argv[0]);
+ goto out;
+ }
+
+ if (dwarves__init(0)) {
+ fputs("codiff: insufficient memory\n", stderr);
+ goto out;
+ }
+
+ if (show_function_diffs == 0 && show_struct_diffs == 0 &&
+ show_terse_type_changes == 0)
+ show_function_diffs = show_struct_diffs = 1;
+
+ structs_printed = strlist__new(false);
+ struct cus *old_cus = cus__new(),
+ *new_cus = cus__new();
+ if (old_cus == NULL || new_cus == NULL || structs_printed == NULL) {
+ fputs("codiff: insufficient memory\n", stderr);
+ goto out_cus_delete;
+ }
+
+ if (stat(old_filename, &st) != 0) {
+ fprintf(stderr, "codiff: %s (%s)\n", strerror(errno), old_filename);
+ goto out_cus_delete;
+ }
+
+ /* If old_file is a character device, leave its cus empty */
+ if (!S_ISCHR(st.st_mode)) {
+ err = cus__load_file(old_cus, &conf_load, old_filename);
+ if (err != 0) {
+ cus__print_error_msg("codiff", old_cus, old_filename, err);
+ goto out_cus_delete_priv;
+ }
+ }
+
+ if (stat(new_filename, &st) != 0) {
+ fprintf(stderr, "codiff: %s (%s)\n", strerror(errno), new_filename);
+ goto out_cus_delete_priv;
+ }
+
+ /* If old_file is a character device, leave its cus empty */
+ if (!S_ISCHR(st.st_mode)) {
+ err = cus__load_file(new_cus, &conf_load, new_filename);
+ if (err != 0) {
+ cus__print_error_msg("codiff", new_cus, new_filename, err);
+ goto out_cus_delete_priv;
+ }
+ }
+
+ cus__for_each_cu(old_cus, cu_diff_iterator, new_cus, NULL);
+ cus__for_each_cu(new_cus, cu_find_new_tags_iterator, old_cus, NULL);
+ cus__for_each_cu(old_cus, cu_show_diffs_iterator, NULL, NULL);
+ if (new_cus->nr_entries > 1)
+ cus__for_each_cu(new_cus, cu_show_diffs_iterator, (void *)1, NULL);
+
+ if (total_cus_changed > 1) {
+ if (show_function_diffs)
+ print_total_function_diff(new_filename);
+ }
+
+ rc = EXIT_SUCCESS;
+out_cus_delete_priv:
+ cus__for_each_cu(old_cus, cu_delete_priv, NULL, NULL);
+ cus__for_each_cu(new_cus, cu_delete_priv, NULL, NULL);
+out_cus_delete:
+ cus__delete(old_cus);
+ cus__delete(new_cus);
+ strlist__delete(structs_printed);
+ dwarves__exit();
+out:
+ return rc;
+}
diff --git a/config.h.cmake b/config.h.cmake
new file mode 100644
index 0000000..1b57acd
--- /dev/null
+++ b/config.h.cmake
@@ -0,0 +1,9 @@
+/*
+ Copyright (C) 2007 Arnaldo Carvalho de Melo <acme@redhat.com>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+*/
+
+#cmakedefine HAVE_DWFL_MODULE_BUILD_ID
diff --git a/ctf.h b/ctf.h
new file mode 100644
index 0000000..25b7989
--- /dev/null
+++ b/ctf.h
@@ -0,0 +1,159 @@
+#ifndef _CTF_H
+#define _CTF_H
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2019 Arnaldo Carvalho de Melo <acme@redhat.com>
+ */
+
+#include <stdint.h>
+
+struct ctf_header {
+ uint16_t ctf_magic; /* Header magic value */
+#define CTF_MAGIC 0xcff1
+#define CTF_MAGIC_SWAP 0xf1cf
+
+ uint8_t ctf_version; /* Header version */
+#define CTF_VERSION 2
+
+ uint8_t ctf_flags; /* Header flags */
+#define CTF_FLAGS_COMPR 0x01
+
+ uint32_t ctf_parent_label; /* Label of parent CTF object */
+ uint32_t ctf_parent_name; /* Name of parent CTF object */
+
+ /* All offsets are in bytes are relative to the end of
+ * this header.
+ */
+ uint32_t ctf_label_off; /* Offset of label section */
+ uint32_t ctf_object_off; /* Offset of data object section */
+ uint32_t ctf_func_off; /* Offset of function section */
+ uint32_t ctf_type_off; /* Offset of type section */
+ uint32_t ctf_str_off; /* Offset of string section */
+ uint32_t ctf_str_len; /* Length of string section */
+};
+
+#define CTF_REF_OFFSET(REF) ((REF) & 0x7fffffff)
+#define CTF_REF_TBL_ID(REF) (((REF) >> 31) & 0x1)
+#define CTF_STR_TBL_ID_0 0
+#define CTF_STR_TBL_ID_1 1
+
+#define CTF_REF_ENCODE(TBL, OFF) (((TBL) << 31) | (OFF))
+
+struct ctf_label_ent {
+ uint32_t ctf_label_ref;
+ uint32_t ctf_type_index;
+};
+
+/* Types are encoded with ctf_short_type so long as the ctf_size
+ * field can be fully represented in a uint16_t. If not, then
+ * the ctf_size is given the value 0xffff and ctf_full_type is
+ * used.
+ */
+struct ctf_short_type {
+ uint32_t ctf_name;
+ uint16_t ctf_info;
+ union {
+ uint16_t ctf_size;
+ uint16_t ctf_type;
+ };
+};
+
+struct ctf_full_type {
+ struct ctf_short_type base;
+ uint32_t ctf_size_high;
+ uint32_t ctf_size_low;
+};
+
+#define CTF_GET_KIND(VAL) (((VAL) >> 11) & 0x1f)
+#define CTF_GET_VLEN(VAL) ((VAL) & 0x3ff)
+#define CTF_ISROOT(VAL) (((VAL) & 0x400) != 0)
+
+#define CTF_INFO_ENCODE(KIND, VLEN, ISROOT) \
+ (((ISROOT) ? 0x400 : 0) | ((KIND) << 11) | (VLEN))
+
+#define CTF_TYPE_KIND_UNKN 0 /* Unknown */
+#define CTF_TYPE_KIND_INT 1 /* Integer */
+#define CTF_TYPE_KIND_FLT 2 /* Float */
+#define CTF_TYPE_KIND_PTR 3 /* Pointer */
+#define CTF_TYPE_KIND_ARR 4 /* Array */
+#define CTF_TYPE_KIND_FUNC 5 /* Function */
+#define CTF_TYPE_KIND_STR 6 /* Struct */
+#define CTF_TYPE_KIND_UNION 7 /* Union */
+#define CTF_TYPE_KIND_ENUM 8 /* Enumeration */
+#define CTF_TYPE_KIND_FWD 9 /* Forward */
+#define CTF_TYPE_KIND_TYPDEF 10 /* Typedef */
+#define CTF_TYPE_KIND_VOLATILE 11 /* Volatile */
+#define CTF_TYPE_KIND_CONST 12 /* Const */
+#define CTF_TYPE_KIND_RESTRICT 13 /* Restrict */
+#define CTF_TYPE_KIND_MAX 31
+
+#define CTF_TYPE_INT_ATTRS(VAL) ((VAL) >> 24)
+#define CTF_TYPE_INT_OFFSET(VAL) (((VAL) >> 16) & 0xff)
+#define CTF_TYPE_INT_BITS(VAL) ((VAL) & 0xffff)
+
+#define CTF_TYPE_INT_ENCODE(ATTRS, OFF, BITS) \
+ (((ATTRS) << 24) | ((OFF) << 16) | (BITS))
+
+/* Integer type attributes */
+#define CTF_TYPE_INT_SIGNED 0x1
+#define CTF_TYPE_INT_CHAR 0x2
+#define CTF_TYPE_INT_BOOL 0x4
+#define CTF_TYPE_INT_VARARGS 0x8
+
+#define CTF_TYPE_FP_ATTRS(VAL) ((VAL) >> 24)
+#define CTF_TYPE_FP_OFFSET(VAL) (((VAL) >> 16) & 0xff)
+#define CTF_TYPE_FP_BITS(VAL) ((VAL) & 0xffff)
+
+#define CTF_TYPE_FP_ENCODE(ATTRS, OFF, BITS) \
+ (((ATTRS) << 24) | ((OFF) << 16) | (BITS))
+
+/* Possible values for the float type attribute field */
+#define CTF_TYPE_FP_SINGLE 1
+#define CTF_TYPE_FP_DOUBLE 2
+#define CTF_TYPE_FP_CMPLX 3
+#define CTF_TYPE_FP_CMPLX_DBL 4
+#define CTF_TYPE_FP_CMPLX_LDBL 5
+#define CTF_TYPE_FP_LDBL 6
+#define CTF_TYPE_FP_INTVL 7
+#define CTF_TYPE_FP_INTVL_DBL 8
+#define CTF_TYPE_FP_INTVL_LDBL 9
+#define CTF_TYPE_FP_IMGRY 10
+#define CTF_TYPE_FP_IMGRY_DBL 11
+#define CTF_TYPE_FP_IMGRY_LDBL 12
+#define CTF_TYPE_FP_MAX 12
+
+struct ctf_enum {
+ uint32_t ctf_enum_name;
+ uint32_t ctf_enum_val;
+};
+
+struct ctf_array {
+ uint16_t ctf_array_type;
+ uint16_t ctf_array_index_type;
+ uint32_t ctf_array_nelems;
+};
+
+/* Struct members are encoded with either ctf_short_member or
+ * ctf_full_member, depending upon the 'size' of the struct or
+ * union being defined. If it is less than CTF_SHORT_MEMBER_LIMIT
+ * then ctf_short_member objects are used to encode, else
+ * ctf_full_member is used.
+ */
+#define CTF_SHORT_MEMBER_LIMIT 8192
+
+struct ctf_short_member {
+ uint32_t ctf_member_name;
+ uint16_t ctf_member_type;
+ uint16_t ctf_member_offset;
+};
+
+struct ctf_full_member {
+ uint32_t ctf_member_name;
+ uint16_t ctf_member_type;
+ uint16_t ctf_member_unused;
+ uint32_t ctf_member_offset_high;
+ uint32_t ctf_member_offset_low;
+};
+
+#endif /* _CTF_H */
diff --git a/ctf_encoder.c b/ctf_encoder.c
new file mode 100644
index 0000000..b761287
--- /dev/null
+++ b/ctf_encoder.c
@@ -0,0 +1,353 @@
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2009 Red Hat Inc.
+ Copyright (C) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
+*/
+
+#include "dwarves.h"
+#include "libctf.h"
+#include "ctf.h"
+#include "hash.h"
+#include "elf_symtab.h"
+#include <inttypes.h>
+
+static int tag__check_id_drift(const struct tag *tag,
+ uint32_t core_id, uint32_t ctf_id)
+{
+ if (ctf_id != core_id) {
+ fprintf(stderr, "%s: %s id drift, core: %u, libctf: %d\n",
+ __func__, dwarf_tag_name(tag->tag), core_id, ctf_id);
+ return -1;
+ }
+ return 0;
+}
+
+static int dwarf_to_ctf_type(uint16_t tag)
+{
+ switch (tag) {
+ case DW_TAG_const_type: return CTF_TYPE_KIND_CONST;
+ case DW_TAG_pointer_type: return CTF_TYPE_KIND_PTR;
+ case DW_TAG_restrict_type: return CTF_TYPE_KIND_RESTRICT;
+ case DW_TAG_volatile_type: return CTF_TYPE_KIND_VOLATILE;
+ case DW_TAG_class_type:
+ case DW_TAG_structure_type: return CTF_TYPE_KIND_STR;
+ case DW_TAG_union_type: return CTF_TYPE_KIND_UNION;
+ }
+ return 0xffff;
+}
+
+static int base_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf)
+{
+ struct base_type *bt = tag__base_type(tag);
+ uint32_t ctf_id = ctf__add_base_type(ctf, bt->name, bt->bit_size);
+
+ if (tag__check_id_drift(tag, core_id, ctf_id))
+ return -1;
+
+ return 0;
+}
+
+static int pointer_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf)
+{
+ uint32_t ctf_id = ctf__add_short_type(ctf, dwarf_to_ctf_type(tag->tag), tag->type, 0);
+
+ if (tag__check_id_drift(tag, core_id, ctf_id))
+ return -1;
+
+ return 0;
+}
+
+static int typedef__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf)
+{
+ uint32_t ctf_id = ctf__add_short_type(ctf, CTF_TYPE_KIND_TYPDEF, tag->type, tag__namespace(tag)->name);
+
+ if (tag__check_id_drift(tag, core_id, ctf_id))
+ return -1;
+
+ return 0;
+}
+
+static int fwd_decl__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf)
+{
+ uint32_t ctf_id = ctf__add_fwd_decl(ctf, tag__namespace(tag)->name);
+
+ if (tag__check_id_drift(tag, core_id, ctf_id))
+ return -1;
+
+ return 0;
+}
+
+static int structure_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf)
+{
+ struct type *type = tag__type(tag);
+ int64_t position;
+ uint32_t ctf_id = ctf__add_struct(ctf, dwarf_to_ctf_type(tag->tag),
+ type->namespace.name, type->size,
+ type->nr_members, &position);
+
+ if (tag__check_id_drift(tag, core_id, ctf_id))
+ return -1;
+
+ const bool is_short = type->size < CTF_SHORT_MEMBER_LIMIT;
+ struct class_member *pos;
+ type__for_each_data_member(type, pos) {
+ if (is_short)
+ ctf__add_short_member(ctf, pos->name, pos->tag.type,
+ pos->bit_offset, &position);
+ else
+ ctf__add_full_member(ctf, pos->name, pos->tag.type,
+ pos->bit_offset, &position);
+ }
+
+ return 0;
+}
+
+static uint32_t array_type__nelems(struct tag *tag)
+{
+ int i;
+ uint32_t nelem = 1;
+ struct array_type *array = tag__array_type(tag);
+
+ for (i = array->dimensions - 1; i >= 0; --i)
+ nelem *= array->nr_entries[i];
+
+ return nelem;
+}
+
+static int array_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf)
+{
+ const uint32_t nelems = array_type__nelems(tag);
+ uint32_t ctf_id = ctf__add_array(ctf, tag->type, 0, nelems);
+
+ if (tag__check_id_drift(tag, core_id, ctf_id))
+ return -1;
+
+ return 0;
+}
+
+static int subroutine_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf)
+{
+ struct parameter *pos;
+ int64_t position;
+ struct ftype *ftype = tag__ftype(tag);
+ uint32_t ctf_id = ctf__add_function_type(ctf, tag->type, ftype->nr_parms, ftype->unspec_parms, &position);
+
+ if (tag__check_id_drift(tag, core_id, ctf_id))
+ return -1;
+
+ ftype__for_each_parameter(ftype, pos)
+ ctf__add_parameter(ctf, pos->tag.type, &position);
+
+ return 0;
+}
+
+static int enumeration_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf)
+{
+ struct type *etype = tag__type(tag);
+ int64_t position;
+ uint32_t ctf_id = ctf__add_enumeration_type(ctf, etype->namespace.name,
+ etype->size, etype->nr_members,
+ &position);
+
+ if (tag__check_id_drift(tag, core_id, ctf_id))
+ return -1;
+
+ struct enumerator *pos;
+ type__for_each_enumerator(etype, pos)
+ ctf__add_enumerator(ctf, pos->name, pos->value, &position);
+
+ return 0;
+}
+
+static void tag__encode_ctf(struct tag *tag, uint32_t core_id, struct ctf *ctf)
+{
+ switch (tag->tag) {
+ case DW_TAG_base_type:
+ base_type__encode(tag, core_id, ctf);
+ break;
+ case DW_TAG_const_type:
+ case DW_TAG_pointer_type:
+ case DW_TAG_restrict_type:
+ case DW_TAG_volatile_type:
+ pointer_type__encode(tag, core_id, ctf);
+ break;
+ case DW_TAG_typedef:
+ typedef__encode(tag, core_id, ctf);
+ break;
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ case DW_TAG_class_type:
+ if (tag__type(tag)->declaration)
+ fwd_decl__encode(tag, core_id, ctf);
+ else
+ structure_type__encode(tag, core_id, ctf);
+ break;
+ case DW_TAG_array_type:
+ array_type__encode(tag, core_id, ctf);
+ break;
+ case DW_TAG_subroutine_type:
+ subroutine_type__encode(tag, core_id, ctf);
+ break;
+ case DW_TAG_enumeration_type:
+ enumeration_type__encode(tag, core_id, ctf);
+ break;
+ }
+}
+
+#define HASHADDR__BITS 8
+#define HASHADDR__SIZE (1UL << HASHADDR__BITS)
+#define hashaddr__fn(key) hash_64(key, HASHADDR__BITS)
+
+static struct function *hashaddr__find_function(const struct hlist_head hashtable[],
+ const uint64_t addr)
+{
+ struct function *function;
+ struct hlist_node *pos;
+ uint16_t bucket = hashaddr__fn(addr);
+ const struct hlist_head *head = &hashtable[bucket];
+
+ hlist_for_each_entry(function, pos, head, tool_hnode) {
+ if (function->lexblock.ip.addr == addr)
+ return function;
+ }
+
+ return NULL;
+}
+
+static struct variable *hashaddr__find_variable(const struct hlist_head hashtable[],
+ const uint64_t addr)
+{
+ struct variable *variable;
+ struct hlist_node *pos;
+ uint16_t bucket = hashaddr__fn(addr);
+ const struct hlist_head *head = &hashtable[bucket];
+
+ hlist_for_each_entry(variable, pos, head, tool_hnode) {
+ if (variable->ip.addr == addr)
+ return variable;
+ }
+
+ return NULL;
+}
+
+/*
+ * FIXME: Its in the DWARF loader, we have to find a better handoff
+ * mechanizm...
+ */
+extern struct strings *strings;
+
+int cu__encode_ctf(struct cu *cu, int verbose)
+{
+ int err = -1;
+ struct ctf *ctf = ctf__new(cu->filename, cu->elf);
+
+ if (ctf == NULL)
+ goto out;
+
+ if (cu__cache_symtab(cu) < 0)
+ goto out_delete;
+
+ ctf__set_strings(ctf, strings);
+
+ uint32_t id;
+ struct tag *pos;
+ cu__for_each_type(cu, id, pos)
+ tag__encode_ctf(pos, id, ctf);
+
+ struct hlist_head hash_addr[HASHADDR__SIZE];
+
+ for (id = 0; id < HASHADDR__SIZE; ++id)
+ INIT_HLIST_HEAD(&hash_addr[id]);
+
+ struct function *function;
+ cu__for_each_function(cu, id, function) {
+ uint64_t addr = function->lexblock.ip.addr;
+ struct hlist_head *head = &hash_addr[hashaddr__fn(addr)];
+ hlist_add_head(&function->tool_hnode, head);
+ }
+
+ uint64_t addr;
+ GElf_Sym sym;
+ const char *sym_name;
+ cu__for_each_cached_symtab_entry(cu, id, sym, sym_name) {
+ if (ctf__ignore_symtab_function(&sym, sym_name))
+ continue;
+
+ addr = elf_sym__value(&sym);
+ int64_t position;
+ function = hashaddr__find_function(hash_addr, addr);
+ if (function == NULL) {
+ if (verbose)
+ fprintf(stderr,
+ "function %4d: %-20s %#" PRIx64 " %5u NOT FOUND!\n",
+ id, sym_name, addr,
+ elf_sym__size(&sym));
+ err = ctf__add_function(ctf, 0, 0, 0, &position);
+ if (err != 0)
+ goto out_err_ctf;
+ continue;
+ }
+
+ const struct ftype *ftype = &function->proto;
+ err = ctf__add_function(ctf, function->proto.tag.type,
+ ftype->nr_parms,
+ ftype->unspec_parms, &position);
+
+ if (err != 0)
+ goto out_err_ctf;
+
+ struct parameter *pos;
+ ftype__for_each_parameter(ftype, pos)
+ ctf__add_function_parameter(ctf, pos->tag.type, &position);
+ }
+
+ for (id = 0; id < HASHADDR__SIZE; ++id)
+ INIT_HLIST_HEAD(&hash_addr[id]);
+
+ struct variable *var;
+ cu__for_each_variable(cu, id, pos) {
+ var = tag__variable(pos);
+ if (variable__scope(var) != VSCOPE_GLOBAL)
+ continue;
+ struct hlist_head *head = &hash_addr[hashaddr__fn(var->ip.addr)];
+ hlist_add_head(&var->tool_hnode, head);
+ }
+
+ cu__for_each_cached_symtab_entry(cu, id, sym, sym_name) {
+ if (ctf__ignore_symtab_object(&sym, sym_name))
+ continue;
+ addr = elf_sym__value(&sym);
+
+ var = hashaddr__find_variable(hash_addr, addr);
+ if (var == NULL) {
+ if (verbose)
+ fprintf(stderr,
+ "variable %4d: %-20s %#" PRIx64 " %5u NOT FOUND!\n",
+ id, sym_name, addr,
+ elf_sym__size(&sym));
+ err = ctf__add_object(ctf, 0);
+ if (err != 0)
+ goto out_err_ctf;
+ continue;
+ }
+
+ err = ctf__add_object(ctf, var->ip.tag.type);
+ if (err != 0)
+ goto out_err_ctf;
+ }
+
+ ctf__encode(ctf, CTF_FLAGS_COMPR);
+
+ err = 0;
+out_delete:
+ ctf__delete(ctf);
+out:
+ return err;
+out_err_ctf:
+ fprintf(stderr,
+ "%4d: %-20s %#llx %5u failed encoding, "
+ "ABORTING!\n", id, sym_name,
+ (unsigned long long)addr, elf_sym__size(&sym));
+ goto out_delete;
+}
diff --git a/ctf_encoder.h b/ctf_encoder.h
new file mode 100644
index 0000000..16e76e2
--- /dev/null
+++ b/ctf_encoder.h
@@ -0,0 +1,14 @@
+#ifndef _CTF_ENCODER_H_
+#define _CTF_ENCODER_H_ 1
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2009 Red Hat Inc.
+ Copyright (C) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
+*/
+
+struct cu;
+
+int cu__encode_ctf(struct cu *cu, int verbose);
+
+#endif /* _CTF_ENCODER_H_ */
diff --git a/ctf_loader.c b/ctf_loader.c
new file mode 100644
index 0000000..9f03f09
--- /dev/null
+++ b/ctf_loader.c
@@ -0,0 +1,771 @@
+/* ctfdump.c: CTF dumper.
+ *
+ * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <malloc.h>
+#include <string.h>
+#include <limits.h>
+#include <libgen.h>
+#include <zlib.h>
+
+#include <gelf.h>
+
+#include "libctf.h"
+#include "ctf.h"
+#include "dutil.h"
+#include "dwarves.h"
+
+/*
+ * FIXME: We should just get the table from the CTF ELF section
+ * and use it directly
+ */
+extern struct strings *strings;
+
+static void *tag__alloc(const size_t size)
+{
+ struct tag *tag = zalloc(size);
+
+ if (tag != NULL)
+ tag->top_level = 1;
+
+ return tag;
+}
+
+static int ctf__load_ftype(struct ctf *ctf, struct ftype *proto, uint16_t tag,
+ uint16_t type, uint16_t vlen, uint16_t *args, long id)
+{
+ proto->tag.tag = tag;
+ proto->tag.type = type;
+ INIT_LIST_HEAD(&proto->parms);
+
+ int i;
+ for (i = 0; i < vlen; i++) {
+ uint16_t type = ctf__get16(ctf, &args[i]);
+
+ if (type == 0)
+ proto->unspec_parms = 1;
+ else {
+ struct parameter *p = tag__alloc(sizeof(*p));
+
+ if (p == NULL)
+ goto out_free_parameters;
+ p->tag.tag = DW_TAG_formal_parameter;
+ p->tag.type = ctf__get16(ctf, &args[i]);
+ ftype__add_parameter(proto, p);
+ }
+ }
+
+ vlen *= sizeof(*args);
+
+ /* Round up to next multiple of 4 to maintain
+ * 32-bit alignment.
+ */
+ if (vlen & 0x2)
+ vlen += 0x2;
+
+ if (id < 0) {
+ uint32_t type_id;
+
+ cu__add_tag(ctf->priv, &proto->tag, &type_id);
+ } else {
+ cu__add_tag_with_id(ctf->priv, &proto->tag, id);
+ }
+
+ return vlen;
+out_free_parameters:
+ ftype__delete(proto, ctf->priv);
+ return -ENOMEM;
+}
+
+static struct function *function__new(uint16_t **ptr, GElf_Sym *sym,
+ struct ctf *ctf)
+{
+ struct function *func = tag__alloc(sizeof(*func));
+
+ if (func != NULL) {
+ func->lexblock.ip.addr = elf_sym__value(sym);
+ func->lexblock.size = elf_sym__size(sym);
+ func->name = sym->st_name;
+ func->vtable_entry = -1;
+ func->external = elf_sym__bind(sym) == STB_GLOBAL;
+ INIT_LIST_HEAD(&func->vtable_node);
+ INIT_LIST_HEAD(&func->tool_node);
+ INIT_LIST_HEAD(&func->lexblock.tags);
+
+ uint16_t val = ctf__get16(ctf, *ptr);
+ uint16_t tag = CTF_GET_KIND(val);
+ uint16_t vlen = CTF_GET_VLEN(val);
+
+ ++*ptr;
+
+ if (tag != CTF_TYPE_KIND_FUNC) {
+ fprintf(stderr,
+ "%s: Expected function type, got %u\n",
+ __func__, tag);
+ goto out_delete;
+ }
+ uint16_t type = ctf__get16(ctf, *ptr);
+ long id = -1; /* FIXME: not needed for funcs... */
+
+ ++*ptr;
+
+ if (ctf__load_ftype(ctf, &func->proto, DW_TAG_subprogram,
+ type, vlen, *ptr, id) < 0)
+ return NULL;
+ /*
+ * Round up to next multiple of 4 to maintain 32-bit alignment.
+ */
+ if (vlen & 0x1)
+ ++vlen;
+ *ptr += vlen;
+ }
+
+ return func;
+out_delete:
+ free(func);
+ return NULL;
+}
+
+static int ctf__load_funcs(struct ctf *ctf)
+{
+ struct ctf_header *hp = ctf__get_buffer(ctf);
+ uint16_t *func_ptr = (ctf__get_buffer(ctf) + sizeof(*hp) +
+ ctf__get32(ctf, &hp->ctf_func_off));
+
+ GElf_Sym sym;
+ uint32_t idx;
+ ctf__for_each_symtab_function(ctf, idx, sym)
+ if (function__new(&func_ptr, &sym, ctf) == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static struct base_type *base_type__new(strings_t name, uint32_t attrs,
+ uint8_t float_type, size_t size)
+{
+ struct base_type *bt = tag__alloc(sizeof(*bt));
+
+ if (bt != NULL) {
+ bt->name = name;
+ bt->bit_size = size;
+ bt->is_signed = attrs & CTF_TYPE_INT_SIGNED;
+ bt->is_bool = attrs & CTF_TYPE_INT_BOOL;
+ bt->is_varargs = attrs & CTF_TYPE_INT_VARARGS;
+ bt->name_has_encoding = false;
+ bt->float_type = float_type;
+ }
+ return bt;
+}
+
+static void type__init(struct type *type, uint16_t tag,
+ strings_t name, size_t size)
+{
+ __type__init(type);
+ INIT_LIST_HEAD(&type->namespace.tags);
+ type->size = size;
+ type->namespace.tag.tag = tag;
+ type->namespace.name = name;
+ type->namespace.sname = 0;
+}
+
+static struct type *type__new(uint16_t tag, strings_t name, size_t size)
+{
+ struct type *type = tag__alloc(sizeof(*type));
+
+ if (type != NULL)
+ type__init(type, tag, name, size);
+
+ return type;
+}
+
+static struct class *class__new(strings_t name, size_t size)
+{
+ struct class *class = tag__alloc(sizeof(*class));
+
+ if (class != NULL) {
+ type__init(&class->type, DW_TAG_structure_type, name, size);
+ INIT_LIST_HEAD(&class->vtable);
+ }
+
+ return class;
+}
+
+static int create_new_base_type(struct ctf *ctf, void *ptr,
+ struct ctf_full_type *tp, uint32_t id)
+{
+ uint32_t *enc = ptr;
+ uint32_t eval = ctf__get32(ctf, enc);
+ uint32_t attrs = CTF_TYPE_INT_ATTRS(eval);
+ strings_t name = ctf__get32(ctf, &tp->base.ctf_name);
+ struct base_type *base = base_type__new(name, attrs, 0,
+ CTF_TYPE_INT_BITS(eval));
+ if (base == NULL)
+ return -ENOMEM;
+
+ base->tag.tag = DW_TAG_base_type;
+ cu__add_tag_with_id(ctf->priv, &base->tag, id);
+
+ return sizeof(*enc);
+}
+
+static int create_new_base_type_float(struct ctf *ctf, void *ptr,
+ struct ctf_full_type *tp,
+ uint32_t id)
+{
+ strings_t name = ctf__get32(ctf, &tp->base.ctf_name);
+ uint32_t *enc = ptr, eval = ctf__get32(ctf, enc);
+ struct base_type *base = base_type__new(name, 0, eval,
+ CTF_TYPE_FP_BITS(eval));
+ if (base == NULL)
+ return -ENOMEM;
+
+ base->tag.tag = DW_TAG_base_type;
+ cu__add_tag_with_id(ctf->priv, &base->tag, id);
+
+ return sizeof(*enc);
+}
+
+static int create_new_array(struct ctf *ctf, void *ptr, uint32_t id)
+{
+ struct ctf_array *ap = ptr;
+ struct array_type *array = tag__alloc(sizeof(*array));
+
+ if (array == NULL)
+ return -ENOMEM;
+
+ /* FIXME: where to get the number of dimensions?
+ * it it flattened? */
+ array->dimensions = 1;
+ array->nr_entries = malloc(sizeof(uint32_t));
+
+ if (array->nr_entries == NULL) {
+ free(array);
+ return -ENOMEM;
+ }
+
+ array->nr_entries[0] = ctf__get32(ctf, &ap->ctf_array_nelems);
+ array->tag.tag = DW_TAG_array_type;
+ array->tag.type = ctf__get16(ctf, &ap->ctf_array_type);
+
+ cu__add_tag_with_id(ctf->priv, &array->tag, id);
+
+ return sizeof(*ap);
+}
+
+static int create_new_subroutine_type(struct ctf *ctf, void *ptr,
+ int vlen, struct ctf_full_type *tp,
+ uint32_t id)
+{
+ uint16_t *args = ptr;
+ unsigned int type = ctf__get16(ctf, &tp->base.ctf_type);
+ struct ftype *proto = tag__alloc(sizeof(*proto));
+
+ if (proto == NULL)
+ return -ENOMEM;
+
+ vlen = ctf__load_ftype(ctf, proto, DW_TAG_subroutine_type,
+ type, vlen, args, id);
+ return vlen < 0 ? -ENOMEM : vlen;
+}
+
+static int create_full_members(struct ctf *ctf, void *ptr,
+ int vlen, struct type *class)
+{
+ struct ctf_full_member *mp = ptr;
+ int i;
+
+ for (i = 0; i < vlen; i++) {
+ struct class_member *member = zalloc(sizeof(*member));
+
+ if (member == NULL)
+ return -ENOMEM;
+
+ member->tag.tag = DW_TAG_member;
+ member->tag.type = ctf__get16(ctf, &mp[i].ctf_member_type);
+ member->name = ctf__get32(ctf, &mp[i].ctf_member_name);
+ member->bit_offset = (ctf__get32(ctf, &mp[i].ctf_member_offset_high) << 16) |
+ ctf__get32(ctf, &mp[i].ctf_member_offset_low);
+ /* sizes and offsets will be corrected at class__fixup_ctf_bitfields */
+ type__add_member(class, member);
+ }
+
+ return sizeof(*mp);
+}
+
+static int create_short_members(struct ctf *ctf, void *ptr,
+ int vlen, struct type *class)
+{
+ struct ctf_short_member *mp = ptr;
+ int i;
+
+ for (i = 0; i < vlen; i++) {
+ struct class_member *member = zalloc(sizeof(*member));
+
+ if (member == NULL)
+ return -ENOMEM;
+
+ member->tag.tag = DW_TAG_member;
+ member->tag.type = ctf__get16(ctf, &mp[i].ctf_member_type);
+ member->name = ctf__get32(ctf, &mp[i].ctf_member_name);
+ member->bit_offset = ctf__get16(ctf, &mp[i].ctf_member_offset);
+ /* sizes and offsets will be corrected at class__fixup_ctf_bitfields */
+
+ type__add_member(class, member);
+ }
+
+ return sizeof(*mp);
+}
+
+static int create_new_class(struct ctf *ctf, void *ptr,
+ int vlen, struct ctf_full_type *tp,
+ uint64_t size, uint32_t id)
+{
+ int member_size;
+ strings_t name = ctf__get32(ctf, &tp->base.ctf_name);
+ struct class *class = class__new(name, size);
+
+ if (size >= CTF_SHORT_MEMBER_LIMIT) {
+ member_size = create_full_members(ctf, ptr, vlen, &class->type);
+ } else {
+ member_size = create_short_members(ctf, ptr, vlen, &class->type);
+ }
+
+ if (member_size < 0)
+ goto out_free;
+
+ cu__add_tag_with_id(ctf->priv, &class->type.namespace.tag, id);
+
+ return (vlen * member_size);
+out_free:
+ class__delete(class, ctf->priv);
+ return -ENOMEM;
+}
+
+static int create_new_union(struct ctf *ctf, void *ptr,
+ int vlen, struct ctf_full_type *tp,
+ uint64_t size, uint32_t id)
+{
+ int member_size;
+ strings_t name = ctf__get32(ctf, &tp->base.ctf_name);
+ struct type *un = type__new(DW_TAG_union_type, name, size);
+
+ if (size >= CTF_SHORT_MEMBER_LIMIT) {
+ member_size = create_full_members(ctf, ptr, vlen, un);
+ } else {
+ member_size = create_short_members(ctf, ptr, vlen, un);
+ }
+
+ if (member_size < 0)
+ goto out_free;
+
+ cu__add_tag_with_id(ctf->priv, &un->namespace.tag, id);
+
+ return (vlen * member_size);
+out_free:
+ type__delete(un, ctf->priv);
+ return -ENOMEM;
+}
+
+static struct enumerator *enumerator__new(strings_t name, uint32_t value)
+{
+ struct enumerator *en = tag__alloc(sizeof(*en));
+
+ if (en != NULL) {
+ en->name = name;
+ en->value = value;
+ en->tag.tag = DW_TAG_enumerator;
+ }
+
+ return en;
+}
+
+static int create_new_enumeration(struct ctf *ctf, void *ptr,
+ int vlen, struct ctf_full_type *tp,
+ uint16_t size, uint32_t id)
+{
+ struct ctf_enum *ep = ptr;
+ uint16_t i;
+ struct type *enumeration = type__new(DW_TAG_enumeration_type,
+ ctf__get32(ctf,
+ &tp->base.ctf_name),
+ size ?: (sizeof(int) * 8));
+
+ if (enumeration == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < vlen; i++) {
+ strings_t name = ctf__get32(ctf, &ep[i].ctf_enum_name);
+ uint32_t value = ctf__get32(ctf, &ep[i].ctf_enum_val);
+ struct enumerator *enumerator = enumerator__new(name, value);
+
+ if (enumerator == NULL)
+ goto out_free;
+
+ enumeration__add(enumeration, enumerator);
+ }
+
+ cu__add_tag_with_id(ctf->priv, &enumeration->namespace.tag, id);
+
+ return (vlen * sizeof(*ep));
+out_free:
+ enumeration__delete(enumeration, ctf->priv);
+ return -ENOMEM;
+}
+
+static int create_new_forward_decl(struct ctf *ctf, struct ctf_full_type *tp,
+ uint64_t size, uint32_t id)
+{
+ strings_t name = ctf__get32(ctf, &tp->base.ctf_name);
+ struct class *fwd = class__new(name, size);
+
+ if (fwd == NULL)
+ return -ENOMEM;
+ fwd->type.declaration = 1;
+ cu__add_tag_with_id(ctf->priv, &fwd->type.namespace.tag, id);
+ return 0;
+}
+
+static int create_new_typedef(struct ctf *ctf, struct ctf_full_type *tp,
+ uint64_t size, uint32_t id)
+{
+ strings_t name = ctf__get32(ctf, &tp->base.ctf_name);
+ unsigned int type_id = ctf__get16(ctf, &tp->base.ctf_type);
+ struct type *type = type__new(DW_TAG_typedef, name, size);
+
+ if (type == NULL)
+ return -ENOMEM;
+
+ type->namespace.tag.type = type_id;
+ cu__add_tag_with_id(ctf->priv, &type->namespace.tag, id);
+
+ return 0;
+}
+
+static int create_new_tag(struct ctf *ctf, int type,
+ struct ctf_full_type *tp, uint32_t id)
+{
+ unsigned int type_id = ctf__get16(ctf, &tp->base.ctf_type);
+ struct tag *tag = zalloc(sizeof(*tag));
+
+ if (tag == NULL)
+ return -ENOMEM;
+
+ switch (type) {
+ case CTF_TYPE_KIND_CONST: tag->tag = DW_TAG_const_type; break;
+ case CTF_TYPE_KIND_PTR: tag->tag = DW_TAG_pointer_type; break;
+ case CTF_TYPE_KIND_RESTRICT: tag->tag = DW_TAG_restrict_type; break;
+ case CTF_TYPE_KIND_VOLATILE: tag->tag = DW_TAG_volatile_type; break;
+ default:
+ free(tag);
+ printf("%s: unknown type %d\n\n", __func__, type);
+ return 0;
+ }
+
+ tag->type = type_id;
+ cu__add_tag_with_id(ctf->priv, tag, id);
+
+ return 0;
+}
+
+static int ctf__load_types(struct ctf *ctf)
+{
+ void *ctf_buffer = ctf__get_buffer(ctf);
+ struct ctf_header *hp = ctf_buffer;
+ void *ctf_contents = ctf_buffer + sizeof(*hp),
+ *type_section = (ctf_contents + ctf__get32(ctf, &hp->ctf_type_off)),
+ *strings_section = (ctf_contents + ctf__get32(ctf, &hp->ctf_str_off));
+ struct ctf_full_type *type_ptr = type_section,
+ *end = strings_section;
+ uint32_t type_index = 0x0001;
+
+ if (hp->ctf_parent_name || hp->ctf_parent_label)
+ type_index += 0x8000;
+
+ while (type_ptr < end) {
+ uint16_t val = ctf__get16(ctf, &type_ptr->base.ctf_info);
+ uint16_t type = CTF_GET_KIND(val);
+ int vlen = CTF_GET_VLEN(val);
+ void *ptr = type_ptr;
+ uint16_t base_size = ctf__get16(ctf, &type_ptr->base.ctf_size);
+ uint64_t size = base_size;
+
+ if (base_size == 0xffff) {
+ size = ctf__get32(ctf, &type_ptr->ctf_size_high);
+ size <<= 32;
+ size |= ctf__get32(ctf, &type_ptr->ctf_size_low);
+ ptr += sizeof(struct ctf_full_type);
+ } else
+ ptr += sizeof(struct ctf_short_type);
+
+ if (type == CTF_TYPE_KIND_INT) {
+ vlen = create_new_base_type(ctf, ptr, type_ptr, type_index);
+ } else if (type == CTF_TYPE_KIND_FLT) {
+ vlen = create_new_base_type_float(ctf, ptr, type_ptr, type_index);
+ } else if (type == CTF_TYPE_KIND_ARR) {
+ vlen = create_new_array(ctf, ptr, type_index);
+ } else if (type == CTF_TYPE_KIND_FUNC) {
+ vlen = create_new_subroutine_type(ctf, ptr, vlen, type_ptr, type_index);
+ } else if (type == CTF_TYPE_KIND_STR) {
+ vlen = create_new_class(ctf, ptr,
+ vlen, type_ptr, size, type_index);
+ } else if (type == CTF_TYPE_KIND_UNION) {
+ vlen = create_new_union(ctf, ptr,
+ vlen, type_ptr, size, type_index);
+ } else if (type == CTF_TYPE_KIND_ENUM) {
+ vlen = create_new_enumeration(ctf, ptr, vlen, type_ptr,
+ size, type_index);
+ } else if (type == CTF_TYPE_KIND_FWD) {
+ vlen = create_new_forward_decl(ctf, type_ptr, size, type_index);
+ } else if (type == CTF_TYPE_KIND_TYPDEF) {
+ vlen = create_new_typedef(ctf, type_ptr, size, type_index);
+ } else if (type == CTF_TYPE_KIND_VOLATILE ||
+ type == CTF_TYPE_KIND_PTR ||
+ type == CTF_TYPE_KIND_CONST ||
+ type == CTF_TYPE_KIND_RESTRICT) {
+ vlen = create_new_tag(ctf, type, type_ptr, type_index);
+ } else if (type == CTF_TYPE_KIND_UNKN) {
+ cu__table_nullify_type_entry(ctf->priv, type_index);
+ fprintf(stderr,
+ "CTF: idx: %d, off: %zd, root: %s Unknown\n",
+ type_index, ((void *)type_ptr) - type_section,
+ CTF_ISROOT(val) ? "yes" : "no");
+ vlen = 0;
+ } else
+ return -EINVAL;
+
+ if (vlen < 0)
+ return vlen;
+
+ type_ptr = ptr + vlen;
+ type_index++;
+ }
+ return 0;
+}
+
+static struct variable *variable__new(uint16_t type, GElf_Sym *sym,
+ struct ctf *ctf)
+{
+ struct variable *var = tag__alloc(sizeof(*var));
+
+ if (var != NULL) {
+ var->scope = VSCOPE_GLOBAL;
+ var->ip.addr = elf_sym__value(sym);
+ var->name = sym->st_name;
+ var->external = elf_sym__bind(sym) == STB_GLOBAL;
+ var->ip.tag.tag = DW_TAG_variable;
+ var->ip.tag.type = type;
+ uint32_t id; /* FIXME: not needed for variables... */
+ cu__add_tag(ctf->priv, &var->ip.tag, &id);
+ }
+
+ return var;
+}
+
+static int ctf__load_objects(struct ctf *ctf)
+{
+ struct ctf_header *hp = ctf__get_buffer(ctf);
+ uint16_t *objp = (ctf__get_buffer(ctf) + sizeof(*hp) +
+ ctf__get32(ctf, &hp->ctf_object_off));
+
+ GElf_Sym sym;
+ uint32_t idx;
+ ctf__for_each_symtab_object(ctf, idx, sym) {
+ const uint16_t type = *objp;
+ /*
+ * Discard void objects, probably was an object
+ * we didn't found DWARF info for when encoding.
+ */
+ if (type && variable__new(type, &sym, ctf) == NULL)
+ return -ENOMEM;
+ ++objp;
+ }
+
+ return 0;
+}
+
+static int ctf__load_sections(struct ctf *ctf)
+{
+ int err = ctf__load_symtab(ctf);
+
+ if (err != 0)
+ goto out;
+ err = ctf__load_funcs(ctf);
+ if (err == 0)
+ err = ctf__load_types(ctf);
+ if (err == 0)
+ err = ctf__load_objects(ctf);
+out:
+ return err;
+}
+
+static int class__fixup_ctf_bitfields(struct tag *tag, struct cu *cu)
+{
+ struct class_member *pos;
+ struct type *tag_type = tag__type(tag);
+
+ type__for_each_data_member(tag_type, pos) {
+ struct tag *type = tag__strip_typedefs_and_modifiers(&pos->tag, cu);
+
+ if (type == NULL) /* FIXME: C++ CTF... */
+ continue;
+
+ pos->bitfield_offset = 0;
+ pos->bitfield_size = 0;
+ pos->byte_offset = pos->bit_offset / 8;
+
+ uint16_t type_bit_size;
+ size_t integral_bit_size;
+
+ switch (type->tag) {
+ case DW_TAG_enumeration_type:
+ type_bit_size = tag__type(type)->size;
+ /* Best we can do to check if this is a packed enum */
+ if (is_power_of_2(type_bit_size))
+ integral_bit_size = roundup(type_bit_size, 8);
+ else
+ integral_bit_size = sizeof(int) * 8;
+ break;
+ case DW_TAG_base_type: {
+ struct base_type *bt = tag__base_type(type);
+ char name[256];
+ type_bit_size = bt->bit_size;
+ integral_bit_size = base_type__name_to_size(bt, cu);
+ if (integral_bit_size == 0)
+ fprintf(stderr, "%s: unknown base type name \"%s\"!\n",
+ __func__, base_type__name(bt, cu, name,
+ sizeof(name)));
+ }
+ break;
+ default:
+ pos->byte_size = tag__size(type, cu);
+ pos->bit_size = pos->byte_size * 8;
+ continue;
+ }
+
+ /*
+ * XXX: integral_bit_size can be zero if base_type__name_to_size doesn't
+ * know about the base_type name, so one has to add there when
+ * such base_type isn't found. pahole will put zero on the
+ * struct output so it should be easy to spot the name when
+ * such unlikely thing happens.
+ */
+ pos->byte_size = integral_bit_size / 8;
+
+ if (integral_bit_size == 0 || type_bit_size == integral_bit_size) {
+ pos->bit_size = integral_bit_size;
+ continue;
+ }
+
+ pos->bitfield_offset = pos->bit_offset % integral_bit_size;
+ pos->bitfield_size = type_bit_size;
+ pos->bit_size = type_bit_size;
+ pos->byte_offset = (((pos->bit_offset / integral_bit_size) *
+ integral_bit_size) / 8);
+ }
+
+ return 0;
+}
+
+static int cu__fixup_ctf_bitfields(struct cu *cu)
+{
+ int err = 0;
+ struct tag *pos;
+
+ list_for_each_entry(pos, &cu->tags, node)
+ if (tag__is_struct(pos) || tag__is_union(pos)) {
+ err = class__fixup_ctf_bitfields(pos, cu);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+static const char *ctf__function_name(struct function *func,
+ const struct cu *cu)
+{
+ struct ctf *ctf = cu->priv;
+
+ return ctf->symtab->symstrs->d_buf + func->name;
+}
+
+static const char *ctf__variable_name(const struct variable *var,
+ const struct cu *cu)
+{
+ struct ctf *ctf = cu->priv;
+
+ return ctf->symtab->symstrs->d_buf + var->name;
+}
+
+static void ctf__cu_delete(struct cu *cu)
+{
+ ctf__delete(cu->priv);
+ cu->priv = NULL;
+}
+
+static const char *ctf__strings_ptr(const struct cu *cu, strings_t s)
+{
+ return ctf__string(cu->priv, s);
+}
+
+struct debug_fmt_ops ctf__ops;
+
+int ctf__load_file(struct cus *cus, struct conf_load *conf,
+ const char *filename)
+{
+ int err;
+ struct ctf *state = ctf__new(filename, NULL);
+
+ if (state == NULL)
+ return -1;
+
+ struct cu *cu = cu__new(filename, state->wordsize, NULL, 0, filename);
+ if (cu == NULL)
+ return -1;
+
+ cu->language = LANG_C;
+ cu->uses_global_strings = false;
+ cu->little_endian = state->ehdr.e_ident[EI_DATA] == ELFDATA2LSB;
+ cu->dfops = &ctf__ops;
+ cu->priv = state;
+ state->priv = cu;
+ if (ctf__load(state) != 0)
+ return -1;
+
+ err = ctf__load_sections(state);
+
+ if (err != 0) {
+ cu__delete(cu);
+ return err;
+ }
+
+ err = cu__fixup_ctf_bitfields(cu);
+ /*
+ * The app stole this cu, possibly deleting it,
+ * so forget about it
+ */
+ if (conf && conf->steal && conf->steal(cu, conf))
+ return 0;
+
+ cus__add(cus, cu);
+ return err;
+}
+
+struct debug_fmt_ops ctf__ops = {
+ .name = "ctf",
+ .function__name = ctf__function_name,
+ .load_file = ctf__load_file,
+ .variable__name = ctf__variable_name,
+ .strings__ptr = ctf__strings_ptr,
+ .cu__delete = ctf__cu_delete,
+};
diff --git a/ctfdwdiff b/ctfdwdiff
new file mode 100755
index 0000000..4ed989f
--- /dev/null
+++ b/ctfdwdiff
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+results_dir=/tmp/ctfdwdiff
+
+diff_tool() {
+ local tool=$1
+ local dwarf_options=$2
+ local ctf_options=$3
+ local obj=$4
+
+ diff=$results_dir/$obj.$tool.diff
+ ctf=$results_dir/$obj.$tool.ctf.c
+ dwarf=$results_dir/$obj.$tool.dwarf.c
+ $tool -F ctf $ctf_options $obj > $ctf
+ $tool -F dwarf $dwarf_options $obj > $dwarf
+ diff -up $dwarf $ctf > $diff
+ if [ -s $diff ] ; then
+ [ $# -gt 4 ] && vim $diff
+ exit 0
+ else
+ rm -f $diff $ctf $dwarf
+ fi
+}
+
+diff_one() {
+ local obj=$1
+ diff_tool "pahole" "--flat_arrays --show_private_classes --fixup_silly_bitfields" " " $obj $2
+ diff_tool "pfunct" "-V --no_parm_names" "-V" $obj $2
+}
+
+
+diff_dir() {
+ find . -type d | \
+ while read dir ; do
+ cd $dir
+ ls *.o 2> /dev/null |
+ while read obj ; do
+ ncus=$(readelf -wi $obj | grep DW_TAG_compile_unit | wc -l)
+ if [ $ncus -ne 1 ] ; then
+ continue
+ fi
+ echo $obj
+ pahole -Z $obj
+ diff_one $obj $1
+ done
+ cd - > /dev/null
+ done
+}
+
+rm -rf $results_dir
+mkdir $results_dir
+
+if [ $# -lt 2 ] ; then
+ diff_dir
+else
+ diff_one $*
+fi
diff --git a/ctracer.c b/ctracer.c
new file mode 100644
index 0000000..0098aa0
--- /dev/null
+++ b/ctracer.c
@@ -0,0 +1,1129 @@
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2006 Mandriva Conectiva S.A.
+ Copyright (C) 2006 Arnaldo Carvalho de Melo <acme@mandriva.com>
+*/
+
+#include <argp.h>
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <limits.h>
+#include <search.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "dwarves_reorganize.h"
+#include "dwarves_emit.h"
+#include "dwarves.h"
+#include "dutil.h"
+#include "elf_symtab.h"
+
+/*
+ * target class name
+ */
+static char *class_name;
+
+/*
+ * List of compilation units being looked for functions with
+ * pointers to the specified struct.
+ */
+static struct cus *methods_cus;
+
+/**
+ * Mini class, the subset of the traced class that is collected at the probes
+ */
+
+static struct class *mini_class;
+
+/*
+ * Directory where to generate source files
+ */
+static const char *src_dir = ".";
+
+/*
+ * Where to print the ctracer_methods.stp file
+ */
+static FILE *fp_methods;
+
+/*
+ * Where to print the ctracer_collector.c file
+ */
+static FILE *fp_collector;
+
+/*
+ * Where to print the ctracer_classes.h file
+ */
+static FILE *fp_classes;
+
+/*
+ * blacklist __init marked functions, i.e. functions that are
+ * in the ".init.text" ELF section and are thus discarded after
+ * boot.
+ */
+static struct strlist *init_blacklist;
+
+/*
+ * List of definitions and forward declarations already emitted for
+ * methods_cus, to avoid duplication.
+ */
+static struct type_emissions emissions;
+
+/*
+ * CU blacklist: if a "blacklist.cu" file is present, don't consider the
+ * CUs listed. Use a default of blacklist.cu.
+ */
+static const char *cu_blacklist_filename = "blacklist.cu";
+
+static struct strlist *cu_blacklist;
+
+static struct cu *cu_filter(struct cu *cu)
+{
+ if (strlist__has_entry(cu_blacklist, cu->name))
+ return NULL;
+ return cu;
+}
+
+/*
+ * List of probes and kretprobes already emitted, this is a hack to cope with
+ * name space collisions, a better solution would be to in these cases to use the
+ * compilation unit name (net/ipv4/tcp.o, for instance) as a prefix when a
+ * static function has the same name in multiple compilation units (aka object
+ * files).
+ */
+static void *probes_emitted;
+
+struct structure {
+ struct list_head node;
+ struct tag *class;
+ struct cu *cu;
+};
+
+static struct structure *structure__new(struct tag *class, struct cu *cu)
+{
+ struct structure *st = malloc(sizeof(*st));
+
+ if (st != NULL) {
+ st->class = class;
+ st->cu = cu;
+ }
+
+ return st;
+}
+
+/*
+ * structs that can be casted to the target class, e.g. i.e. that has the target
+ * class at its first member.
+ */
+static LIST_HEAD(aliases);
+
+/*
+ * structs have pointers to the target class.
+ */
+static LIST_HEAD(pointers);
+
+static const char *structure__name(const struct structure *st)
+{
+ return class__name(tag__class(st->class), st->cu);
+}
+
+static struct structure *structures__find(struct list_head *list, const char *name)
+{
+ struct structure *pos;
+
+ if (name == NULL)
+ return NULL;
+
+ list_for_each_entry(pos, list, node)
+ if (strcmp(structure__name(pos), name) == 0)
+ return pos;
+
+ return NULL;
+}
+
+static void structures__add(struct list_head *list, struct tag *class, struct cu *cu)
+{
+ struct structure *str = structure__new(class, cu);
+
+ if (str != NULL)
+ list_add(&str->node, list);
+}
+
+static int methods__compare(const void *a, const void *b)
+{
+ return strcmp(a, b);
+}
+
+static int methods__add(void **table, const char *str)
+{
+ char **s = tsearch(str, table, methods__compare);
+
+ if (s != NULL) {
+ if (*s == str) {
+ char *dup = strdup(str);
+ if (dup != NULL)
+ *s = dup;
+ else {
+ tdelete(str, table, methods__compare);
+ return -1;
+ }
+ } else
+ return -1;
+ } else
+ return -1;
+
+ return 0;
+}
+
+static void method__add(struct cu *cu, struct function *function, uint32_t id)
+{
+ list_add(&function->tool_node, &cu->tool_list);
+ function->priv = (void *)(long)id;
+}
+
+/*
+ * We want just the function tags that have as one of its parameters
+ * a pointer to the specified "class" (a struct, unions can be added later).
+ */
+static struct function *function__filter(struct function *function,
+ struct cu *cu, type_id_t target_type_id)
+{
+ if (function__inlined(function) ||
+ function->abstract_origin != 0 ||
+ !list_empty(&function->tool_node) ||
+ !ftype__has_parm_of_type(&function->proto, target_type_id, cu) ||
+ strlist__has_entry(init_blacklist, function__name(function, cu))) {
+ return NULL;
+ }
+
+ return function;
+}
+
+/*
+ * Iterate thru all the tags in the compilation unit, looking just for the
+ * function tags that have as one of its parameters a pointer to
+ * the specified "class" (struct).
+ */
+static int cu_find_methods_iterator(struct cu *cu, void *cookie)
+{
+ type_id_t target_type_id;
+ uint32_t function_id;
+ struct function *function;
+ struct tag *target = cu__find_struct_by_name(cu, cookie, 0,
+ &target_type_id);
+
+ INIT_LIST_HEAD(&cu->tool_list);
+
+ if (target == NULL)
+ return 0;
+
+ cu__for_each_function(cu, function_id, function)
+ if (function__filter(function, cu, target_type_id))
+ method__add(cu, function, function_id);
+
+ return 0;
+}
+
+static struct class_member *class_member__bitfield_tail(struct class_member *head,
+ struct class *class)
+{
+ struct class_member *tail = head,
+ *member = list_prepare_entry(head,
+ class__tags(class),
+ tag.node);
+ list_for_each_entry_continue(member, class__tags(class), tag.node)
+ if (member->byte_offset == head->byte_offset)
+ tail = member;
+ else
+ break;
+
+ return tail;
+}
+
+/*
+ * Bitfields are removed as one for simplification right now.
+ */
+static struct class_member *class__remove_member(struct class *class, const struct cu *cu,
+ struct class_member *member)
+{
+ size_t size = member->byte_size;
+ struct class_member *bitfield_tail = NULL;
+ struct list_head *next;
+ uint16_t member_hole = member->hole;
+
+ if (member->bitfield_size != 0) {
+ bitfield_tail = class_member__bitfield_tail(member, class);
+ member_hole = bitfield_tail->hole;
+ }
+ /*
+ * Is this the first member?
+ */
+ if (member->tag.node.prev == class__tags(class)) {
+ class->type.size -= size + member_hole;
+ class__subtract_offsets_from(class, bitfield_tail ?: member,
+ size + member_hole);
+ /*
+ * Is this the last member?
+ */
+ } else if (member->tag.node.next == class__tags(class)) {
+ if (size + class->padding >= cu->addr_size) {
+ class->type.size -= size + class->padding;
+ class->padding = 0;
+ } else
+ class->padding += size;
+ } else {
+ if (size + member_hole >= cu->addr_size) {
+ class->type.size -= size + member_hole;
+ class__subtract_offsets_from(class,
+ bitfield_tail ?: member,
+ size + member_hole);
+ } else {
+ struct class_member *from_prev =
+ list_entry(member->tag.node.prev,
+ struct class_member,
+ tag.node);
+ if (from_prev->hole == 0)
+ class->nr_holes++;
+ from_prev->hole += size + member_hole;
+ }
+ }
+ if (member_hole != 0)
+ class->nr_holes--;
+
+ if (bitfield_tail != NULL) {
+ next = bitfield_tail->tag.node.next;
+ list_del_range(&member->tag.node, &bitfield_tail->tag.node);
+ if (bitfield_tail->bit_hole != 0)
+ class->nr_bit_holes--;
+ } else {
+ next = member->tag.node.next;
+ list_del(&member->tag.node);
+ }
+
+ return list_entry(next, struct class_member, tag.node);
+}
+
+static size_t class__find_biggest_member_name(const struct class *class,
+ const struct cu *cu)
+{
+ struct class_member *pos;
+ size_t biggest_name_len = 0;
+
+ type__for_each_data_member(&class->type, pos) {
+ const size_t len = pos->name ?
+ strlen(class_member__name(pos, cu)) : 0;
+
+ if (len > biggest_name_len)
+ biggest_name_len = len;
+ }
+
+ return biggest_name_len;
+}
+
+static void class__emit_class_state_collector(struct class *class,
+ const struct cu *cu,
+ struct class *clone)
+{
+ struct class_member *pos;
+ int len = class__find_biggest_member_name(clone, cu);
+
+ fprintf(fp_collector,
+ "void ctracer__class_state(const void *from, void *to)\n"
+ "{\n"
+ "\tconst struct %s *obj = from;\n"
+ "\tstruct %s *mini_obj = to;\n\n",
+ class__name(class, cu), class__name(clone, cu));
+ type__for_each_data_member(&clone->type, pos)
+ fprintf(fp_collector, "\tmini_obj->%-*s = obj->%s;\n", len,
+ class_member__name(pos, cu),
+ class_member__name(pos, cu));
+ fputs("}\n\n", fp_collector);
+}
+
+static struct class *class__clone_base_types(const struct tag *tag,
+ struct cu *cu,
+ const char *new_class_name)
+{
+ struct class *class = tag__class(tag);
+ struct class_member *pos, *next;
+ struct class *clone = class__clone(class, new_class_name, cu);
+
+ if (clone == NULL)
+ return NULL;
+
+ type__for_each_data_member_safe(&clone->type, pos, next) {
+ struct tag *member_type = cu__type(cu, pos->tag.type);
+
+ tag__assert_search_result(member_type);
+ if (!tag__is_base_type(member_type, cu)) {
+ next = class__remove_member(clone, cu, pos);
+ class_member__delete(pos, cu);
+ }
+ }
+ class__fixup_alignment(clone, cu);
+ class__reorganize(clone, cu, 0, NULL);
+ return clone;
+}
+
+/**
+ * Converter to the legacy ostra tables, will be much improved in the future.
+ */
+static void emit_struct_member_table_entry(FILE *fp,
+ int field, const char *name,
+ int traced, const char *hooks)
+{
+ fprintf(fp, "%u:%s:", field, name);
+ if (traced)
+ fprintf(fp, "yes:%%object->%s:u:%s:none\n", name, hooks);
+ else
+ fprintf(fp, "no:None:None:%s:dev_null\n", hooks);
+}
+
+/**
+ * Generates a converter to the ostra lebacy tables format, needef by
+ * ostra-cg to preprocess the raw data collected from the debugfs/relay
+ * channel.
+ */
+static int class__emit_ostra_converter(struct tag *tag,
+ const struct cu *cu)
+{
+ struct class *class = tag__class(tag);
+ struct class_member *pos;
+ struct type *type = &mini_class->type;
+ int field = 0, first = 1;
+ char filename[128];
+ char parm_list[1024];
+ char *p = parm_list;
+ size_t n;
+ size_t plen = sizeof(parm_list);
+ FILE *fp_fields, *fp_converter;
+ const char *name = class__name(class, cu);
+
+ snprintf(filename, sizeof(filename), "%s/%s.fields", src_dir, name);
+ fp_fields = fopen(filename, "w");
+ if (fp_fields == NULL) {
+ fprintf(stderr, "ctracer: couldn't create %s\n", filename);
+ exit(EXIT_FAILURE);
+ }
+
+ snprintf(filename, sizeof(filename), "%s/ctracer2ostra.c", src_dir);
+
+ fp_converter = fopen(filename, "w");
+ if (fp_converter == NULL) {
+ fprintf(stderr, "ctracer: couldn't create %s\n", filename);
+ exit(EXIT_FAILURE);
+ }
+
+ fputs("#include \"ctracer_classes.h\"\n"
+ "#include <stdio.h>\n"
+ "#include <string.h>\n"
+ "#include \"ctracer_relay.h\"\n\n", fp_converter);
+ emit_struct_member_table_entry(fp_fields, field++, "action", 0,
+ "entry,exit");
+ emit_struct_member_table_entry(fp_fields, field++, "function_id", 0,
+ "entry,exit");
+ emit_struct_member_table_entry(fp_fields, field++, "object", 1,
+ "entry,exit");
+
+ fprintf(fp_converter, "\n"
+ "int main(void)\n"
+ "{\n"
+ "\twhile (1) {\n"
+ "\t\tstruct trace_entry hdr;\n"
+ "\t\tstruct ctracer__mini_%s obj;\n"
+ "\n"
+ "\t\tif (read(0, &hdr, sizeof(hdr)) != sizeof(hdr))\n"
+ "\t\t\tbreak;\n"
+ "\n"
+ "\t\tfprintf(stdout, \"%%llu %%c:%%llu:%%p\",\n"
+ "\t\t\thdr.nsec,\n"
+ "\t\t\thdr.probe_type ? 'o' : 'i',\n"
+ "\t\t\thdr.function_id,\n"
+ "\t\t\thdr.object);\n"
+ "\n"
+ "\t\tif (read(0, &obj, sizeof(obj)) != sizeof(obj))\n"
+ "\t\t\tbreak;\n"
+ "\t\tfprintf(stdout,\n"
+ "\t\t\t\":", name);
+
+ type__for_each_data_member(type, pos) {
+ if (first)
+ first = 0;
+ else {
+ fputc(':', fp_converter);
+ n = snprintf(p, plen, ",\n\t\t\t ");
+ plen -= n; p += n;
+ }
+ fprintf(fp_converter, "%%u");
+ n = snprintf(p, plen, "obj.%s", class_member__name(pos, cu));
+ plen -= n; p += n;
+ emit_struct_member_table_entry(fp_fields, field++,
+ class_member__name(pos, cu),
+ 1, "entry,exit");
+ }
+ fprintf(fp_converter,
+ "\\n\",\n\t\t\t %s);\n"
+ "\t}\n"
+ "\treturn 0;\n"
+ "}\n", parm_list);
+ fclose(fp_fields);
+ fclose(fp_converter);
+ return 0;
+}
+
+/*
+ * We want just the DW_TAG_structure_type tags that have a member that is a pointer
+ * to the target class.
+ */
+static struct tag *pointer_filter(struct tag *tag, struct cu *cu,
+ type_id_t target_type_id)
+{
+ struct type *type;
+ struct class_member *pos;
+ const char *class_name;
+
+ if (!tag__is_struct(tag))
+ return NULL;
+
+ type = tag__type(tag);
+ if (type->nr_members == 0)
+ return NULL;
+
+ class_name = class__name(tag__class(tag), cu);
+ if (class_name == NULL || structures__find(&pointers, class_name))
+ return NULL;
+
+ type__for_each_member(type, pos) {
+ struct tag *ctype = cu__type(cu, pos->tag.type);
+
+ tag__assert_search_result(ctype);
+ if (tag__is_pointer_to(ctype, target_type_id))
+ return tag;
+ }
+
+ return NULL;
+}
+
+/*
+ * Iterate thru all the tags in the compilation unit, looking for classes
+ * that have as one member that is a pointer to the target type.
+ */
+static int cu_find_pointers_iterator(struct cu *cu, void *class_name)
+{
+ type_id_t target_type_id, id;
+ struct tag *target = cu__find_struct_by_name(cu, class_name, 0,
+ &target_type_id), *pos;
+
+ if (target == NULL)
+ return 0;
+
+ cu__for_each_type(cu, id, pos)
+ if (pointer_filter(pos, cu, target_type_id))
+ structures__add(&pointers, pos, cu);
+
+ return 0;
+}
+
+static void class__find_pointers(const char *class_name)
+{
+ cus__for_each_cu(methods_cus, cu_find_pointers_iterator, (void *)class_name, cu_filter);
+}
+
+/*
+ * We want just the DW_TAG_structure_type tags that have as its first member
+ * a struct of type target.
+ */
+static struct tag *alias_filter(struct tag *tag, const struct cu *cu,
+ type_id_t target_type_id)
+{
+ struct type *type;
+ struct class_member *first_member;
+
+ if (!tag__is_struct(tag))
+ return NULL;
+
+ type = tag__type(tag);
+ if (type->nr_members == 0)
+ return NULL;
+
+ first_member = list_first_entry(&type->namespace.tags,
+ struct class_member, tag.node);
+ if (first_member->tag.type != target_type_id)
+ return NULL;
+
+ if (structures__find(&aliases, class__name(tag__class(tag), cu)))
+ return NULL;
+
+ return tag;
+}
+
+static void class__find_aliases(const char *class_name);
+
+/*
+ * Iterate thru all the tags in the compilation unit, looking for classes
+ * that have as its first member the specified "class" (struct).
+ */
+static int cu_find_aliases_iterator(struct cu *cu, void *class_name)
+{
+ type_id_t target_type_id, id;
+ struct tag *target = cu__find_struct_by_name(cu, class_name, 0,
+ &target_type_id), *pos;
+ if (target == NULL)
+ return 0;
+
+ cu__for_each_type(cu, id, pos) {
+ if (alias_filter(pos, cu, target_type_id)) {
+ const char *alias_name = class__name(tag__class(pos), cu);
+
+ structures__add(&aliases, pos, cu);
+
+ /*
+ * Now find aliases to this alias, e.g.:
+ *
+ * struct tcp_sock {
+ * struct inet_connection_sock {
+ * struct inet_sock {
+ * struct sock {
+ * }
+ * }
+ * }
+ * }
+ */
+ class__find_aliases(alias_name);
+ }
+ }
+
+ return 0;
+}
+
+static void class__find_aliases(const char *class_name)
+{
+ cus__for_each_cu(methods_cus, cu_find_aliases_iterator, (void *)class_name, cu_filter);
+}
+
+static void emit_list_of_types(struct list_head *list, const struct cu *cu)
+{
+ struct structure *pos;
+
+ list_for_each_entry(pos, list, node) {
+ struct type *type = tag__type(pos->class);
+ /*
+ * Lets look at the other CUs, perhaps we have already
+ * emmited this one
+ */
+ if (type_emissions__find_definition(&emissions, cu,
+ structure__name(pos))) {
+ type->definition_emitted = 1;
+ continue;
+ }
+ type__emit_definitions(pos->class, pos->cu, &emissions,
+ fp_classes);
+ type->definition_emitted = 1;
+ type__emit(pos->class, pos->cu, NULL, NULL, fp_classes);
+ tag__type(pos->class)->definition_emitted = 1;
+ fputc('\n', fp_classes);
+ }
+}
+
+static int class__emit_classes(struct tag *tag, struct cu *cu)
+{
+ struct class *class = tag__class(tag);
+ int err = -1;
+ char mini_class_name[128];
+
+ snprintf(mini_class_name, sizeof(mini_class_name), "ctracer__mini_%s",
+ class__name(class, cu));
+
+ mini_class = class__clone_base_types(tag, cu, mini_class_name);
+ if (mini_class == NULL)
+ goto out;
+
+ type__emit_definitions(tag, cu, &emissions, fp_classes);
+
+ type__emit(tag, cu, NULL, NULL, fp_classes);
+ fputs("\n/* class aliases */\n\n", fp_classes);
+
+ emit_list_of_types(&aliases, cu);
+
+ fputs("\n/* class with pointers */\n\n", fp_classes);
+
+ emit_list_of_types(&pointers, cu);
+
+ class__fprintf(mini_class, cu, fp_classes);
+ fputs(";\n\n", fp_classes);
+ class__emit_class_state_collector(class, cu, mini_class);
+ err = 0;
+out:
+ return err;
+}
+
+/*
+ * Emit the kprobes routine for one of the selected "methods", later we'll
+ * put this into the 'kprobes' table, in cu_emit_kprobes_table_iterator.
+ *
+ * This marks the function entry, function__emit_kretprobes will emit the
+ * probe for the function exit.
+ */
+static int function__emit_probes(struct function *func, uint32_t function_id,
+ const struct cu *cu,
+ const type_id_t target_type_id, int probe_type,
+ const char *member)
+{
+ struct parameter *pos;
+ const char *name = function__name(func, cu);
+
+ fprintf(fp_methods, "probe %s%s = kernel.function(\"%s@%s\")%s\n"
+ "{\n"
+ "}\n\n"
+ "probe %s%s\n"
+ "{\n", name,
+ probe_type == 0 ? "" : "__return",
+ name,
+ cu->name,
+ probe_type == 0 ? "" : ".return",
+ name,
+ probe_type == 0 ? "" : "__return");
+
+ list_for_each_entry(pos, &func->proto.parms, tag.node) {
+ struct tag *type = cu__type(cu, pos->tag.type);
+
+ tag__assert_search_result(type);
+ if (!tag__is_pointer_to(type, target_type_id))
+ continue;
+
+ if (member != NULL)
+ fprintf(fp_methods, "\tif ($%s)\n\t",
+ parameter__name(pos, cu));
+
+ fprintf(fp_methods,
+ "\tctracer__method_hook(%d, %d, $%s%s%s, %d);\n",
+ probe_type,
+ function_id,
+ parameter__name(pos, cu),
+ member ? "->" : "", member ?: "",
+ class__size(mini_class));
+ break;
+ }
+
+ fputs("}\n\n", fp_methods);
+ fflush(fp_methods);
+
+ return 0;
+}
+
+/*
+ * Iterate thru the list of methods previously collected by
+ * cu_find_methods_iterator, emitting the probes for function entry.
+ */
+static int cu_emit_probes_iterator(struct cu *cu, void *cookie)
+{
+ type_id_t target_type_id;
+ struct tag *target = cu__find_struct_by_name(cu, cookie, 0, &target_type_id);
+ struct function *pos;
+
+ /* OK, this type is not present in this compile unit */
+ if (target == NULL)
+ return 0;
+
+ list_for_each_entry(pos, &cu->tool_list, tool_node) {
+ uint32_t function_id = (long)pos->priv;
+
+ if (methods__add(&probes_emitted, function__name(pos, cu)) != 0)
+ continue;
+ function__emit_probes(pos, function_id, cu, target_type_id, 0, NULL); /* entry */
+ function__emit_probes(pos, function_id, cu, target_type_id, 1, NULL); /* exit */
+ }
+
+ return 0;
+}
+
+/*
+ * Iterate thru the list of methods previously collected by
+ * cu_find_methods_iterator, emitting the probes for function entry.
+ */
+static int cu_emit_pointer_probes_iterator(struct cu *cu, void *cookie)
+{
+ type_id_t target_type_id, pointer_id;
+ struct tag *target, *pointer;
+ struct function *pos_tag;
+ struct class_member *pos_member;
+
+ /* This CU doesn't have our classes */
+ if (list_empty(&cu->tool_list))
+ return 0;
+
+ target = cu__find_struct_by_name(cu, class_name, 1, &target_type_id);
+ pointer = cu__find_struct_by_name(cu, cookie, 0, &pointer_id);
+
+ /* OK, this type is not present in this compile unit */
+ if (target == NULL || pointer == NULL)
+ return 0;
+
+ /* for now just for the first member that is a pointer */
+ type__for_each_member(tag__type(pointer), pos_member) {
+ struct tag *ctype = cu__type(cu, pos_member->tag.type);
+
+ tag__assert_search_result(ctype);
+ if (tag__is_pointer_to(ctype, target_type_id))
+ break;
+ }
+
+ list_for_each_entry(pos_tag, &cu->tool_list, tool_node) {
+ uint32_t function_id = (long)pos_tag->priv;
+
+ if (methods__add(&probes_emitted, function__name(pos_tag, cu)) != 0)
+ continue;
+
+ function__emit_probes(pos_tag, function_id, cu, target_type_id, 0,
+ class_member__name(pos_member, cu)); /* entry */
+ function__emit_probes(pos_tag, function_id, cu, target_type_id, 1,
+ class_member__name(pos_member, cu)); /* exit */
+ }
+
+ return 0;
+}
+
+/*
+ * Iterate thru the list of methods previously collected by
+ * cu_find_methods_iterator, creating the functions table that will
+ * be used by ostra-cg
+ */
+static int cu_emit_functions_table(struct cu *cu, void *fp)
+{
+ struct function *pos;
+
+ list_for_each_entry(pos, &cu->tool_list, tool_node)
+ if (pos->priv != NULL) {
+ uint32_t function_id = (long)pos->priv;
+ fprintf(fp, "%d:%s\n", function_id,
+ function__name(pos, cu));
+ pos->priv = NULL;
+ }
+
+ return 0;
+}
+
+static int elf__open(const char *filename)
+{
+ int fd = open(filename, O_RDONLY);
+
+ if (fd < 0)
+ return -1;
+
+ int err = -1;
+
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ fprintf(stderr, "%s: cannot set libelf version.\n", __func__);
+ goto out_close;
+ }
+
+ Elf *elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+ if (elf == NULL) {
+ fprintf(stderr, "%s: cannot read %s ELF file.\n",
+ __func__, filename);
+ goto out_close;
+ }
+
+ GElf_Ehdr ehdr;
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ fprintf(stderr, "%s: cannot get elf header.\n", __func__);
+ goto out_elf_end;
+ }
+
+ GElf_Shdr shdr;
+ size_t init_index;
+ Elf_Scn *init = elf_section_by_name(elf, &ehdr, &shdr, ".init.text",
+ &init_index);
+ if (init == NULL)
+ goto out_elf_end;
+
+ struct elf_symtab *symtab = elf_symtab__new(".symtab", elf, &ehdr);
+ if (symtab == NULL)
+ goto out_elf_end;
+
+ init_blacklist = strlist__new(true);
+ if (init_blacklist == NULL)
+ goto out_elf_symtab_delete;
+
+ uint32_t index;
+ GElf_Sym sym;
+ elf_symtab__for_each_symbol(symtab, index, sym) {
+ if (!elf_sym__is_local_function(&sym))
+ continue;
+ if (elf_sym__section(&sym) != init_index)
+ continue;
+ err = strlist__add(init_blacklist, elf_sym__name(&sym, symtab));
+ if (err == -ENOMEM) {
+ fprintf(stderr, "failed for %s(%d,%zd)\n", elf_sym__name(&sym, symtab),elf_sym__section(&sym),init_index);
+ goto out_delete_blacklist;
+ }
+ }
+
+ err = 0;
+out_elf_symtab_delete:
+ elf_symtab__delete(symtab);
+out_elf_end:
+ elf_end(elf);
+out_close:
+ close(fd);
+ return err;
+out_delete_blacklist:
+ strlist__delete(init_blacklist);
+ goto out_elf_symtab_delete;
+}
+
+/* Name and version of program. */
+ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version;
+
+static const struct argp_option ctracer__options[] = {
+ {
+ .key = 'd',
+ .name = "src_dir",
+ .arg = "SRC_DIR",
+ .doc = "generate source files in this directory",
+ },
+ {
+ .key = 'C',
+ .name = "cu_blacklist",
+ .arg = "FILE",
+ .doc = "Blacklist the CUs in FILE",
+ },
+ {
+ .key = 'D',
+ .name = "dir",
+ .arg = "DIR",
+ .doc = "load files in this directory",
+ },
+ {
+ .key = 'g',
+ .name = "glob",
+ .arg = "GLOB",
+ .doc = "file mask to load",
+ },
+ {
+ .key = 'r',
+ .name = "recursive",
+ .doc = "recursively load files",
+ },
+ {
+ .name = NULL,
+ }
+};
+
+static const char *dirname, *glob;
+static int recursive;
+
+static error_t ctracer__options_parser(int key, char *arg,
+ struct argp_state *state __unused)
+{
+ switch (key) {
+ case 'd': src_dir = arg; break;
+ case 'C': cu_blacklist_filename = arg; break;
+ case 'D': dirname = arg; break;
+ case 'g': glob = arg; break;
+ case 'r': recursive = 1; break;
+ default: return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static const char ctracer__args_doc[] = "FILE CLASS";
+
+static struct argp ctracer__argp = {
+ .options = ctracer__options,
+ .parser = ctracer__options_parser,
+ .args_doc = ctracer__args_doc,
+};
+
+int main(int argc, char *argv[])
+{
+ int remaining, err;
+ struct tag *class;
+ struct cu *cu;
+ char *filename;
+ char functions_filename[PATH_MAX];
+ char methods_filename[PATH_MAX];
+ char collector_filename[PATH_MAX];
+ char classes_filename[PATH_MAX];
+ struct structure *pos;
+ FILE *fp_functions;
+ int rc = EXIT_FAILURE;
+
+ if (dwarves__init(0)) {
+ fputs("ctracer: insufficient memory\n", stderr);
+ goto out;
+ }
+
+ if (argp_parse(&ctracer__argp, argc, argv, 0, &remaining, NULL) ||
+ remaining < argc) {
+ switch (argc - remaining) {
+ case 1: goto failure;
+ case 2: filename = argv[remaining++];
+ class_name = argv[remaining++]; break;
+ default: goto failure;
+ }
+ } else {
+failure:
+ argp_help(&ctracer__argp, stderr, ARGP_HELP_SEE, argv[0]);
+ goto out;
+ }
+
+ type_emissions__init(&emissions);
+
+ /*
+ * Create the methods_cus (Compilation Units) object where we will
+ * load the objects where we'll look for functions pointers to the
+ * specified class, i.e. to find its "methods", where we'll insert
+ * the entry and exit hooks.
+ */
+ methods_cus = cus__new();
+ if (methods_cus == NULL) {
+ fputs("ctracer: insufficient memory\n", stderr);
+ goto out;
+ }
+
+ /*
+ * if --dir/-D was specified, recursively traverse the path looking for
+ * object files (compilation units) that match the glob specified (*.ko)
+ * for kernel modules, but could be "*.o" in the future when we support
+ * uprobes for user space tracing.
+ */
+ if (dirname != NULL && cus__load_dir(methods_cus, NULL, dirname, glob,
+ recursive) != 0) {
+ fprintf(stderr, "ctracer: couldn't load DWARF info "
+ "from %s dir with glob %s\n",
+ dirname, glob);
+ goto out;
+ }
+
+ /*
+ * If a filename was specified, for instance "vmlinux", load it too.
+ */
+ if (filename != NULL) {
+ if (elf__open(filename)) {
+ fprintf(stderr, "ctracer: couldn't load ELF symtab "
+ "info from %s\n", filename);
+ goto out;
+ }
+ err = cus__load_file(methods_cus, NULL, filename);
+ if (err != 0) {
+ cus__print_error_msg("ctracer", methods_cus, filename, err);
+ goto out;
+ }
+ }
+
+ /*
+ * See if the specified struct exists:
+ */
+ class = cus__find_struct_by_name(methods_cus, &cu, class_name, 0, NULL);
+ if (class == NULL) {
+ fprintf(stderr, "ctracer: struct %s not found!\n", class_name);
+ goto out;
+ }
+
+ snprintf(functions_filename, sizeof(functions_filename),
+ "%s/%s.functions", src_dir, class__name(tag__class(class), cu));
+ fp_functions = fopen(functions_filename, "w");
+ if (fp_functions == NULL) {
+ fprintf(stderr, "ctracer: couldn't create %s\n",
+ functions_filename);
+ goto out;
+ }
+
+ snprintf(methods_filename, sizeof(methods_filename),
+ "%s/ctracer_methods.stp", src_dir);
+ fp_methods = fopen(methods_filename, "w");
+ if (fp_methods == NULL) {
+ fprintf(stderr, "ctracer: couldn't create %s\n",
+ methods_filename);
+ goto out;
+ }
+
+ snprintf(collector_filename, sizeof(collector_filename),
+ "%s/ctracer_collector.c", src_dir);
+ fp_collector = fopen(collector_filename, "w");
+ if (fp_collector == NULL) {
+ fprintf(stderr, "ctracer: couldn't create %s\n",
+ collector_filename);
+ goto out;
+ }
+
+ snprintf(classes_filename, sizeof(classes_filename),
+ "%s/ctracer_classes.h", src_dir);
+ fp_classes = fopen(classes_filename, "w");
+ if (fp_classes == NULL) {
+ fprintf(stderr, "ctracer: couldn't create %s\n",
+ classes_filename);
+ goto out;
+ }
+
+ fputs("%{\n"
+ "#include </home/acme/git/pahole/lib/ctracer_relay.h>\n"
+ "%}\n"
+ "function ctracer__method_hook(probe_type, func, object, state_len)\n"
+ "%{\n"
+ "\tctracer__method_hook(_stp_gettimeofday_ns(), "
+ "THIS->probe_type, THIS->func, "
+ "(void *)(long)THIS->object, "
+ "THIS->state_len);\n"
+ "%}\n\n", fp_methods);
+
+ fputs("\n#include \"ctracer_classes.h\"\n\n", fp_collector);
+ class__find_aliases(class_name);
+ class__find_pointers(class_name);
+
+ class__emit_classes(class, cu);
+ fputc('\n', fp_collector);
+
+ class__emit_ostra_converter(class, cu);
+
+ cu_blacklist = strlist__new(true);
+ if (cu_blacklist != NULL)
+ strlist__load(cu_blacklist, cu_blacklist_filename);
+
+ cus__for_each_cu(methods_cus, cu_find_methods_iterator,
+ class_name, cu_filter);
+ cus__for_each_cu(methods_cus, cu_emit_probes_iterator,
+ class_name, cu_filter);
+ cus__for_each_cu(methods_cus, cu_emit_functions_table,
+ fp_functions, cu_filter);
+
+ list_for_each_entry(pos, &aliases, node) {
+ const char *alias_name = structure__name(pos);
+
+ cus__for_each_cu(methods_cus, cu_find_methods_iterator,
+ (void *)alias_name, cu_filter);
+ cus__for_each_cu(methods_cus, cu_emit_probes_iterator,
+ (void *)alias_name, cu_filter);
+ cus__for_each_cu(methods_cus, cu_emit_functions_table,
+ fp_functions, cu_filter);
+ }
+
+ list_for_each_entry(pos, &pointers, node) {
+ const char *pointer_name = structure__name(pos);
+ cus__for_each_cu(methods_cus, cu_find_methods_iterator,
+ (void *)pointer_name, cu_filter);
+ cus__for_each_cu(methods_cus, cu_emit_pointer_probes_iterator,
+ (void *)pointer_name, cu_filter);
+ cus__for_each_cu(methods_cus, cu_emit_functions_table, fp_functions,
+ cu_filter);
+ }
+
+ fclose(fp_methods);
+ fclose(fp_collector);
+ fclose(fp_functions);
+ fclose(fp_classes);
+ strlist__delete(cu_blacklist);
+
+ rc = EXIT_SUCCESS;
+out:
+ cus__delete(methods_cus);
+ dwarves__exit();
+ return rc;
+}
diff --git a/dtagnames.c b/dtagnames.c
new file mode 100644
index 0000000..0ffcbf7
--- /dev/null
+++ b/dtagnames.c
@@ -0,0 +1,63 @@
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2006 Mandriva Conectiva S.A.
+ Copyright (C) 2006 Arnaldo Carvalho de Melo <acme@mandriva.com>
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+
+#include "dwarves.h"
+#include "dutil.h"
+
+static void print_malloc_stats(void)
+{
+ struct mallinfo m = mallinfo();
+
+ fprintf(stderr, "size: %u\n", m.uordblks);
+}
+
+static int class__tag_name(struct tag *tag, struct cu *cu __unused,
+ void *cookie __unused)
+{
+ puts(dwarf_tag_name(tag->tag));
+ return 0;
+}
+
+static int cu__dump_class_tag_names(struct cu *cu, void *cookie __unused)
+{
+ cu__for_all_tags(cu, class__tag_name, NULL);
+ return 0;
+}
+
+static void cus__dump_class_tag_names(struct cus *cus)
+{
+ cus__for_each_cu(cus, cu__dump_class_tag_names, NULL, NULL);
+}
+
+int main(int argc __unused, char *argv[])
+{
+ int err, rc = EXIT_FAILURE;
+ struct cus *cus = cus__new();
+
+ if (dwarves__init(0) || cus == NULL) {
+ fputs("dtagnames: insufficient memory\n", stderr);
+ goto out;
+ }
+
+ err = cus__load_files(cus, NULL, argv + 1);
+ if (err != 0) {
+ cus__fprintf_load_files_err(cus, "dtagnames", argv + 1, err, stderr);
+ goto out;
+ }
+
+ cus__dump_class_tag_names(cus);
+ print_malloc_stats();
+ rc = EXIT_SUCCESS;
+out:
+ cus__delete(cus);
+ dwarves__exit();
+ return rc;
+}
diff --git a/dutil.c b/dutil.c
new file mode 100644
index 0000000..f7b853f
--- /dev/null
+++ b/dutil.c
@@ -0,0 +1,207 @@
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2007 Arnaldo Carvalho de Melo <acme@redhat.com>
+*/
+
+
+#include "dutil.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void *zalloc(const size_t size)
+{
+ void *s = malloc(size);
+ if (s != NULL)
+ memset(s, 0, size);
+ return s;
+}
+
+struct str_node *str_node__new(const char *s, bool dupstr)
+{
+ struct str_node *snode = malloc(sizeof(*snode));
+
+ if (snode != NULL){
+ if (dupstr) {
+ s = strdup(s);
+ if (s == NULL)
+ goto out_delete;
+ }
+ snode->s = s;
+ }
+
+ return snode;
+
+out_delete:
+ free(snode);
+ return NULL;
+}
+
+static void str_node__delete(struct str_node *snode, bool dupstr)
+{
+ if (dupstr)
+ free((void *)snode->s);
+ free(snode);
+}
+
+int __strlist__add(struct strlist *slist, const char *new_entry, void *priv)
+{
+ struct rb_node **p = &slist->entries.rb_node;
+ struct rb_node *parent = NULL;
+ struct str_node *sn;
+
+ while (*p != NULL) {
+ int rc;
+
+ parent = *p;
+ sn = rb_entry(parent, struct str_node, rb_node);
+ rc = strcmp(sn->s, new_entry);
+
+ if (rc > 0)
+ p = &(*p)->rb_left;
+ else if (rc < 0)
+ p = &(*p)->rb_right;
+ else
+ return -EEXIST;
+ }
+
+ sn = str_node__new(new_entry, slist->dupstr);
+ if (sn == NULL)
+ return -ENOMEM;
+
+ rb_link_node(&sn->rb_node, parent, p);
+ rb_insert_color(&sn->rb_node, &slist->entries);
+
+ sn->priv = priv;
+
+ list_add_tail(&sn->node, &slist->list_entries);
+
+ return 0;
+}
+
+int strlist__add(struct strlist *slist, const char *new_entry)
+{
+ return __strlist__add(slist, new_entry, NULL);
+}
+
+int strlist__load(struct strlist *slist, const char *filename)
+{
+ char entry[1024];
+ int err = -1;
+ FILE *fp = fopen(filename, "r");
+
+ if (fp == NULL)
+ return -1;
+
+ while (fgets(entry, sizeof(entry), fp) != NULL) {
+ const size_t len = strlen(entry);
+
+ if (len == 0)
+ continue;
+ entry[len - 1] = '\0';
+
+ if (strlist__add(slist, entry) != 0)
+ goto out;
+ }
+
+ err = 0;
+out:
+ fclose(fp);
+ return err;
+}
+
+struct strlist *strlist__new(bool dupstr)
+{
+ struct strlist *slist = malloc(sizeof(*slist));
+
+ if (slist != NULL) {
+ slist->entries = RB_ROOT;
+ INIT_LIST_HEAD(&slist->list_entries);
+ slist->dupstr = dupstr;
+ }
+
+ return slist;
+}
+
+void strlist__delete(struct strlist *slist)
+{
+ if (slist != NULL) {
+ struct str_node *pos;
+ struct rb_node *next = rb_first(&slist->entries);
+
+ while (next) {
+ pos = rb_entry(next, struct str_node, rb_node);
+ next = rb_next(&pos->rb_node);
+ strlist__remove(slist, pos);
+ }
+ slist->entries = RB_ROOT;
+ free(slist);
+ }
+}
+
+void strlist__remove(struct strlist *slist, struct str_node *sn)
+{
+ rb_erase(&sn->rb_node, &slist->entries);
+ list_del_init(&sn->node);
+ str_node__delete(sn, slist->dupstr);
+}
+
+bool strlist__has_entry(struct strlist *slist, const char *entry)
+{
+ struct rb_node **p = &slist->entries.rb_node;
+ struct rb_node *parent = NULL;
+
+ while (*p != NULL) {
+ struct str_node *sn;
+ int rc;
+
+ parent = *p;
+ sn = rb_entry(parent, struct str_node, rb_node);
+ rc = strcmp(sn->s, entry);
+
+ if (rc > 0)
+ p = &(*p)->rb_left;
+ else if (rc < 0)
+ p = &(*p)->rb_right;
+ else
+ return true;
+ }
+
+ return false;
+}
+
+Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
+ GElf_Shdr *shp, const char *name, size_t *index)
+{
+ Elf_Scn *sec = NULL;
+ size_t cnt = 1;
+
+ while ((sec = elf_nextscn(elf, sec)) != NULL) {
+ char *str;
+
+ gelf_getshdr(sec, shp);
+ str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
+ if (!strcmp(name, str)) {
+ if (index)
+ *index = cnt;
+ break;
+ }
+ ++cnt;
+ }
+
+ return sec;
+}
+
+char *strlwr(char *s)
+{
+ int len = strlen(s), i;
+
+ for (i = 0; i < len; ++i)
+ s[i] = tolower(s[i]);
+
+ return s;
+}
diff --git a/dutil.h b/dutil.h
new file mode 100644
index 0000000..676770d
--- /dev/null
+++ b/dutil.h
@@ -0,0 +1,337 @@
+#ifndef _DUTIL_H_
+#define _DUTIL_H_ 1
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ * Copyright (C) 2007..2009 Arnaldo Carvalho de Melo <acme@redhat.com>
+ *
+ * Some functions came from the Linux Kernel sources, copyrighted by a
+ * cast of dozens, please see the Linux Kernel git history for details.
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <elf.h>
+#include <gelf.h>
+#include <asm/bitsperlong.h>
+#include "rbtree.h"
+#include "list.h"
+
+#define BITS_PER_LONG __BITS_PER_LONG
+
+#ifndef __unused
+#define __unused __attribute__ ((unused))
+#endif
+
+#ifndef __pure
+#define __pure __attribute__ ((pure))
+#endif
+
+#define roundup(x,y) ((((x) + ((y) - 1)) / (y)) * (y))
+
+static inline __attribute__((const)) bool is_power_of_2(unsigned long n)
+{
+ return (n != 0 && ((n & (n - 1)) == 0));
+}
+
+/**
+ * fls - find last (most-significant) bit set
+ * @x: the word to search
+ *
+ * This is defined the same way as ffs.
+ * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
+ */
+static __always_inline int fls(int x)
+{
+ return x ? sizeof(x) * 8 - __builtin_clz(x) : 0;
+}
+
+/**
+ * fls64 - find last set bit in a 64-bit word
+ * @x: the word to search
+ *
+ * This is defined in a similar way as the libc and compiler builtin
+ * ffsll, but returns the position of the most significant set bit.
+ *
+ * fls64(value) returns 0 if value is 0 or the position of the last
+ * set bit if value is nonzero. The last (most significant) bit is
+ * at position 64.
+ */
+#if BITS_PER_LONG == 32
+static __always_inline int fls64(uint64_t x)
+{
+ uint32_t h = x >> 32;
+ if (h)
+ return fls(h) + 32;
+ return fls(x);
+}
+#elif BITS_PER_LONG == 64
+/**
+ * __fls - find last (most-significant) set bit in a long word
+ * @word: the word to search
+ *
+ * Undefined if no set bit exists, so code should check against 0 first.
+ */
+static __always_inline unsigned long __fls(unsigned long word)
+{
+ int num = BITS_PER_LONG - 1;
+
+#if BITS_PER_LONG == 64
+ if (!(word & (~0ul << 32))) {
+ num -= 32;
+ word <<= 32;
+ }
+#endif
+ if (!(word & (~0ul << (BITS_PER_LONG-16)))) {
+ num -= 16;
+ word <<= 16;
+ }
+ if (!(word & (~0ul << (BITS_PER_LONG-8)))) {
+ num -= 8;
+ word <<= 8;
+ }
+ if (!(word & (~0ul << (BITS_PER_LONG-4)))) {
+ num -= 4;
+ word <<= 4;
+ }
+ if (!(word & (~0ul << (BITS_PER_LONG-2)))) {
+ num -= 2;
+ word <<= 2;
+ }
+ if (!(word & (~0ul << (BITS_PER_LONG-1))))
+ num -= 1;
+ return num;
+}
+
+static __always_inline int fls64(uint64_t x)
+{
+ if (x == 0)
+ return 0;
+ return __fls(x) + 1;
+}
+#else
+#error BITS_PER_LONG not 32 or 64
+#endif
+
+static inline unsigned fls_long(unsigned long l)
+{
+ if (sizeof(l) == 4)
+ return fls(l);
+ return fls64(l);
+}
+
+/*
+ * round up to nearest power of two
+ */
+static inline __attribute__((const))
+unsigned long __roundup_pow_of_two(unsigned long n)
+{
+ return 1UL << fls_long(n - 1);
+}
+
+/*
+ * non-constant log of base 2 calculators
+ * - the arch may override these in asm/bitops.h if they can be implemented
+ * more efficiently than using fls() and fls64()
+ * - the arch is not required to handle n==0 if implementing the fallback
+ */
+static inline __attribute__((const))
+int __ilog2_u32(uint32_t n)
+{
+ return fls(n) - 1;
+}
+
+static inline __attribute__((const))
+int __ilog2_u64(uint64_t n)
+{
+ return fls64(n) - 1;
+}
+
+/*
+ * deal with unrepresentable constant logarithms
+ */
+extern __attribute__((const))
+int ____ilog2_NaN(void);
+
+/**
+ * ilog2 - log of base 2 of 32-bit or a 64-bit unsigned value
+ * @n - parameter
+ *
+ * constant-capable log of base 2 calculation
+ * - this can be used to initialise global variables from constant data, hence
+ * the massive ternary operator construction
+ *
+ * selects the appropriately-sized optimised version depending on sizeof(n)
+ */
+#define ilog2(n) \
+( \
+ __builtin_constant_p(n) ? ( \
+ (n) < 1 ? ____ilog2_NaN() : \
+ (n) & (1ULL << 63) ? 63 : \
+ (n) & (1ULL << 62) ? 62 : \
+ (n) & (1ULL << 61) ? 61 : \
+ (n) & (1ULL << 60) ? 60 : \
+ (n) & (1ULL << 59) ? 59 : \
+ (n) & (1ULL << 58) ? 58 : \
+ (n) & (1ULL << 57) ? 57 : \
+ (n) & (1ULL << 56) ? 56 : \
+ (n) & (1ULL << 55) ? 55 : \
+ (n) & (1ULL << 54) ? 54 : \
+ (n) & (1ULL << 53) ? 53 : \
+ (n) & (1ULL << 52) ? 52 : \
+ (n) & (1ULL << 51) ? 51 : \
+ (n) & (1ULL << 50) ? 50 : \
+ (n) & (1ULL << 49) ? 49 : \
+ (n) & (1ULL << 48) ? 48 : \
+ (n) & (1ULL << 47) ? 47 : \
+ (n) & (1ULL << 46) ? 46 : \
+ (n) & (1ULL << 45) ? 45 : \
+ (n) & (1ULL << 44) ? 44 : \
+ (n) & (1ULL << 43) ? 43 : \
+ (n) & (1ULL << 42) ? 42 : \
+ (n) & (1ULL << 41) ? 41 : \
+ (n) & (1ULL << 40) ? 40 : \
+ (n) & (1ULL << 39) ? 39 : \
+ (n) & (1ULL << 38) ? 38 : \
+ (n) & (1ULL << 37) ? 37 : \
+ (n) & (1ULL << 36) ? 36 : \
+ (n) & (1ULL << 35) ? 35 : \
+ (n) & (1ULL << 34) ? 34 : \
+ (n) & (1ULL << 33) ? 33 : \
+ (n) & (1ULL << 32) ? 32 : \
+ (n) & (1ULL << 31) ? 31 : \
+ (n) & (1ULL << 30) ? 30 : \
+ (n) & (1ULL << 29) ? 29 : \
+ (n) & (1ULL << 28) ? 28 : \
+ (n) & (1ULL << 27) ? 27 : \
+ (n) & (1ULL << 26) ? 26 : \
+ (n) & (1ULL << 25) ? 25 : \
+ (n) & (1ULL << 24) ? 24 : \
+ (n) & (1ULL << 23) ? 23 : \
+ (n) & (1ULL << 22) ? 22 : \
+ (n) & (1ULL << 21) ? 21 : \
+ (n) & (1ULL << 20) ? 20 : \
+ (n) & (1ULL << 19) ? 19 : \
+ (n) & (1ULL << 18) ? 18 : \
+ (n) & (1ULL << 17) ? 17 : \
+ (n) & (1ULL << 16) ? 16 : \
+ (n) & (1ULL << 15) ? 15 : \
+ (n) & (1ULL << 14) ? 14 : \
+ (n) & (1ULL << 13) ? 13 : \
+ (n) & (1ULL << 12) ? 12 : \
+ (n) & (1ULL << 11) ? 11 : \
+ (n) & (1ULL << 10) ? 10 : \
+ (n) & (1ULL << 9) ? 9 : \
+ (n) & (1ULL << 8) ? 8 : \
+ (n) & (1ULL << 7) ? 7 : \
+ (n) & (1ULL << 6) ? 6 : \
+ (n) & (1ULL << 5) ? 5 : \
+ (n) & (1ULL << 4) ? 4 : \
+ (n) & (1ULL << 3) ? 3 : \
+ (n) & (1ULL << 2) ? 2 : \
+ (n) & (1ULL << 1) ? 1 : \
+ (n) & (1ULL << 0) ? 0 : \
+ ____ilog2_NaN() \
+ ) : \
+ (sizeof(n) <= 4) ? \
+ __ilog2_u32(n) : \
+ __ilog2_u64(n) \
+ )
+
+/**
+ * roundup_pow_of_two - round the given value up to nearest power of two
+ * @n - parameter
+ *
+ * round the given value up to the nearest power of two
+ * - the result is undefined when n == 0
+ * - this can be used to initialise global variables from constant data
+ */
+#define roundup_pow_of_two(n) \
+( \
+ __builtin_constant_p(n) ? ( \
+ (n == 1) ? 1 : \
+ (1UL << (ilog2((n) - 1) + 1)) \
+ ) : \
+ __roundup_pow_of_two(n) \
+ )
+
+/* We need define two variables, argp_program_version_hook and
+ argp_program_bug_address, in all programs. argp.h declares these
+ variables as non-const (which is correct in general). But we can
+ do better, it is not going to change. So we want to move them into
+ the .rodata section. Define macros to do the trick. */
+#define ARGP_PROGRAM_VERSION_HOOK_DEF \
+ void (*const apvh) (FILE *, struct argp_state *) \
+ __asm ("argp_program_version_hook")
+#define ARGP_PROGRAM_BUG_ADDRESS_DEF \
+ const char *const apba__ __asm ("argp_program_bug_address")
+
+// Use a list_head so that we keep the original order when iterating in
+// the strlist.
+
+struct str_node {
+ struct rb_node rb_node;
+ struct list_head node;
+ const char *s;
+ void *priv;
+};
+
+// list_entries to keep the original order as passed, say, in the command line
+
+struct strlist {
+ struct rb_root entries;
+ struct list_head list_entries;
+ bool dupstr;
+};
+
+struct strlist *strlist__new(bool dupstr);
+void strlist__delete(struct strlist *slist);
+
+void strlist__remove(struct strlist *slist, struct str_node *sn);
+int strlist__load(struct strlist *slist, const char *filename);
+int strlist__add(struct strlist *slist, const char *str);
+int __strlist__add(struct strlist *slist, const char *str, void *priv);
+
+bool strlist__has_entry(struct strlist *slist, const char *entry);
+
+static inline bool strlist__empty(const struct strlist *slist)
+{
+ return rb_first(&slist->entries) == NULL;
+}
+
+/**
+ * strlist__for_each_entry_safe - iterate thru all the strings safe against removal of list entry
+ * @slist: struct strlist instance to iterate
+ * @pos: struct str_node iterator
+ * @n: tmp struct str_node
+ */
+#define strlist__for_each_entry_safe(slist, pos, n) \
+ list_for_each_entry_safe(pos, n, &(slist)->list_entries, node)
+
+/**
+ * strstarts - does @str start with @prefix?
+ * @str: string to examine
+ * @prefix: prefix to look for.
+ */
+static inline bool strstarts(const char *str, const char *prefix)
+{
+ return strncmp(str, prefix, strlen(prefix)) == 0;
+}
+
+void *zalloc(const size_t size);
+
+Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
+ GElf_Shdr *shp, const char *name, size_t *index);
+
+#ifndef SHT_GNU_ATTRIBUTES
+/* Just a way to check if we're using an old elfutils version */
+static inline int elf_getshdrstrndx(Elf *elf, size_t *dst)
+{
+ return elf_getshstrndx(elf, dst);
+}
+#endif
+
+char *strlwr(char *s);
+
+#endif /* _DUTIL_H_ */
diff --git a/dwarf_loader.c b/dwarf_loader.c
new file mode 100644
index 0000000..4638df7
--- /dev/null
+++ b/dwarf_loader.c
@@ -0,0 +1,2620 @@
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2008 Arnaldo Carvalho de Melo <acme@redhat.com>
+*/
+
+#include <assert.h>
+#include <dirent.h>
+#include <dwarf.h>
+#include <elfutils/libdwfl.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <libelf.h>
+#include <obstack.h>
+#include <search.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "list.h"
+#include "dwarves.h"
+#include "dutil.h"
+#include "pahole_strings.h"
+#include "hash.h"
+
+struct strings *strings;
+
+#ifndef DW_AT_alignment
+#define DW_AT_alignment 0x88
+#endif
+
+#ifndef DW_AT_GNU_vector
+#define DW_AT_GNU_vector 0x2107
+#endif
+
+#ifndef DW_TAG_GNU_call_site
+#define DW_TAG_GNU_call_site 0x4109
+#define DW_TAG_GNU_call_site_parameter 0x410a
+#endif
+
+#define hashtags__fn(key) hash_64(key, HASHTAGS__BITS)
+
+bool no_bitfield_type_recode = true;
+
+static void __tag__print_not_supported(uint32_t tag, const char *func)
+{
+ static bool dwarf_tags_warned[DW_TAG_GNU_call_site_parameter + 64];
+
+ if (tag < sizeof(dwarf_tags_warned)) {
+ if (dwarf_tags_warned[tag])
+ return;
+ dwarf_tags_warned[tag] = true;
+ }
+
+ fprintf(stderr, "%s: tag not supported %#x (%s)!\n", func,
+ tag, dwarf_tag_name(tag));
+}
+
+#define tag__print_not_supported(tag) \
+ __tag__print_not_supported(tag, __func__)
+
+struct dwarf_off_ref {
+ unsigned int from_types : 1;
+ Dwarf_Off off;
+};
+
+typedef struct dwarf_off_ref dwarf_off_ref;
+
+struct dwarf_tag {
+ struct hlist_node hash_node;
+ dwarf_off_ref type;
+ Dwarf_Off id;
+ union {
+ dwarf_off_ref abstract_origin;
+ dwarf_off_ref containing_type;
+ };
+ struct tag *tag;
+ uint32_t small_id;
+ strings_t decl_file;
+ uint16_t decl_line;
+};
+
+static dwarf_off_ref dwarf_tag__spec(struct dwarf_tag *dtag)
+{
+ return *(dwarf_off_ref *)(dtag + 1);
+}
+
+static void dwarf_tag__set_spec(struct dwarf_tag *dtag, dwarf_off_ref spec)
+{
+ *(dwarf_off_ref *)(dtag + 1) = spec;
+}
+
+#define HASHTAGS__BITS 15
+#define HASHTAGS__SIZE (1UL << HASHTAGS__BITS)
+
+#define obstack_chunk_alloc malloc
+#define obstack_chunk_free free
+
+static void *obstack_zalloc(struct obstack *obstack, size_t size)
+{
+ void *o = obstack_alloc(obstack, size);
+
+ if (o)
+ memset(o, 0, size);
+ return o;
+}
+
+struct dwarf_cu {
+ struct hlist_head hash_tags[HASHTAGS__SIZE];
+ struct hlist_head hash_types[HASHTAGS__SIZE];
+ struct obstack obstack;
+ struct cu *cu;
+ struct dwarf_cu *type_unit;
+};
+
+static void dwarf_cu__init(struct dwarf_cu *dcu)
+{
+ unsigned int i;
+ for (i = 0; i < HASHTAGS__SIZE; ++i) {
+ INIT_HLIST_HEAD(&dcu->hash_tags[i]);
+ INIT_HLIST_HEAD(&dcu->hash_types[i]);
+ }
+ obstack_init(&dcu->obstack);
+ dcu->type_unit = NULL;
+}
+
+static void hashtags__hash(struct hlist_head *hashtable,
+ struct dwarf_tag *dtag)
+{
+ struct hlist_head *head = hashtable + hashtags__fn(dtag->id);
+ hlist_add_head(&dtag->hash_node, head);
+}
+
+static struct dwarf_tag *hashtags__find(const struct hlist_head *hashtable,
+ const Dwarf_Off id)
+{
+ if (id == 0)
+ return NULL;
+
+ struct dwarf_tag *tpos;
+ struct hlist_node *pos;
+ uint16_t bucket = hashtags__fn(id);
+ const struct hlist_head *head = hashtable + bucket;
+
+ hlist_for_each_entry(tpos, pos, head, hash_node) {
+ if (tpos->id == id)
+ return tpos;
+ }
+
+ return NULL;
+}
+
+static void cu__hash(struct cu *cu, struct tag *tag)
+{
+ struct dwarf_cu *dcu = cu->priv;
+ struct hlist_head *hashtable = tag__is_tag_type(tag) ?
+ dcu->hash_types :
+ dcu->hash_tags;
+ hashtags__hash(hashtable, tag->priv);
+}
+
+static struct dwarf_tag *dwarf_cu__find_tag_by_ref(const struct dwarf_cu *cu,
+ const struct dwarf_off_ref *ref)
+{
+ if (cu == NULL)
+ return NULL;
+ if (ref->from_types) {
+ return NULL;
+ }
+ return hashtags__find(cu->hash_tags, ref->off);
+}
+
+static struct dwarf_tag *dwarf_cu__find_type_by_ref(const struct dwarf_cu *dcu,
+ const struct dwarf_off_ref *ref)
+{
+ if (dcu == NULL)
+ return NULL;
+ if (ref->from_types) {
+ dcu = dcu->type_unit;
+ if (dcu == NULL) {
+ return NULL;
+ }
+ }
+ return hashtags__find(dcu->hash_types, ref->off);
+}
+
+extern struct strings *strings;
+
+static void *memdup(const void *src, size_t len, struct cu *cu)
+{
+ void *s = obstack_alloc(&cu->obstack, len);
+ if (s != NULL)
+ memcpy(s, src, len);
+ return s;
+}
+
+/* Number decoding macros. See 7.6 Variable Length Data. */
+
+#define get_uleb128_step(var, addr, nth, break) \
+ __b = *(addr)++; \
+ var |= (uintmax_t) (__b & 0x7f) << (nth * 7); \
+ if ((__b & 0x80) == 0) \
+ break
+
+#define get_uleb128_rest_return(var, i, addrp) \
+ do { \
+ for (; i < 10; ++i) { \
+ get_uleb128_step(var, *addrp, i, \
+ return var); \
+ } \
+ /* Other implementations set VALUE to UINT_MAX in this \
+ case. So we better do this as well. */ \
+ return UINT64_MAX; \
+ } while (0)
+
+static uint64_t __libdw_get_uleb128(uint64_t acc, uint32_t i,
+ const uint8_t **addrp)
+{
+ uint8_t __b;
+ get_uleb128_rest_return (acc, i, addrp);
+}
+
+#define get_uleb128(var, addr) \
+ do { \
+ uint8_t __b; \
+ var = 0; \
+ get_uleb128_step(var, addr, 0, break); \
+ var = __libdw_get_uleb128 (var, 1, &(addr)); \
+ } while (0)
+
+static uint64_t attr_numeric(Dwarf_Die *die, uint32_t name)
+{
+ Dwarf_Attribute attr;
+ uint32_t form;
+
+ if (dwarf_attr(die, name, &attr) == NULL)
+ return 0;
+
+ form = dwarf_whatform(&attr);
+
+ switch (form) {
+ case DW_FORM_addr: {
+ Dwarf_Addr addr;
+ if (dwarf_formaddr(&attr, &addr) == 0)
+ return addr;
+ }
+ break;
+ case DW_FORM_data1:
+ case DW_FORM_data2:
+ case DW_FORM_data4:
+ case DW_FORM_data8:
+ case DW_FORM_sdata:
+ case DW_FORM_udata: {
+ Dwarf_Word value;
+ if (dwarf_formudata(&attr, &value) == 0)
+ return value;
+ }
+ break;
+ case DW_FORM_flag:
+ case DW_FORM_flag_present: {
+ bool value;
+ if (dwarf_formflag(&attr, &value) == 0)
+ return value;
+ }
+ break;
+ default:
+ fprintf(stderr, "DW_AT_<0x%x>=0x%x\n", name, form);
+ break;
+ }
+
+ return 0;
+}
+
+static uint64_t dwarf_expr(const uint8_t *expr, uint32_t len __unused)
+{
+ /* Common case: offset from start of the class */
+ if (expr[0] == DW_OP_plus_uconst ||
+ expr[0] == DW_OP_constu) {
+ uint64_t result;
+ ++expr;
+ get_uleb128(result, expr);
+ return result;
+ }
+
+ fprintf(stderr, "%s: unhandled %#x DW_OP_ operation\n",
+ __func__, *expr);
+ return UINT64_MAX;
+}
+
+static Dwarf_Off attr_offset(Dwarf_Die *die, const uint32_t name)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Block block;
+
+ if (dwarf_attr(die, name, &attr) == NULL)
+ return 0;
+
+ switch (dwarf_whatform(&attr)) {
+ case DW_FORM_data1:
+ case DW_FORM_data2:
+ case DW_FORM_data4:
+ case DW_FORM_data8:
+ case DW_FORM_sdata:
+ case DW_FORM_udata: {
+ Dwarf_Word value;
+ if (dwarf_formudata(&attr, &value) == 0)
+ return value;
+ break;
+ }
+ default:
+ if (dwarf_formblock(&attr, &block) == 0)
+ return dwarf_expr(block.data, block.length);
+ }
+
+ return 0;
+}
+
+static const char *attr_string(Dwarf_Die *die, uint32_t name)
+{
+ Dwarf_Attribute attr;
+ if (dwarf_attr(die, name, &attr) != NULL)
+ return dwarf_formstring(&attr);
+ return NULL;
+}
+
+static struct dwarf_off_ref attr_type(Dwarf_Die *die, uint32_t attr_name)
+{
+ Dwarf_Attribute attr;
+ struct dwarf_off_ref ref;
+ if (dwarf_attr(die, attr_name, &attr) != NULL) {
+ Dwarf_Die type_die;
+ if (dwarf_formref_die(&attr, &type_die) != NULL) {
+ ref.from_types = attr.form == DW_FORM_ref_sig8;
+ ref.off = dwarf_dieoffset(&type_die);
+ return ref;
+ }
+ }
+ memset(&ref, 0, sizeof(ref));
+ return ref;
+}
+
+static int attr_location(Dwarf_Die *die, Dwarf_Op **expr, size_t *exprlen)
+{
+ Dwarf_Attribute attr;
+ if (dwarf_attr(die, DW_AT_location, &attr) != NULL) {
+ if (dwarf_getlocation(&attr, expr, exprlen) == 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+static void *__tag__alloc(struct dwarf_cu *dcu, size_t size, bool spec)
+{
+ struct dwarf_tag *dtag = obstack_zalloc(&dcu->obstack,
+ (sizeof(*dtag) +
+ (spec ? sizeof(dwarf_off_ref) : 0)));
+ if (dtag == NULL)
+ return NULL;
+
+ struct tag *tag = obstack_zalloc(&dcu->cu->obstack, size);
+
+ if (tag == NULL)
+ return NULL;
+
+ dtag->tag = tag;
+ tag->priv = dtag;
+ tag->type = 0;
+ tag->top_level = 0;
+
+ return tag;
+}
+
+static void *tag__alloc(struct cu *cu, size_t size)
+{
+ return __tag__alloc(cu->priv, size, false);
+}
+
+static void *tag__alloc_with_spec(struct cu *cu, size_t size)
+{
+ return __tag__alloc(cu->priv, size, true);
+}
+
+static void tag__init(struct tag *tag, struct cu *cu, Dwarf_Die *die)
+{
+ struct dwarf_tag *dtag = tag->priv;
+
+ tag->tag = dwarf_tag(die);
+
+ dtag->id = dwarf_dieoffset(die);
+
+ if (tag->tag == DW_TAG_imported_module ||
+ tag->tag == DW_TAG_imported_declaration)
+ dtag->type = attr_type(die, DW_AT_import);
+ else
+ dtag->type = attr_type(die, DW_AT_type);
+
+ dtag->abstract_origin = attr_type(die, DW_AT_abstract_origin);
+ tag->recursivity_level = 0;
+
+ if (cu->extra_dbg_info) {
+ int32_t decl_line;
+ const char *decl_file = dwarf_decl_file(die);
+ static const char *last_decl_file;
+ static uint32_t last_decl_file_idx;
+
+ if (decl_file != last_decl_file) {
+ last_decl_file_idx = strings__add(strings, decl_file);
+ last_decl_file = decl_file;
+ }
+
+ dtag->decl_file = last_decl_file_idx;
+ dwarf_decl_line(die, &decl_line);
+ dtag->decl_line = decl_line;
+ }
+
+ INIT_LIST_HEAD(&tag->node);
+}
+
+static struct tag *tag__new(Dwarf_Die *die, struct cu *cu)
+{
+ struct tag *tag = tag__alloc(cu, sizeof(*tag));
+
+ if (tag != NULL)
+ tag__init(tag, cu, die);
+
+ return tag;
+}
+
+static struct ptr_to_member_type *ptr_to_member_type__new(Dwarf_Die *die,
+ struct cu *cu)
+{
+ struct ptr_to_member_type *ptr = tag__alloc(cu, sizeof(*ptr));
+
+ if (ptr != NULL) {
+ tag__init(&ptr->tag, cu, die);
+ struct dwarf_tag *dtag = ptr->tag.priv;
+ dtag->containing_type = attr_type(die, DW_AT_containing_type);
+ }
+
+ return ptr;
+}
+
+static struct base_type *base_type__new(Dwarf_Die *die, struct cu *cu)
+{
+ struct base_type *bt = tag__alloc(cu, sizeof(*bt));
+
+ if (bt != NULL) {
+ tag__init(&bt->tag, cu, die);
+ bt->name = strings__add(strings, attr_string(die, DW_AT_name));
+ bt->bit_size = attr_numeric(die, DW_AT_byte_size) * 8;
+ uint64_t encoding = attr_numeric(die, DW_AT_encoding);
+ bt->is_bool = encoding == DW_ATE_boolean;
+ bt->is_signed = encoding == DW_ATE_signed;
+ bt->is_varargs = false;
+ bt->name_has_encoding = true;
+ }
+
+ return bt;
+}
+
+static struct array_type *array_type__new(Dwarf_Die *die, struct cu *cu)
+{
+ struct array_type *at = tag__alloc(cu, sizeof(*at));
+
+ if (at != NULL) {
+ tag__init(&at->tag, cu, die);
+ at->dimensions = 0;
+ at->nr_entries = NULL;
+ at->is_vector = dwarf_hasattr(die, DW_AT_GNU_vector);
+ }
+
+ return at;
+}
+
+static struct string_type *string_type__new(Dwarf_Die *die, struct cu *cu)
+{
+ struct string_type *st = tag__alloc(cu, sizeof(*st));
+
+ if (st != NULL) {
+ tag__init(&st->tag, cu, die);
+ st->nr_entries = attr_numeric(die, DW_AT_byte_size);
+ if (st->nr_entries == 0)
+ st->nr_entries = 1;
+ }
+
+ return st;
+}
+
+static void namespace__init(struct namespace *namespace, Dwarf_Die *die,
+ struct cu *cu)
+{
+ tag__init(&namespace->tag, cu, die);
+ INIT_LIST_HEAD(&namespace->tags);
+ namespace->sname = 0;
+ namespace->name = strings__add(strings, attr_string(die, DW_AT_name));
+ namespace->nr_tags = 0;
+ namespace->shared_tags = 0;
+}
+
+static struct namespace *namespace__new(Dwarf_Die *die, struct cu *cu)
+{
+ struct namespace *namespace = tag__alloc(cu, sizeof(*namespace));
+
+ if (namespace != NULL)
+ namespace__init(namespace, die, cu);
+
+ return namespace;
+}
+
+static void type__init(struct type *type, Dwarf_Die *die, struct cu *cu)
+{
+ namespace__init(&type->namespace, die, cu);
+ __type__init(type);
+ type->size = attr_numeric(die, DW_AT_byte_size);
+ type->alignment = attr_numeric(die, DW_AT_alignment);
+ type->declaration = attr_numeric(die, DW_AT_declaration);
+ dwarf_tag__set_spec(type->namespace.tag.priv,
+ attr_type(die, DW_AT_specification));
+ type->definition_emitted = 0;
+ type->fwd_decl_emitted = 0;
+ type->resized = 0;
+ type->nr_members = 0;
+ type->nr_static_members = 0;
+}
+
+static struct type *type__new(Dwarf_Die *die, struct cu *cu)
+{
+ struct type *type = tag__alloc_with_spec(cu, sizeof(*type));
+
+ if (type != NULL)
+ type__init(type, die, cu);
+
+ return type;
+}
+
+static struct enumerator *enumerator__new(Dwarf_Die *die, struct cu *cu)
+{
+ struct enumerator *enumerator = tag__alloc(cu, sizeof(*enumerator));
+
+ if (enumerator != NULL) {
+ tag__init(&enumerator->tag, cu, die);
+ enumerator->name = strings__add(strings, attr_string(die, DW_AT_name));
+ enumerator->value = attr_numeric(die, DW_AT_const_value);
+ }
+
+ return enumerator;
+}
+
+static enum vscope dwarf__location(Dwarf_Die *die, uint64_t *addr, struct location *location)
+{
+ enum vscope scope = VSCOPE_UNKNOWN;
+
+ if (attr_location(die, &location->expr, &location->exprlen) != 0)
+ scope = VSCOPE_OPTIMIZED;
+ else if (location->exprlen != 0) {
+ Dwarf_Op *expr = location->expr;
+ switch (expr->atom) {
+ case DW_OP_addr:
+ scope = VSCOPE_GLOBAL;
+ *addr = expr[0].number;
+ break;
+ case DW_OP_reg1 ... DW_OP_reg31:
+ case DW_OP_breg0 ... DW_OP_breg31:
+ scope = VSCOPE_REGISTER; break;
+ case DW_OP_fbreg:
+ scope = VSCOPE_LOCAL; break;
+ }
+ }
+
+ return scope;
+}
+
+enum vscope variable__scope(const struct variable *var)
+{
+ return var->scope;
+}
+
+const char *variable__scope_str(const struct variable *var)
+{
+ switch (var->scope) {
+ case VSCOPE_LOCAL: return "local";
+ case VSCOPE_GLOBAL: return "global";
+ case VSCOPE_REGISTER: return "register";
+ case VSCOPE_OPTIMIZED: return "optimized";
+ default: break;
+ };
+
+ return "unknown";
+}
+
+static struct variable *variable__new(Dwarf_Die *die, struct cu *cu)
+{
+ struct variable *var;
+ bool has_specification;
+
+ has_specification = dwarf_hasattr(die, DW_AT_specification);
+ if (has_specification) {
+ var = tag__alloc_with_spec(cu, sizeof(*var));
+ } else {
+ var = tag__alloc(cu, sizeof(*var));
+ }
+
+ if (var != NULL) {
+ tag__init(&var->ip.tag, cu, die);
+ var->name = strings__add(strings, attr_string(die, DW_AT_name));
+ /* variable is visible outside of its enclosing cu */
+ var->external = dwarf_hasattr(die, DW_AT_external);
+ /* non-defining declaration of an object */
+ var->declaration = dwarf_hasattr(die, DW_AT_declaration);
+ var->scope = VSCOPE_UNKNOWN;
+ var->ip.addr = 0;
+ if (!var->declaration && cu->has_addr_info)
+ var->scope = dwarf__location(die, &var->ip.addr, &var->location);
+ if (has_specification) {
+ dwarf_tag__set_spec(var->ip.tag.priv,
+ attr_type(die, DW_AT_specification));
+ }
+ }
+
+ return var;
+}
+
+static int tag__recode_dwarf_bitfield(struct tag *tag, struct cu *cu, uint16_t bit_size)
+{
+ int id;
+ type_id_t short_id;
+ struct tag *recoded;
+ /* in all the cases the name is at the same offset */
+ strings_t name = tag__namespace(tag)->name;
+
+ switch (tag->tag) {
+ case DW_TAG_typedef: {
+ const struct dwarf_tag *dtag = tag->priv;
+ struct dwarf_tag *dtype = dwarf_cu__find_type_by_ref(cu->priv,
+ &dtag->type);
+ struct tag *type = dtype->tag;
+
+ id = tag__recode_dwarf_bitfield(type, cu, bit_size);
+ if (id < 0)
+ return id;
+
+ struct type *new_typedef = obstack_zalloc(&cu->obstack,
+ sizeof(*new_typedef));
+ if (new_typedef == NULL)
+ return -ENOMEM;
+
+ recoded = (struct tag *)new_typedef;
+ recoded->tag = DW_TAG_typedef;
+ recoded->type = id;
+ new_typedef->namespace.name = tag__namespace(tag)->name;
+ }
+ break;
+
+ case DW_TAG_const_type:
+ case DW_TAG_volatile_type: {
+ const struct dwarf_tag *dtag = tag->priv;
+ struct dwarf_tag *dtype = dwarf_cu__find_type_by_ref(cu->priv, &dtag->type);
+ struct tag *type = dtype->tag;
+
+ id = tag__recode_dwarf_bitfield(type, cu, bit_size);
+ if (id == tag->type)
+ return id;
+
+ recoded = obstack_zalloc(&cu->obstack, sizeof(*recoded));
+ if (recoded == NULL)
+ return -ENOMEM;
+
+ recoded->tag = DW_TAG_volatile_type;
+ recoded->type = id;
+ }
+ break;
+
+ case DW_TAG_base_type:
+ /*
+ * Here we must search on the final, core cu, not on
+ * the dwarf_cu as in dwarf there are no such things
+ * as base_types of less than 8 bits, etc.
+ */
+ recoded = cu__find_base_type_by_sname_and_size(cu, name, bit_size, &short_id);
+ if (recoded != NULL)
+ return short_id;
+
+ struct base_type *new_bt = obstack_zalloc(&cu->obstack,
+ sizeof(*new_bt));
+ if (new_bt == NULL)
+ return -ENOMEM;
+
+ recoded = (struct tag *)new_bt;
+ recoded->tag = DW_TAG_base_type;
+ recoded->top_level = 1;
+ new_bt->name = name;
+ new_bt->bit_size = bit_size;
+ break;
+
+ case DW_TAG_enumeration_type:
+ /*
+ * Here we must search on the final, core cu, not on
+ * the dwarf_cu as in dwarf there are no such things
+ * as enumeration_types of less than 8 bits, etc.
+ */
+ recoded = cu__find_enumeration_by_sname_and_size(cu, name, bit_size, &short_id);
+ if (recoded != NULL)
+ return short_id;
+
+ struct type *alias = tag__type(tag);
+ struct type *new_enum = obstack_zalloc(&cu->obstack, sizeof(*new_enum));
+ if (new_enum == NULL)
+ return -ENOMEM;
+
+ recoded = (struct tag *)new_enum;
+ recoded->tag = DW_TAG_enumeration_type;
+ recoded->top_level = 1;
+ new_enum->nr_members = alias->nr_members;
+ /*
+ * Share the tags
+ */
+ new_enum->namespace.tags.next = &alias->namespace.tags;
+ new_enum->namespace.shared_tags = 1;
+ new_enum->namespace.name = name;
+ new_enum->size = bit_size;
+ break;
+ default:
+ fprintf(stderr, "%s: tag=%s, name=%s, bit_size=%d\n",
+ __func__, dwarf_tag_name(tag->tag),
+ strings__ptr(strings, name), bit_size);
+ return -EINVAL;
+ }
+
+ uint32_t new_id;
+ if (cu__add_tag(cu, recoded, &new_id) == 0)
+ return new_id;
+
+ obstack_free(&cu->obstack, recoded);
+ return -ENOMEM;
+}
+
+int class_member__dwarf_recode_bitfield(struct class_member *member,
+ struct cu *cu)
+{
+ struct dwarf_tag *dtag = member->tag.priv;
+ struct dwarf_tag *type = dwarf_cu__find_type_by_ref(cu->priv, &dtag->type);
+ int recoded_type_id;
+
+ if (type == NULL)
+ return -ENOENT;
+
+ recoded_type_id = tag__recode_dwarf_bitfield(type->tag, cu, member->bitfield_size);
+ if (recoded_type_id < 0)
+ return recoded_type_id;
+
+ member->tag.type = recoded_type_id;
+ return 0;
+}
+
+static struct class_member *class_member__new(Dwarf_Die *die, struct cu *cu,
+ bool in_union)
+{
+ struct class_member *member = tag__alloc(cu, sizeof(*member));
+
+ if (member != NULL) {
+ tag__init(&member->tag, cu, die);
+ member->name = strings__add(strings, attr_string(die, DW_AT_name));
+ member->is_static = !in_union && !dwarf_hasattr(die, DW_AT_data_member_location);
+ member->const_value = attr_numeric(die, DW_AT_const_value);
+ member->alignment = attr_numeric(die, DW_AT_alignment);
+ member->byte_offset = attr_offset(die, DW_AT_data_member_location);
+ /*
+ * Bit offset calculated here is valid only for byte-aligned
+ * fields. For bitfields on little-endian archs we need to
+ * adjust them taking into account byte size of the field,
+ * which might not be yet known. So we'll re-calculate bit
+ * offset later, in class_member__cache_byte_size.
+ */
+ member->bit_offset = member->byte_offset * 8;
+ /*
+ * If DW_AT_byte_size is not present, byte size will be
+ * determined later in class_member__cache_byte_size using
+ * base integer/enum type
+ */
+ member->byte_size = attr_numeric(die, DW_AT_byte_size);
+ member->bitfield_offset = attr_numeric(die, DW_AT_bit_offset);
+ member->bitfield_size = attr_numeric(die, DW_AT_bit_size);
+ member->bit_hole = 0;
+ member->bitfield_end = 0;
+ member->visited = 0;
+ member->accessibility = attr_numeric(die, DW_AT_accessibility);
+ member->virtuality = attr_numeric(die, DW_AT_virtuality);
+ member->hole = 0;
+ }
+
+ return member;
+}
+
+static struct parameter *parameter__new(Dwarf_Die *die, struct cu *cu)
+{
+ struct parameter *parm = tag__alloc(cu, sizeof(*parm));
+
+ if (parm != NULL) {
+ tag__init(&parm->tag, cu, die);
+ parm->name = strings__add(strings, attr_string(die, DW_AT_name));
+ }
+
+ return parm;
+}
+
+static struct inline_expansion *inline_expansion__new(Dwarf_Die *die,
+ struct cu *cu)
+{
+ struct inline_expansion *exp = tag__alloc(cu, sizeof(*exp));
+
+ if (exp != NULL) {
+ struct dwarf_tag *dtag = exp->ip.tag.priv;
+
+ tag__init(&exp->ip.tag, cu, die);
+ dtag->decl_file =
+ strings__add(strings, attr_string(die, DW_AT_call_file));
+ dtag->decl_line = attr_numeric(die, DW_AT_call_line);
+ dtag->type = attr_type(die, DW_AT_abstract_origin);
+ exp->ip.addr = 0;
+ exp->high_pc = 0;
+
+ if (!cu->has_addr_info)
+ goto out;
+
+ if (dwarf_lowpc(die, &exp->ip.addr))
+ exp->ip.addr = 0;
+ if (dwarf_lowpc(die, &exp->high_pc))
+ exp->high_pc = 0;
+
+ exp->size = exp->high_pc - exp->ip.addr;
+ if (exp->size == 0) {
+ Dwarf_Addr base, start;
+ ptrdiff_t offset = 0;
+
+ while (1) {
+ offset = dwarf_ranges(die, offset, &base, &start,
+ &exp->high_pc);
+ start = (unsigned long)start;
+ exp->high_pc = (unsigned long)exp->high_pc;
+ if (offset <= 0)
+ break;
+ exp->size += exp->high_pc - start;
+ if (exp->ip.addr == 0)
+ exp->ip.addr = start;
+ }
+ }
+ }
+out:
+ return exp;
+}
+
+static struct label *label__new(Dwarf_Die *die, struct cu *cu)
+{
+ struct label *label = tag__alloc(cu, sizeof(*label));
+
+ if (label != NULL) {
+ tag__init(&label->ip.tag, cu, die);
+ label->name = strings__add(strings, attr_string(die, DW_AT_name));
+ if (!cu->has_addr_info || dwarf_lowpc(die, &label->ip.addr))
+ label->ip.addr = 0;
+ }
+
+ return label;
+}
+
+static struct class *class__new(Dwarf_Die *die, struct cu *cu)
+{
+ struct class *class = tag__alloc_with_spec(cu, sizeof(*class));
+
+ if (class != NULL) {
+ type__init(&class->type, die, cu);
+ INIT_LIST_HEAD(&class->vtable);
+ class->nr_vtable_entries =
+ class->nr_holes =
+ class->nr_bit_holes =
+ class->padding =
+ class->bit_padding = 0;
+ class->priv = NULL;
+ }
+
+ return class;
+}
+
+static void lexblock__init(struct lexblock *block, struct cu *cu,
+ Dwarf_Die *die)
+{
+ Dwarf_Off high_pc;
+
+ if (!cu->has_addr_info || dwarf_lowpc(die, &block->ip.addr)) {
+ block->ip.addr = 0;
+ block->size = 0;
+ } else if (dwarf_highpc(die, &high_pc))
+ block->size = 0;
+ else
+ block->size = high_pc - block->ip.addr;
+
+ INIT_LIST_HEAD(&block->tags);
+
+ block->size_inline_expansions =
+ block->nr_inline_expansions =
+ block->nr_labels =
+ block->nr_lexblocks =
+ block->nr_variables = 0;
+}
+
+static struct lexblock *lexblock__new(Dwarf_Die *die, struct cu *cu)
+{
+ struct lexblock *block = tag__alloc(cu, sizeof(*block));
+
+ if (block != NULL) {
+ tag__init(&block->ip.tag, cu, die);
+ lexblock__init(block, cu, die);
+ }
+
+ return block;
+}
+
+static void ftype__init(struct ftype *ftype, Dwarf_Die *die, struct cu *cu)
+{
+ const uint16_t tag = dwarf_tag(die);
+ assert(tag == DW_TAG_subprogram || tag == DW_TAG_subroutine_type);
+
+ tag__init(&ftype->tag, cu, die);
+ INIT_LIST_HEAD(&ftype->parms);
+ ftype->nr_parms = 0;
+ ftype->unspec_parms = 0;
+}
+
+static struct ftype *ftype__new(Dwarf_Die *die, struct cu *cu)
+{
+ struct ftype *ftype = tag__alloc(cu, sizeof(*ftype));
+
+ if (ftype != NULL)
+ ftype__init(ftype, die, cu);
+
+ return ftype;
+}
+
+static struct function *function__new(Dwarf_Die *die, struct cu *cu)
+{
+ struct function *func = tag__alloc_with_spec(cu, sizeof(*func));
+
+ if (func != NULL) {
+ ftype__init(&func->proto, die, cu);
+ lexblock__init(&func->lexblock, cu, die);
+ func->name = strings__add(strings, attr_string(die, DW_AT_name));
+ func->linkage_name = strings__add(strings, attr_string(die, DW_AT_MIPS_linkage_name));
+ func->inlined = attr_numeric(die, DW_AT_inline);
+ func->declaration = dwarf_hasattr(die, DW_AT_declaration);
+ func->external = dwarf_hasattr(die, DW_AT_external);
+ func->abstract_origin = dwarf_hasattr(die, DW_AT_abstract_origin);
+ dwarf_tag__set_spec(func->proto.tag.priv,
+ attr_type(die, DW_AT_specification));
+ func->accessibility = attr_numeric(die, DW_AT_accessibility);
+ func->virtuality = attr_numeric(die, DW_AT_virtuality);
+ INIT_LIST_HEAD(&func->vtable_node);
+ INIT_LIST_HEAD(&func->tool_node);
+ func->vtable_entry = -1;
+ if (dwarf_hasattr(die, DW_AT_vtable_elem_location))
+ func->vtable_entry = attr_offset(die, DW_AT_vtable_elem_location);
+ func->cu_total_size_inline_expansions = 0;
+ func->cu_total_nr_inline_expansions = 0;
+ func->priv = NULL;
+ }
+
+ return func;
+}
+
+static uint64_t attr_upper_bound(Dwarf_Die *die)
+{
+ Dwarf_Attribute attr;
+
+ if (dwarf_attr(die, DW_AT_upper_bound, &attr) != NULL) {
+ Dwarf_Word num;
+
+ if (dwarf_formudata(&attr, &num) == 0) {
+ return (uintmax_t)num + 1;
+ }
+ } else if (dwarf_attr(die, DW_AT_count, &attr) != NULL) {
+ Dwarf_Word num;
+
+ if (dwarf_formudata(&attr, &num) == 0) {
+ return (uintmax_t)num;
+ }
+ }
+
+ return 0;
+}
+
+static void __cu__tag_not_handled(Dwarf_Die *die, const char *fn)
+{
+ uint32_t tag = dwarf_tag(die);
+
+ fprintf(stderr, "%s: DW_TAG_%s (%#x) @ <%#llx> not handled!\n",
+ fn, dwarf_tag_name(tag), tag,
+ (unsigned long long)dwarf_dieoffset(die));
+}
+
+static struct tag unsupported_tag;
+
+#define cu__tag_not_handled(die) __cu__tag_not_handled(die, __FUNCTION__)
+
+static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu,
+ int toplevel, const char *fn);
+
+#define die__process_tag(die, cu, toplevel) \
+ __die__process_tag(die, cu, toplevel, __FUNCTION__)
+
+static struct tag *die__create_new_tag(Dwarf_Die *die, struct cu *cu)
+{
+ struct tag *tag = tag__new(die, cu);
+
+ if (tag != NULL) {
+ if (dwarf_haschildren(die))
+ fprintf(stderr, "%s: %s WITH children!\n", __func__,
+ dwarf_tag_name(tag->tag));
+ }
+
+ return tag;
+}
+
+static struct tag *die__create_new_ptr_to_member_type(Dwarf_Die *die,
+ struct cu *cu)
+{
+ struct ptr_to_member_type *ptr = ptr_to_member_type__new(die, cu);
+
+ return ptr ? &ptr->tag : NULL;
+}
+
+static int die__process_class(Dwarf_Die *die,
+ struct type *class, struct cu *cu);
+
+static struct tag *die__create_new_class(Dwarf_Die *die, struct cu *cu)
+{
+ Dwarf_Die child;
+ struct class *class = class__new(die, cu);
+
+ if (class != NULL &&
+ dwarf_haschildren(die) != 0 &&
+ dwarf_child(die, &child) == 0) {
+ if (die__process_class(&child, &class->type, cu) != 0) {
+ class__delete(class, cu);
+ class = NULL;
+ }
+ }
+
+ return class ? &class->type.namespace.tag : NULL;
+}
+
+static int die__process_namespace(Dwarf_Die *die, struct namespace *namespace,
+ struct cu *cu);
+
+static struct tag *die__create_new_namespace(Dwarf_Die *die, struct cu *cu)
+{
+ Dwarf_Die child;
+ struct namespace *namespace = namespace__new(die, cu);
+
+ if (namespace != NULL &&
+ dwarf_haschildren(die) != 0 &&
+ dwarf_child(die, &child) == 0) {
+ if (die__process_namespace(&child, namespace, cu) != 0) {
+ namespace__delete(namespace, cu);
+ namespace = NULL;
+ }
+ }
+
+ return namespace ? &namespace->tag : NULL;
+}
+
+static struct tag *die__create_new_union(Dwarf_Die *die, struct cu *cu)
+{
+ Dwarf_Die child;
+ struct type *utype = type__new(die, cu);
+
+ if (utype != NULL &&
+ dwarf_haschildren(die) != 0 &&
+ dwarf_child(die, &child) == 0) {
+ if (die__process_class(&child, utype, cu) != 0) {
+ type__delete(utype, cu);
+ utype = NULL;
+ }
+ }
+
+ return utype ? &utype->namespace.tag : NULL;
+}
+
+static struct tag *die__create_new_base_type(Dwarf_Die *die, struct cu *cu)
+{
+ struct base_type *base = base_type__new(die, cu);
+
+ if (base == NULL)
+ return NULL;
+
+ if (dwarf_haschildren(die))
+ fprintf(stderr, "%s: DW_TAG_base_type WITH children!\n",
+ __func__);
+
+ return &base->tag;
+}
+
+static struct tag *die__create_new_typedef(Dwarf_Die *die, struct cu *cu)
+{
+ struct type *tdef = type__new(die, cu);
+
+ if (tdef == NULL)
+ return NULL;
+
+ if (dwarf_haschildren(die)) {
+ struct dwarf_tag *dtag = tdef->namespace.tag.priv;
+ fprintf(stderr, "%s: DW_TAG_typedef %llx WITH children!\n",
+ __func__, (unsigned long long)dtag->id);
+ }
+
+ return &tdef->namespace.tag;
+}
+
+static struct tag *die__create_new_array(Dwarf_Die *die, struct cu *cu)
+{
+ Dwarf_Die child;
+ /* "64 dimensions will be enough for everybody." acme, 2006 */
+ const uint8_t max_dimensions = 64;
+ uint32_t nr_entries[max_dimensions];
+ struct array_type *array = array_type__new(die, cu);
+
+ if (array == NULL)
+ return NULL;
+
+ if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0)
+ return &array->tag;
+
+ die = &child;
+ do {
+ if (dwarf_tag(die) == DW_TAG_subrange_type) {
+ nr_entries[array->dimensions++] = attr_upper_bound(die);
+ if (array->dimensions == max_dimensions) {
+ fprintf(stderr, "%s: only %u dimensions are "
+ "supported!\n",
+ __FUNCTION__, max_dimensions);
+ break;
+ }
+ } else
+ cu__tag_not_handled(die);
+ } while (dwarf_siblingof(die, die) == 0);
+
+ array->nr_entries = memdup(nr_entries,
+ array->dimensions * sizeof(uint32_t), cu);
+ if (array->nr_entries == NULL)
+ goto out_free;
+
+ return &array->tag;
+out_free:
+ obstack_free(&cu->obstack, array);
+ return NULL;
+}
+
+static struct tag *die__create_new_string_type(Dwarf_Die *die, struct cu *cu)
+{
+ struct string_type *string = string_type__new(die, cu);
+
+ if (string == NULL)
+ return NULL;
+
+ return &string->tag;
+}
+
+static struct tag *die__create_new_parameter(Dwarf_Die *die,
+ struct ftype *ftype,
+ struct lexblock *lexblock,
+ struct cu *cu)
+{
+ struct parameter *parm = parameter__new(die, cu);
+
+ if (parm == NULL)
+ return NULL;
+
+ if (ftype != NULL)
+ ftype__add_parameter(ftype, parm);
+ else {
+ /*
+ * DW_TAG_formal_parameters on a non DW_TAG_subprogram nor
+ * DW_TAG_subroutine_type tag happens sometimes, likely due to
+ * compiler optimizing away a inline expansion (at least this
+ * was observed in some cases, such as in the Linux kernel
+ * current_kernel_time function circa 2.6.20-rc5), keep it in
+ * the lexblock tag list because it can be referenced as an
+ * DW_AT_abstract_origin in another DW_TAG_formal_parameter.
+ */
+ lexblock__add_tag(lexblock, &parm->tag);
+ }
+
+ return &parm->tag;
+}
+
+static struct tag *die__create_new_label(Dwarf_Die *die,
+ struct lexblock *lexblock,
+ struct cu *cu)
+{
+ struct label *label = label__new(die, cu);
+
+ if (label == NULL)
+ return NULL;
+
+ lexblock__add_label(lexblock, label);
+ return &label->ip.tag;
+}
+
+static struct tag *die__create_new_variable(Dwarf_Die *die, struct cu *cu)
+{
+ struct variable *var = variable__new(die, cu);
+
+ return var ? &var->ip.tag : NULL;
+}
+
+static struct tag *die__create_new_subroutine_type(Dwarf_Die *die,
+ struct cu *cu)
+{
+ Dwarf_Die child;
+ struct ftype *ftype = ftype__new(die, cu);
+ struct tag *tag;
+
+ if (ftype == NULL)
+ return NULL;
+
+ if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0)
+ goto out;
+
+ die = &child;
+ do {
+ uint32_t id;
+
+ switch (dwarf_tag(die)) {
+ case DW_TAG_subrange_type: // ADA stuff
+ tag__print_not_supported(dwarf_tag(die));
+ continue;
+ case DW_TAG_formal_parameter:
+ tag = die__create_new_parameter(die, ftype, NULL, cu);
+ break;
+ case DW_TAG_unspecified_parameters:
+ ftype->unspec_parms = 1;
+ continue;
+ default:
+ tag = die__process_tag(die, cu, 0);
+ if (tag == NULL)
+ goto out_delete;
+
+ if (tag == &unsupported_tag) {
+ tag__print_not_supported(dwarf_tag(die));
+ continue;
+ }
+
+ if (cu__add_tag(cu, tag, &id) < 0)
+ goto out_delete_tag;
+
+ goto hash;
+ }
+
+ if (tag == NULL)
+ goto out_delete;
+
+ if (cu__table_add_tag(cu, tag, &id) < 0)
+ goto out_delete_tag;
+hash:
+ cu__hash(cu, tag);
+ struct dwarf_tag *dtag = tag->priv;
+ dtag->small_id = id;
+ } while (dwarf_siblingof(die, die) == 0);
+out:
+ return &ftype->tag;
+out_delete_tag:
+ tag__delete(tag, cu);
+out_delete:
+ ftype__delete(ftype, cu);
+ return NULL;
+}
+
+static struct tag *die__create_new_enumeration(Dwarf_Die *die, struct cu *cu)
+{
+ Dwarf_Die child;
+ struct type *enumeration = type__new(die, cu);
+
+ if (enumeration == NULL)
+ return NULL;
+
+ if (enumeration->size == 0)
+ enumeration->size = sizeof(int) * 8;
+ else
+ enumeration->size *= 8;
+
+ if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0) {
+ /* Seen on libQtCore.so.4.3.4.debug,
+ * class QAbstractFileEngineIterator, enum EntryInfoType */
+ goto out;
+ }
+
+ die = &child;
+ do {
+ struct enumerator *enumerator;
+
+ if (dwarf_tag(die) != DW_TAG_enumerator) {
+ cu__tag_not_handled(die);
+ continue;
+ }
+ enumerator = enumerator__new(die, cu);
+ if (enumerator == NULL)
+ goto out_delete;
+
+ enumeration__add(enumeration, enumerator);
+ } while (dwarf_siblingof(die, die) == 0);
+out:
+ return &enumeration->namespace.tag;
+out_delete:
+ enumeration__delete(enumeration, cu);
+ return NULL;
+}
+
+static int die__process_class(Dwarf_Die *die, struct type *class,
+ struct cu *cu)
+{
+ const bool is_union = tag__is_union(&class->namespace.tag);
+
+ do {
+ switch (dwarf_tag(die)) {
+ case DW_TAG_subrange_type: // XXX: ADA stuff, its a type tho, will have other entries referencing it...
+ case DW_TAG_variant_part: // XXX: Rust stuff
+#ifdef STB_GNU_UNIQUE
+ case DW_TAG_GNU_formal_parameter_pack:
+ case DW_TAG_GNU_template_parameter_pack:
+ case DW_TAG_GNU_template_template_param:
+#endif
+ case DW_TAG_template_type_parameter:
+ case DW_TAG_template_value_parameter:
+ /*
+ * FIXME: probably we'll have to attach this as a list of
+ * template parameters to use at class__fprintf time...
+ *
+ * See:
+ * https://gcc.gnu.org/wiki/TemplateParmsDwarf
+ */
+ tag__print_not_supported(dwarf_tag(die));
+ continue;
+ case DW_TAG_inheritance:
+ case DW_TAG_member: {
+ struct class_member *member = class_member__new(die, cu, is_union);
+
+ if (member == NULL)
+ return -ENOMEM;
+
+ if (cu__is_c_plus_plus(cu)) {
+ uint32_t id;
+
+ if (cu__table_add_tag(cu, &member->tag, &id) < 0) {
+ class_member__delete(member, cu);
+ return -ENOMEM;
+ }
+
+ struct dwarf_tag *dtag = member->tag.priv;
+ dtag->small_id = id;
+ }
+
+ type__add_member(class, member);
+ cu__hash(cu, &member->tag);
+ }
+ continue;
+ default: {
+ struct tag *tag = die__process_tag(die, cu, 0);
+
+ if (tag == NULL)
+ return -ENOMEM;
+
+ if (tag == &unsupported_tag) {
+ tag__print_not_supported(dwarf_tag(die));
+ continue;
+ }
+
+ uint32_t id;
+
+ if (cu__table_add_tag(cu, tag, &id) < 0) {
+ tag__delete(tag, cu);
+ return -ENOMEM;
+ }
+
+ struct dwarf_tag *dtag = tag->priv;
+ dtag->small_id = id;
+
+ namespace__add_tag(&class->namespace, tag);
+ cu__hash(cu, tag);
+ if (tag__is_function(tag)) {
+ struct function *fself = tag__function(tag);
+
+ if (fself->vtable_entry != -1)
+ class__add_vtable_entry(type__class(class), fself);
+ }
+ continue;
+ }
+ }
+ } while (dwarf_siblingof(die, die) == 0);
+
+ return 0;
+}
+
+static int die__process_namespace(Dwarf_Die *die, struct namespace *namespace,
+ struct cu *cu)
+{
+ struct tag *tag;
+ do {
+ tag = die__process_tag(die, cu, 0);
+ if (tag == NULL)
+ goto out_enomem;
+
+ if (tag == &unsupported_tag) {
+ tag__print_not_supported(dwarf_tag(die));
+ continue;
+ }
+
+ uint32_t id;
+ if (cu__table_add_tag(cu, tag, &id) < 0)
+ goto out_delete_tag;
+
+ struct dwarf_tag *dtag = tag->priv;
+ dtag->small_id = id;
+
+ namespace__add_tag(namespace, tag);
+ cu__hash(cu, tag);
+ } while (dwarf_siblingof(die, die) == 0);
+
+ return 0;
+out_delete_tag:
+ tag__delete(tag, cu);
+out_enomem:
+ return -ENOMEM;
+}
+
+static int die__process_function(Dwarf_Die *die, struct ftype *ftype,
+ struct lexblock *lexblock, struct cu *cu);
+
+static int die__create_new_lexblock(Dwarf_Die *die,
+ struct cu *cu, struct lexblock *father)
+{
+ struct lexblock *lexblock = lexblock__new(die, cu);
+
+ if (lexblock != NULL) {
+ if (die__process_function(die, NULL, lexblock, cu) != 0)
+ goto out_delete;
+ }
+ if (father != NULL)
+ lexblock__add_lexblock(father, lexblock);
+ return 0;
+out_delete:
+ lexblock__delete(lexblock, cu);
+ return -ENOMEM;
+}
+
+static struct tag *die__create_new_inline_expansion(Dwarf_Die *die,
+ struct lexblock *lexblock,
+ struct cu *cu);
+
+static int die__process_inline_expansion(Dwarf_Die *die, struct lexblock *lexblock, struct cu *cu)
+{
+ Dwarf_Die child;
+ struct tag *tag;
+
+ if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0)
+ return 0;
+
+ die = &child;
+ do {
+ uint32_t id;
+
+ switch (dwarf_tag(die)) {
+ case DW_TAG_GNU_call_site:
+ case DW_TAG_GNU_call_site_parameter:
+ /*
+ * FIXME: read http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open
+ * and write proper support.
+ *
+ * From a quick read there is not much we can use in
+ * the existing dwarves tools, so just stop warning the user,
+ * developers will find these notes if wanting to use in a
+ * new tool.
+ */
+ continue;
+ case DW_TAG_lexical_block:
+ if (die__create_new_lexblock(die, cu, lexblock) != 0)
+ goto out_enomem;
+ continue;
+ case DW_TAG_formal_parameter:
+ /*
+ * FIXME:
+ * So far DW_TAG_inline_routine had just an
+ * abstract origin, but starting with
+ * /usr/lib/openoffice.org/basis3.0/program/libdbalx.so
+ * I realized it really has to be handled as a
+ * DW_TAG_function... Lets just get the types
+ * for 1.8, then fix this properly.
+ *
+ * cu__tag_not_handled(die);
+ */
+ continue;
+ case DW_TAG_inlined_subroutine:
+ tag = die__create_new_inline_expansion(die, lexblock, cu);
+ break;
+ case DW_TAG_label:
+ tag = die__create_new_label(die, lexblock, cu);
+ break;
+ default:
+ tag = die__process_tag(die, cu, 0);
+ if (tag == NULL)
+ goto out_enomem;
+
+ if (tag == &unsupported_tag) {
+ tag__print_not_supported(dwarf_tag(die));
+ continue;
+ }
+
+ if (cu__add_tag(cu, tag, &id) < 0)
+ goto out_delete_tag;
+ goto hash;
+ }
+
+ if (tag == NULL)
+ goto out_enomem;
+
+ if (cu__table_add_tag(cu, tag, &id) < 0)
+ goto out_delete_tag;
+hash:
+ cu__hash(cu, tag);
+ struct dwarf_tag *dtag = tag->priv;
+ dtag->small_id = id;
+ } while (dwarf_siblingof(die, die) == 0);
+
+ return 0;
+out_delete_tag:
+ tag__delete(tag, cu);
+out_enomem:
+ return -ENOMEM;
+}
+
+static struct tag *die__create_new_inline_expansion(Dwarf_Die *die,
+ struct lexblock *lexblock,
+ struct cu *cu)
+{
+ struct inline_expansion *exp = inline_expansion__new(die, cu);
+
+ if (exp == NULL)
+ return NULL;
+
+ if (die__process_inline_expansion(die, lexblock, cu) != 0) {
+ obstack_free(&cu->obstack, exp);
+ return NULL;
+ }
+
+ if (lexblock != NULL)
+ lexblock__add_inline_expansion(lexblock, exp);
+ return &exp->ip.tag;
+}
+
+static int die__process_function(Dwarf_Die *die, struct ftype *ftype,
+ struct lexblock *lexblock, struct cu *cu)
+{
+ Dwarf_Die child;
+ struct tag *tag;
+
+ if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0)
+ return 0;
+
+ die = &child;
+ do {
+ uint32_t id;
+
+ switch (dwarf_tag(die)) {
+ case DW_TAG_GNU_call_site:
+ case DW_TAG_GNU_call_site_parameter:
+ /*
+ * XXX: read http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open
+ * and write proper support.
+ *
+ * From a quick read there is not much we can use in
+ * the existing dwarves tools, so just stop warning the user,
+ * developers will find these notes if wanting to use in a
+ * new tool.
+ */
+ continue;
+ case DW_TAG_dwarf_procedure:
+ /*
+ * Ignore it, just scope expressions, that we have no use for (so far).
+ */
+ continue;
+#ifdef STB_GNU_UNIQUE
+ case DW_TAG_GNU_formal_parameter_pack:
+ case DW_TAG_GNU_template_parameter_pack:
+ case DW_TAG_GNU_template_template_param:
+#endif
+ case DW_TAG_template_type_parameter:
+ case DW_TAG_template_value_parameter:
+ /* FIXME: probably we'll have to attach this as a list of
+ * template parameters to use at class__fprintf time...
+ * See die__process_class */
+ tag__print_not_supported(dwarf_tag(die));
+ continue;
+ case DW_TAG_formal_parameter:
+ tag = die__create_new_parameter(die, ftype, lexblock, cu);
+ break;
+ case DW_TAG_variable:
+ tag = die__create_new_variable(die, cu);
+ if (tag == NULL)
+ goto out_enomem;
+ lexblock__add_variable(lexblock, tag__variable(tag));
+ break;
+ case DW_TAG_unspecified_parameters:
+ if (ftype != NULL)
+ ftype->unspec_parms = 1;
+ continue;
+ case DW_TAG_label:
+ tag = die__create_new_label(die, lexblock, cu);
+ break;
+ case DW_TAG_inlined_subroutine:
+ tag = die__create_new_inline_expansion(die, lexblock, cu);
+ break;
+ case DW_TAG_lexical_block:
+ if (die__create_new_lexblock(die, cu, lexblock) != 0)
+ goto out_enomem;
+ continue;
+ default:
+ tag = die__process_tag(die, cu, 0);
+
+ if (tag == NULL)
+ goto out_enomem;
+
+ if (tag == &unsupported_tag) {
+ tag__print_not_supported(dwarf_tag(die));
+ continue;
+ }
+
+ if (cu__add_tag(cu, tag, &id) < 0)
+ goto out_delete_tag;
+
+ goto hash;
+ }
+
+ if (tag == NULL)
+ goto out_enomem;
+
+ if (cu__table_add_tag(cu, tag, &id) < 0)
+ goto out_delete_tag;
+hash:
+ cu__hash(cu, tag);
+ struct dwarf_tag *dtag = tag->priv;
+ dtag->small_id = id;
+ } while (dwarf_siblingof(die, die) == 0);
+
+ return 0;
+out_delete_tag:
+ tag__delete(tag, cu);
+out_enomem:
+ return -ENOMEM;
+}
+
+static struct tag *die__create_new_function(Dwarf_Die *die, struct cu *cu)
+{
+ struct function *function = function__new(die, cu);
+
+ if (function != NULL &&
+ die__process_function(die, &function->proto,
+ &function->lexblock, cu) != 0) {
+ function__delete(function, cu);
+ function = NULL;
+ }
+
+ return function ? &function->proto.tag : NULL;
+}
+
+static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu,
+ int top_level, const char *fn)
+{
+ struct tag *tag;
+
+ switch (dwarf_tag(die)) {
+ case DW_TAG_imported_unit:
+ return NULL; // We don't support imported units yet, so to avoid segfaults
+ case DW_TAG_array_type:
+ tag = die__create_new_array(die, cu); break;
+ case DW_TAG_string_type: // FORTRAN stuff, looks like an array
+ tag = die__create_new_string_type(die, cu); break;
+ case DW_TAG_base_type:
+ tag = die__create_new_base_type(die, cu); break;
+ case DW_TAG_const_type:
+ case DW_TAG_imported_declaration:
+ case DW_TAG_imported_module:
+ case DW_TAG_pointer_type:
+ case DW_TAG_reference_type:
+ case DW_TAG_restrict_type:
+ case DW_TAG_unspecified_type:
+ case DW_TAG_volatile_type:
+ tag = die__create_new_tag(die, cu); break;
+ case DW_TAG_ptr_to_member_type:
+ tag = die__create_new_ptr_to_member_type(die, cu); break;
+ case DW_TAG_enumeration_type:
+ tag = die__create_new_enumeration(die, cu); break;
+ case DW_TAG_namespace:
+ tag = die__create_new_namespace(die, cu); break;
+ case DW_TAG_class_type:
+ case DW_TAG_interface_type:
+ case DW_TAG_structure_type:
+ tag = die__create_new_class(die, cu); break;
+ case DW_TAG_subprogram:
+ tag = die__create_new_function(die, cu); break;
+ case DW_TAG_subroutine_type:
+ tag = die__create_new_subroutine_type(die, cu); break;
+ case DW_TAG_rvalue_reference_type:
+ case DW_TAG_typedef:
+ tag = die__create_new_typedef(die, cu); break;
+ case DW_TAG_union_type:
+ tag = die__create_new_union(die, cu); break;
+ case DW_TAG_variable:
+ tag = die__create_new_variable(die, cu); break;
+ default:
+ __cu__tag_not_handled(die, fn);
+ /* fall thru */
+ case DW_TAG_dwarf_procedure:
+ /*
+ * Ignore it, just scope expressions, that we have no use for (so far).
+ */
+ tag = &unsupported_tag;
+ break;
+ }
+
+ if (tag != NULL)
+ tag->top_level = top_level;
+
+ return tag;
+}
+
+static int die__process_unit(Dwarf_Die *die, struct cu *cu)
+{
+ do {
+ struct tag *tag = die__process_tag(die, cu, 1);
+ if (tag == NULL)
+ return -ENOMEM;
+
+ if (tag == &unsupported_tag) {
+ // XXX special case DW_TAG_dwarf_procedure, appears when looking at a recent ~/bin/perf
+ // Investigate later how to properly support this...
+ if (dwarf_tag(die) != DW_TAG_dwarf_procedure)
+ tag__print_not_supported(dwarf_tag(die));
+ continue;
+ }
+
+ uint32_t id;
+ cu__add_tag(cu, tag, &id);
+ cu__hash(cu, tag);
+ struct dwarf_tag *dtag = tag->priv;
+ dtag->small_id = id;
+ } while (dwarf_siblingof(die, die) == 0);
+
+ return 0;
+}
+
+static void __tag__print_type_not_found(struct tag *tag, const char *func)
+{
+ struct dwarf_tag *dtag = tag->priv;
+ fprintf(stderr, "%s: couldn't find %#llx type for %#llx (%s)!\n", func,
+ (unsigned long long)dtag->type.off, (unsigned long long)dtag->id,
+ dwarf_tag_name(tag->tag));
+}
+
+#define tag__print_type_not_found(tag) \
+ __tag__print_type_not_found(tag, __func__)
+
+static void ftype__recode_dwarf_types(struct tag *tag, struct cu *cu);
+
+static int namespace__recode_dwarf_types(struct tag *tag, struct cu *cu)
+{
+ struct tag *pos;
+ struct dwarf_cu *dcu = cu->priv;
+ struct namespace *ns = tag__namespace(tag);
+
+ namespace__for_each_tag(ns, pos) {
+ struct dwarf_tag *dtype;
+ struct dwarf_tag *dpos = pos->priv;
+
+ if (tag__has_namespace(pos)) {
+ if (namespace__recode_dwarf_types(pos, cu))
+ return -1;
+ continue;
+ }
+
+ switch (pos->tag) {
+ case DW_TAG_member: {
+ struct class_member *member = tag__class_member(pos);
+ /*
+ * We may need to recode the type, possibly creating a
+ * suitably sized new base_type
+ */
+ if (member->bitfield_size != 0 && !no_bitfield_type_recode) {
+ if (class_member__dwarf_recode_bitfield(member, cu))
+ return -1;
+ continue;
+ }
+ }
+ break;
+ case DW_TAG_subroutine_type:
+ case DW_TAG_subprogram:
+ ftype__recode_dwarf_types(pos, cu);
+ break;
+ case DW_TAG_imported_module:
+ dtype = dwarf_cu__find_tag_by_ref(dcu, &dpos->type);
+ goto check_type;
+ /* Can be for both types and non types */
+ case DW_TAG_imported_declaration:
+ dtype = dwarf_cu__find_tag_by_ref(dcu, &dpos->type);
+ if (dtype != NULL)
+ goto next;
+ goto find_type;
+ }
+
+ if (dpos->type.off == 0) /* void */
+ continue;
+find_type:
+ dtype = dwarf_cu__find_type_by_ref(dcu, &dpos->type);
+check_type:
+ if (dtype == NULL) {
+ tag__print_type_not_found(pos);
+ continue;
+ }
+next:
+ pos->type = dtype->small_id;
+ }
+ return 0;
+}
+
+static void type__recode_dwarf_specification(struct tag *tag, struct cu *cu)
+{
+ struct dwarf_tag *dtype;
+ struct type *t = tag__type(tag);
+ dwarf_off_ref specification = dwarf_tag__spec(tag->priv);
+
+ if (t->namespace.name != 0 || specification.off == 0)
+ return;
+
+ dtype = dwarf_cu__find_type_by_ref(cu->priv, &specification);
+ if (dtype != NULL)
+ t->namespace.name = tag__namespace(dtype->tag)->name;
+ else {
+ struct dwarf_tag *dtag = tag->priv;
+
+ fprintf(stderr,
+ "%s: couldn't find name for "
+ "class %#llx, specification=%#llx\n", __func__,
+ (unsigned long long)dtag->id,
+ (unsigned long long)specification.off);
+ }
+}
+
+static void __tag__print_abstract_origin_not_found(struct tag *tag,
+ const char *func)
+{
+ struct dwarf_tag *dtag = tag->priv;
+ fprintf(stderr,
+ "%s: couldn't find %#llx abstract_origin for %#llx (%s)!\n",
+ func, (unsigned long long)dtag->abstract_origin.off,
+ (unsigned long long)dtag->id,
+ dwarf_tag_name(tag->tag));
+}
+
+#define tag__print_abstract_origin_not_found(tag ) \
+ __tag__print_abstract_origin_not_found(tag, __func__)
+
+static void ftype__recode_dwarf_types(struct tag *tag, struct cu *cu)
+{
+ struct parameter *pos;
+ struct dwarf_cu *dcu = cu->priv;
+ struct ftype *type = tag__ftype(tag);
+
+ ftype__for_each_parameter(type, pos) {
+ struct dwarf_tag *dpos = pos->tag.priv;
+ struct dwarf_tag *dtype;
+
+ if (dpos->type.off == 0) {
+ if (dpos->abstract_origin.off == 0) {
+ /* Function without parameters */
+ pos->tag.type = 0;
+ continue;
+ }
+ dtype = dwarf_cu__find_tag_by_ref(dcu, &dpos->abstract_origin);
+ if (dtype == NULL) {
+ tag__print_abstract_origin_not_found(&pos->tag);
+ continue;
+ }
+ pos->name = tag__parameter(dtype->tag)->name;
+ pos->tag.type = dtype->tag->type;
+ continue;
+ }
+
+ dtype = dwarf_cu__find_type_by_ref(dcu, &dpos->type);
+ if (dtype == NULL) {
+ tag__print_type_not_found(&pos->tag);
+ continue;
+ }
+ pos->tag.type = dtype->small_id;
+ }
+}
+
+static void lexblock__recode_dwarf_types(struct lexblock *tag, struct cu *cu)
+{
+ struct tag *pos;
+ struct dwarf_cu *dcu = cu->priv;
+
+ list_for_each_entry(pos, &tag->tags, node) {
+ struct dwarf_tag *dpos = pos->priv;
+ struct dwarf_tag *dtype;
+
+ switch (pos->tag) {
+ case DW_TAG_lexical_block:
+ lexblock__recode_dwarf_types(tag__lexblock(pos), cu);
+ continue;
+ case DW_TAG_inlined_subroutine:
+ dtype = dwarf_cu__find_tag_by_ref(dcu, &dpos->type);
+ if (dtype == NULL) {
+ tag__print_type_not_found(pos);
+ continue;
+ }
+ ftype__recode_dwarf_types(dtype->tag, cu);
+ continue;
+
+ case DW_TAG_formal_parameter:
+ if (dpos->type.off != 0)
+ break;
+
+ struct parameter *fp = tag__parameter(pos);
+ dtype = dwarf_cu__find_tag_by_ref(dcu,
+ &dpos->abstract_origin);
+ if (dtype == NULL) {
+ tag__print_abstract_origin_not_found(pos);
+ continue;
+ }
+ fp->name = tag__parameter(dtype->tag)->name;
+ pos->type = dtype->tag->type;
+ continue;
+
+ case DW_TAG_variable:
+ if (dpos->type.off != 0)
+ break;
+
+ struct variable *var = tag__variable(pos);
+
+ if (dpos->abstract_origin.off == 0) {
+ /*
+ * DW_TAG_variable completely empty was
+ * found on libQtGui.so.4.3.4.debug
+ * <3><d6ea1>: Abbrev Number: 164 (DW_TAG_variable)
+ */
+ continue;
+ }
+
+ dtype = dwarf_cu__find_tag_by_ref(dcu,
+ &dpos->abstract_origin);
+ if (dtype == NULL) {
+ tag__print_abstract_origin_not_found(pos);
+ continue;
+ }
+ var->name = tag__variable(dtype->tag)->name;
+ pos->type = dtype->tag->type;
+ continue;
+
+ case DW_TAG_label: {
+ struct label *l = tag__label(pos);
+
+ if (dpos->abstract_origin.off == 0)
+ continue;
+
+ dtype = dwarf_cu__find_tag_by_ref(dcu, &dpos->abstract_origin);
+ if (dtype != NULL)
+ l->name = tag__label(dtype->tag)->name;
+ else
+ tag__print_abstract_origin_not_found(pos);
+ }
+ continue;
+ }
+
+ dtype = dwarf_cu__find_type_by_ref(dcu, &dpos->type);
+ if (dtype == NULL) {
+ tag__print_type_not_found(pos);
+ continue;
+ }
+ pos->type = dtype->small_id;
+ }
+}
+
+static int tag__recode_dwarf_type(struct tag *tag, struct cu *cu)
+{
+ struct dwarf_tag *dtag = tag->priv;
+ struct dwarf_tag *dtype;
+
+ /* Check if this is an already recoded bitfield */
+ if (dtag == NULL)
+ return 0;
+
+ if (tag__is_type(tag))
+ type__recode_dwarf_specification(tag, cu);
+
+ if (tag__has_namespace(tag))
+ return namespace__recode_dwarf_types(tag, cu);
+
+ switch (tag->tag) {
+ case DW_TAG_subprogram: {
+ struct function *fn = tag__function(tag);
+
+ if (fn->name == 0) {
+ dwarf_off_ref specification = dwarf_tag__spec(dtag);
+ if (dtag->abstract_origin.off == 0 &&
+ specification.off == 0) {
+ /*
+ * Found on libQtGui.so.4.3.4.debug
+ * <3><1423de>: Abbrev Number: 209 (DW_TAG_subprogram)
+ * <1423e0> DW_AT_declaration : 1
+ */
+ return 0;
+ }
+ dtype = dwarf_cu__find_tag_by_ref(cu->priv, &dtag->abstract_origin);
+ if (dtype == NULL)
+ dtype = dwarf_cu__find_tag_by_ref(cu->priv, &specification);
+ if (dtype != NULL)
+ fn->name = tag__function(dtype->tag)->name;
+ else {
+ fprintf(stderr,
+ "%s: couldn't find name for "
+ "function %#llx, abstract_origin=%#llx,"
+ " specification=%#llx\n", __func__,
+ (unsigned long long)dtag->id,
+ (unsigned long long)dtag->abstract_origin.off,
+ (unsigned long long)specification.off);
+ }
+ }
+ lexblock__recode_dwarf_types(&fn->lexblock, cu);
+ }
+ /* Fall thru */
+
+ case DW_TAG_subroutine_type:
+ ftype__recode_dwarf_types(tag, cu);
+ /* Fall thru, for the function return type */
+ break;
+
+ case DW_TAG_lexical_block:
+ lexblock__recode_dwarf_types(tag__lexblock(tag), cu);
+ return 0;
+
+ case DW_TAG_ptr_to_member_type: {
+ struct ptr_to_member_type *pt = tag__ptr_to_member_type(tag);
+
+ dtype = dwarf_cu__find_type_by_ref(cu->priv, &dtag->containing_type);
+ if (dtype != NULL)
+ pt->containing_type = dtype->small_id;
+ else {
+ fprintf(stderr,
+ "%s: couldn't find type for "
+ "containing_type %#llx, containing_type=%#llx\n",
+ __func__,
+ (unsigned long long)dtag->id,
+ (unsigned long long)dtag->containing_type.off);
+ }
+ }
+ break;
+
+ case DW_TAG_namespace:
+ return namespace__recode_dwarf_types(tag, cu);
+ /* Damn, DW_TAG_inlined_subroutine is an special case
+ as dwarf_tag->id is in fact an abtract origin, i.e. must be
+ looked up in the tags_table, not in the types_table.
+ The others also point to routines, so are in tags_table */
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_imported_module:
+ dtype = dwarf_cu__find_tag_by_ref(cu->priv, &dtag->type);
+ goto check_type;
+ /* Can be for both types and non types */
+ case DW_TAG_imported_declaration:
+ dtype = dwarf_cu__find_tag_by_ref(cu->priv, &dtag->type);
+ if (dtype != NULL)
+ goto out;
+ goto find_type;
+ case DW_TAG_variable: {
+ struct variable *var = tag__variable(tag);
+ dwarf_off_ref specification = dwarf_tag__spec(dtag);
+
+ if (specification.off) {
+ dtype = dwarf_cu__find_tag_by_ref(cu->priv, &specification);
+ if (dtype)
+ var->spec = tag__variable(dtype->tag);
+ }
+ }
+
+ }
+
+ if (dtag->type.off == 0) {
+ tag->type = 0; /* void */
+ return 0;
+ }
+
+find_type:
+ dtype = dwarf_cu__find_type_by_ref(cu->priv, &dtag->type);
+check_type:
+ if (dtype == NULL) {
+ tag__print_type_not_found(tag);
+ return 0;
+ }
+out:
+ tag->type = dtype->small_id;
+ return 0;
+}
+
+static int cu__recode_dwarf_types_table(struct cu *cu,
+ struct ptr_table *pt,
+ uint32_t i)
+{
+ for (; i < pt->nr_entries; ++i) {
+ struct tag *tag = pt->entries[i];
+
+ if (tag != NULL) /* void, see cu__new */
+ if (tag__recode_dwarf_type(tag, cu))
+ return -1;
+ }
+ return 0;
+}
+
+static int cu__recode_dwarf_types(struct cu *cu)
+{
+ if (cu__recode_dwarf_types_table(cu, &cu->types_table, 1) ||
+ cu__recode_dwarf_types_table(cu, &cu->tags_table, 0) ||
+ cu__recode_dwarf_types_table(cu, &cu->functions_table, 0))
+ return -1;
+ return 0;
+}
+
+static const char *dwarf_tag__decl_file(const struct tag *tag,
+ const struct cu *cu)
+{
+ struct dwarf_tag *dtag = tag->priv;
+ return cu->extra_dbg_info ?
+ strings__ptr(strings, dtag->decl_file) : NULL;
+}
+
+static uint32_t dwarf_tag__decl_line(const struct tag *tag,
+ const struct cu *cu)
+{
+ struct dwarf_tag *dtag = tag->priv;
+ return cu->extra_dbg_info ? dtag->decl_line : 0;
+}
+
+static unsigned long long dwarf_tag__orig_id(const struct tag *tag,
+ const struct cu *cu)
+{
+ struct dwarf_tag *dtag = tag->priv;
+ return cu->extra_dbg_info ? dtag->id : 0;
+}
+
+static const char *dwarf__strings_ptr(const struct cu *cu __unused,
+ strings_t s)
+{
+ return s ? strings__ptr(strings, s) : NULL;
+}
+
+struct debug_fmt_ops dwarf__ops;
+
+static int die__process(Dwarf_Die *die, struct cu *cu)
+{
+ Dwarf_Die child;
+ const uint16_t tag = dwarf_tag(die);
+
+ if (tag == DW_TAG_partial_unit) {
+ static bool warned;
+
+ if (!warned) {
+ fprintf(stderr, "WARNING: DW_TAG_partial_unit used, some types will not be considered!\n"
+ " Probably this was optimized using a tool like 'dwz'\n"
+ " A future version of pahole will support this.\n");
+ warned = true;
+ }
+ return 0; // so that other units can be processed
+ }
+
+ if (tag != DW_TAG_compile_unit && tag != DW_TAG_type_unit) {
+ fprintf(stderr, "%s: DW_TAG_compile_unit, DW_TAG_type_unit or DW_TAG_partial_unit expected got %s!\n",
+ __FUNCTION__, dwarf_tag_name(tag));
+ return -EINVAL;
+ }
+
+ cu->language = attr_numeric(die, DW_AT_language);
+
+ if (dwarf_child(die, &child) == 0) {
+ int err = die__process_unit(&child, cu);
+ if (err)
+ return err;
+ }
+
+ if (dwarf_siblingof(die, die) == 0)
+ fprintf(stderr, "%s: got %s unexpected tag after "
+ "DW_TAG_compile_unit!\n",
+ __FUNCTION__, dwarf_tag_name(tag));
+
+ return 0;
+}
+
+static int die__process_and_recode(Dwarf_Die *die, struct cu *cu)
+{
+ int ret = die__process(die, cu);
+ if (ret != 0)
+ return ret;
+ return cu__recode_dwarf_types(cu);
+}
+
+static int class_member__cache_byte_size(struct tag *tag, struct cu *cu,
+ void *cookie)
+{
+ struct class_member *member = tag__class_member(tag);
+ struct conf_load *conf_load = cookie;
+
+ if (tag__is_class_member(tag)) {
+ if (member->is_static)
+ return 0;
+ } else if (tag->tag != DW_TAG_inheritance) {
+ return 0;
+ }
+
+ if (member->bitfield_size == 0) {
+ member->byte_size = tag__size(tag, cu);
+ member->bit_size = member->byte_size * 8;
+ return 0;
+ }
+
+ /*
+ * Try to figure out byte size, if it's not directly provided in DWARF
+ */
+ if (member->byte_size == 0) {
+ struct tag *type = tag__strip_typedefs_and_modifiers(&member->tag, cu);
+ member->byte_size = tag__size(type, cu);
+ if (member->byte_size == 0) {
+ int bit_size;
+ if (tag__is_enumeration(type)) {
+ bit_size = tag__type(type)->size;
+ } else {
+ struct base_type *bt = tag__base_type(type);
+ bit_size = bt->bit_size ? bt->bit_size : base_type__name_to_size(bt, cu);
+ }
+ member->byte_size = (bit_size + 7) / 8 * 8;
+ }
+ }
+ member->bit_size = member->byte_size * 8;
+
+ /*
+ * XXX: after all the attemps to determine byte size, we might still
+ * be unsuccessful, because base_type__name_to_size doesn't know about
+ * the base_type name, so one has to add there when such base_type
+ * isn't found. pahole will put zero on the struct output so it should
+ * be easy to spot the name when such unlikely thing happens.
+ */
+ if (member->byte_size == 0) {
+ member->bitfield_offset = 0;
+ return 0;
+ }
+
+ /*
+ * For little-endian architectures, DWARF data emitted by gcc/clang
+ * specifies bitfield offset as an offset from the highest-order bit
+ * of an underlying integral type (e.g., int) to a highest-order bit
+ * of a bitfield. E.g., for bitfield taking first 5 bits of int-backed
+ * bitfield, bit offset will be 27 (sizeof(int) - 0 offset - 5 bit
+ * size), which is very counter-intuitive and isn't a natural
+ * extension of byte offset, which on little-endian points to
+ * lowest-order byte. So here we re-adjust bitfield offset to be an
+ * offset from lowest-order bit of underlying integral type to
+ * a lowest-order bit of a bitfield. This makes bitfield offset
+ * a natural extension of byte offset for bitfields and is uniform
+ * with how big-endian bit offsets work.
+ */
+ if (cu->little_endian) {
+ member->bitfield_offset = member->bit_size - member->bitfield_offset - member->bitfield_size;
+ }
+ member->bit_offset = member->byte_offset * 8 + member->bitfield_offset;
+
+ /* make sure bitfield offset is non-negative */
+ if (member->bitfield_offset < 0) {
+ member->bitfield_offset += member->bit_size;
+ member->byte_offset -= member->byte_size;
+ member->bit_offset = member->byte_offset * 8 + member->bitfield_offset;
+ }
+ /* align on underlying base type natural alignment boundary */
+ member->bitfield_offset += (member->byte_offset % member->byte_size) * 8;
+ member->byte_offset = member->bit_offset / member->bit_size * member->bit_size / 8;
+ if (member->bitfield_offset >= member->bit_size) {
+ member->bitfield_offset -= member->bit_size;
+ member->byte_offset += member->byte_size;
+ }
+
+ if (conf_load && conf_load->fixup_silly_bitfields &&
+ member->byte_size == 8 * member->bitfield_size) {
+ member->bitfield_size = 0;
+ member->bitfield_offset = 0;
+ }
+
+ return 0;
+}
+
+static int finalize_cu(struct cus *cus, struct cu *cu, struct dwarf_cu *dcu,
+ struct conf_load *conf)
+{
+ base_type_name_to_size_table__init(strings);
+ cu__for_all_tags(cu, class_member__cache_byte_size, conf);
+ if (conf && conf->steal) {
+ return conf->steal(cu, conf);
+ }
+ return LSK__KEEPIT;
+}
+
+static int finalize_cu_immediately(struct cus *cus, struct cu *cu,
+ struct dwarf_cu *dcu,
+ struct conf_load *conf)
+{
+ int lsk = finalize_cu(cus, cu, dcu, conf);
+ switch (lsk) {
+ case LSK__DELETE:
+ cu__delete(cu);
+ break;
+ case LSK__STOP_LOADING:
+ break;
+ case LSK__KEEPIT:
+ if (!cu->extra_dbg_info)
+ obstack_free(&dcu->obstack, NULL);
+ cus__add(cus, cu);
+ break;
+ }
+ return lsk;
+}
+
+static int cus__load_debug_types(struct cus *cus, struct conf_load *conf,
+ Dwfl_Module *mod, Dwarf *dw, Elf *elf,
+ const char *filename,
+ const unsigned char *build_id,
+ int build_id_len,
+ struct cu **cup, struct dwarf_cu *dcup)
+{
+ Dwarf_Off off = 0, noff, type_off;
+ size_t cuhl;
+ uint8_t pointer_size, offset_size;
+ uint64_t signature;
+
+ *cup = NULL;
+
+ while (dwarf_next_unit(dw, off, &noff, &cuhl, NULL, NULL, &pointer_size,
+ &offset_size, &signature, &type_off)
+ == 0) {
+
+ if (*cup == NULL) {
+ struct cu *cu;
+
+ cu = cu__new("", pointer_size, build_id,
+ build_id_len, filename);
+ if (cu == NULL) {
+ return DWARF_CB_ABORT;
+ }
+
+ cu->uses_global_strings = true;
+ cu->elf = elf;
+ cu->dwfl = mod;
+ cu->extra_dbg_info = conf ? conf->extra_dbg_info : 0;
+ cu->has_addr_info = conf ? conf->get_addr_info : 0;
+
+ GElf_Ehdr ehdr;
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ return DWARF_CB_ABORT;
+ }
+ cu->little_endian = ehdr.e_ident[EI_DATA] == ELFDATA2LSB;
+
+ dwarf_cu__init(dcup);
+ dcup->cu = cu;
+ /* Funny hack. */
+ dcup->type_unit = dcup;
+ cu->priv = dcup;
+ cu->dfops = &dwarf__ops;
+
+ *cup = cu;
+ }
+
+ Dwarf_Die die_mem;
+ Dwarf_Die *cu_die = dwarf_offdie_types(dw, off + cuhl,
+ &die_mem);
+
+ if (die__process(cu_die, *cup) != 0)
+ return DWARF_CB_ABORT;
+
+ off = noff;
+ }
+
+ if (*cup != NULL && cu__recode_dwarf_types(*cup) != 0)
+ return DWARF_CB_ABORT;
+
+ return 0;
+}
+
+static int cus__load_module(struct cus *cus, struct conf_load *conf,
+ Dwfl_Module *mod, Dwarf *dw, Elf *elf,
+ const char *filename)
+{
+ Dwarf_Off off = 0, noff;
+ size_t cuhl;
+ const unsigned char *build_id = NULL;
+ uint8_t pointer_size, offset_size;
+
+#ifdef HAVE_DWFL_MODULE_BUILD_ID
+ GElf_Addr vaddr;
+ int build_id_len = dwfl_module_build_id(mod, &build_id, &vaddr);
+#else
+ int build_id_len = 0;
+#endif
+
+ struct cu *type_cu;
+ struct dwarf_cu type_dcu;
+ int type_lsk = LSK__KEEPIT;
+
+ int res = cus__load_debug_types(cus, conf, mod, dw, elf, filename,
+ build_id, build_id_len,
+ &type_cu, &type_dcu);
+ if (res != 0) {
+ return res;
+ }
+
+ if (type_cu != NULL) {
+ type_lsk = finalize_cu(cus, type_cu, &type_dcu, conf);
+ if (type_lsk == LSK__KEEPIT) {
+ cus__add(cus, type_cu);
+ }
+ }
+
+ while (dwarf_nextcu(dw, off, &noff, &cuhl, NULL, &pointer_size,
+ &offset_size) == 0) {
+ Dwarf_Die die_mem;
+ Dwarf_Die *cu_die = dwarf_offdie(dw, off + cuhl, &die_mem);
+
+ if (cu_die == NULL)
+ break;
+
+ /*
+ * DW_AT_name in DW_TAG_compile_unit can be NULL, first
+ * seen in:
+ * /usr/libexec/gcc/x86_64-redhat-linux/4.3.2/ecj1.debug
+ */
+ const char *name = attr_string(cu_die, DW_AT_name);
+ struct cu *cu = cu__new(name ?: "", pointer_size,
+ build_id, build_id_len, filename);
+ if (cu == NULL)
+ return DWARF_CB_ABORT;
+ cu->uses_global_strings = true;
+ cu->elf = elf;
+ cu->dwfl = mod;
+ cu->extra_dbg_info = conf ? conf->extra_dbg_info : 0;
+ cu->has_addr_info = conf ? conf->get_addr_info : 0;
+
+ GElf_Ehdr ehdr;
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ return DWARF_CB_ABORT;
+ }
+ cu->little_endian = ehdr.e_ident[EI_DATA] == ELFDATA2LSB;
+
+ struct dwarf_cu dcu;
+
+ dwarf_cu__init(&dcu);
+ dcu.cu = cu;
+ dcu.type_unit = type_cu ? &type_dcu : NULL;
+ cu->priv = &dcu;
+ cu->dfops = &dwarf__ops;
+
+ if (die__process_and_recode(cu_die, cu) != 0)
+ return DWARF_CB_ABORT;
+
+ if (finalize_cu_immediately(cus, cu, &dcu, conf)
+ == LSK__STOP_LOADING)
+ return DWARF_CB_ABORT;
+
+ off = noff;
+ }
+
+ if (type_lsk == LSK__DELETE)
+ cu__delete(type_cu);
+
+ return DWARF_CB_OK;
+}
+
+struct process_dwflmod_parms {
+ struct cus *cus;
+ struct conf_load *conf;
+ const char *filename;
+ uint32_t nr_dwarf_sections_found;
+};
+
+static int cus__process_dwflmod(Dwfl_Module *dwflmod,
+ void **userdata __unused,
+ const char *name __unused,
+ Dwarf_Addr base __unused,
+ void *arg)
+{
+ struct process_dwflmod_parms *parms = arg;
+ struct cus *cus = parms->cus;
+
+ GElf_Addr dwflbias;
+ /*
+ * Does the relocation and saves the elf for later processing
+ * by the stealer, such as pahole_stealer, so that it don't
+ * have to create another Elf instance just to do things like
+ * reading this ELF file symtab to do CTF encoding of the
+ * DW_TAG_suprogram tags (functions).
+ */
+ Elf *elf = dwfl_module_getelf(dwflmod, &dwflbias);
+
+ Dwarf_Addr dwbias;
+ Dwarf *dw = dwfl_module_getdwarf(dwflmod, &dwbias);
+
+ int err = DWARF_CB_OK;
+ if (dw != NULL) {
+ ++parms->nr_dwarf_sections_found;
+ err = cus__load_module(cus, parms->conf, dwflmod, dw, elf,
+ parms->filename);
+ }
+ /*
+ * XXX We will fall back to try finding other debugging
+ * formats (CTF), so no point in telling this to the user
+ * Use for debugging.
+ * else
+ * fprintf(stderr,
+ * "%s: can't get debug context descriptor: %s\n",
+ * __func__, dwfl_errmsg(-1));
+ */
+
+ return err;
+}
+
+static int cus__process_file(struct cus *cus, struct conf_load *conf, int fd,
+ const char *filename)
+{
+ /* Duplicate an fd for dwfl_report_offline to swallow. */
+ int dwfl_fd = dup(fd);
+
+ if (dwfl_fd < 0)
+ return -1;
+
+ /*
+ * Use libdwfl in a trivial way to open the libdw handle for us.
+ * This takes care of applying relocations to DWARF data in ET_REL
+ * files.
+ */
+
+ static const Dwfl_Callbacks callbacks = {
+ .section_address = dwfl_offline_section_address,
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ /* We use this table for core files too. */
+ .find_elf = dwfl_build_id_find_elf,
+ };
+
+ Dwfl *dwfl = dwfl_begin(&callbacks);
+
+ if (dwfl_report_offline(dwfl, filename, filename, dwfl_fd) == NULL)
+ return -1;
+
+ dwfl_report_end(dwfl, NULL, NULL);
+
+ struct process_dwflmod_parms parms = {
+ .cus = cus,
+ .conf = conf,
+ .filename = filename,
+ .nr_dwarf_sections_found = 0,
+ };
+
+ /* Process the one or more modules gleaned from this file. */
+ dwfl_getmodules(dwfl, cus__process_dwflmod, &parms, 0);
+ dwfl_end(dwfl);
+ return parms.nr_dwarf_sections_found ? 0 : -1;
+}
+
+static int dwarf__load_file(struct cus *cus, struct conf_load *conf,
+ const char *filename)
+{
+ int fd, err;
+
+ elf_version(EV_CURRENT);
+
+ fd = open(filename, O_RDONLY);
+
+ if (fd == -1)
+ return -1;
+
+ err = cus__process_file(cus, conf, fd, filename);
+ close(fd);
+
+ return err;
+}
+
+static int dwarf__init(void)
+{
+ strings = strings__new();
+ return strings != NULL ? 0 : -ENOMEM;
+}
+
+static void dwarf__exit(void)
+{
+ strings__delete(strings);
+ strings = NULL;
+}
+
+struct debug_fmt_ops dwarf__ops = {
+ .name = "dwarf",
+ .init = dwarf__init,
+ .exit = dwarf__exit,
+ .load_file = dwarf__load_file,
+ .strings__ptr = dwarf__strings_ptr,
+ .tag__decl_file = dwarf_tag__decl_file,
+ .tag__decl_line = dwarf_tag__decl_line,
+ .tag__orig_id = dwarf_tag__orig_id,
+ .has_alignment_info = true,
+};
diff --git a/dwarves.c b/dwarves.c
new file mode 100644
index 0000000..a2d871a
--- /dev/null
+++ b/dwarves.c
@@ -0,0 +1,2415 @@
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2006 Mandriva Conectiva S.A.
+ Copyright (C) 2006 Arnaldo Carvalho de Melo <acme@mandriva.com>
+ Copyright (C) 2007 Red Hat Inc.
+ Copyright (C) 2007 Arnaldo Carvalho de Melo <acme@redhat.com>
+*/
+
+#include <assert.h>
+#include <dirent.h>
+#include <dwarf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <libelf.h>
+#include <search.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+
+#include "config.h"
+#include "list.h"
+#include "dwarves.h"
+#include "dutil.h"
+#include "pahole_strings.h"
+#include <obstack.h>
+
+#define obstack_chunk_alloc malloc
+#define obstack_chunk_free free
+
+#define min(x, y) ((x) < (y) ? (x) : (y))
+
+int tag__is_base_type(const struct tag *tag, const struct cu *cu)
+{
+ switch (tag->tag) {
+ case DW_TAG_base_type:
+ return 1;
+
+ case DW_TAG_typedef: {
+ const struct tag *type = cu__type(cu, tag->type);
+
+ if (type == NULL)
+ return 0;
+ return tag__is_base_type(type, cu);
+ }
+ }
+ return 0;
+}
+
+bool tag__is_array(const struct tag *tag, const struct cu *cu)
+{
+ switch (tag->tag) {
+ case DW_TAG_array_type:
+ return true;
+
+ case DW_TAG_const_type:
+ case DW_TAG_typedef: {
+ const struct tag *type = cu__type(cu, tag->type);
+
+ if (type == NULL)
+ return 0;
+ return tag__is_array(type, cu);
+ }
+ }
+ return 0;
+}
+
+const char *cu__string(const struct cu *cu, strings_t s)
+{
+ if (cu->dfops && cu->dfops->strings__ptr)
+ return cu->dfops->strings__ptr(cu, s);
+ return NULL;
+}
+
+static inline const char *s(const struct cu *cu, strings_t i)
+{
+ return cu__string(cu, i);
+}
+
+int __tag__has_type_loop(const struct tag *tag, const struct tag *type,
+ char *bf, size_t len, FILE *fp,
+ const char *fn, int line)
+{
+ char bbf[2048], *abf = bbf;
+
+ if (type == NULL)
+ return 0;
+
+ if (tag->type == type->type) {
+ int printed;
+
+ if (bf != NULL)
+ abf = bf;
+ else
+ len = sizeof(bbf);
+ printed = snprintf(abf, len, "<ERROR(%s:%d): detected type loop: type=%d, tag=%s>",
+ fn, line, tag->type, dwarf_tag_name(tag->tag));
+ if (bf == NULL)
+ printed = fprintf(fp ?: stderr, "%s\n", abf);
+ return printed;
+ }
+
+ return 0;
+}
+
+static void lexblock__delete_tags(struct tag *tag, struct cu *cu)
+{
+ struct lexblock *block = tag__lexblock(tag);
+ struct tag *pos, *n;
+
+ list_for_each_entry_safe_reverse(pos, n, &block->tags, node) {
+ list_del_init(&pos->node);
+ tag__delete(pos, cu);
+ }
+}
+
+void lexblock__delete(struct lexblock *block, struct cu *cu)
+{
+ lexblock__delete_tags(&block->ip.tag, cu);
+ obstack_free(&cu->obstack, block);
+}
+
+void tag__delete(struct tag *tag, struct cu *cu)
+{
+ assert(list_empty(&tag->node));
+
+ switch (tag->tag) {
+ case DW_TAG_union_type:
+ type__delete(tag__type(tag), cu); break;
+ case DW_TAG_class_type:
+ case DW_TAG_structure_type:
+ class__delete(tag__class(tag), cu); break;
+ case DW_TAG_enumeration_type:
+ enumeration__delete(tag__type(tag), cu); break;
+ case DW_TAG_subroutine_type:
+ ftype__delete(tag__ftype(tag), cu); break;
+ case DW_TAG_subprogram:
+ function__delete(tag__function(tag), cu); break;
+ case DW_TAG_lexical_block:
+ lexblock__delete(tag__lexblock(tag), cu); break;
+ default:
+ obstack_free(&cu->obstack, tag);
+ }
+}
+
+void tag__not_found_die(const char *file, int line, const char *func)
+{
+ fprintf(stderr, "%s::%s(%d): tag not found, please report to "
+ "acme@kernel.org\n", file, func, line);
+ exit(1);
+}
+
+struct tag *tag__follow_typedef(const struct tag *tag, const struct cu *cu)
+{
+ struct tag *type = cu__type(cu, tag->type);
+
+ if (type != NULL && tag__is_typedef(type))
+ return tag__follow_typedef(type, cu);
+
+ return type;
+}
+
+struct tag *tag__strip_typedefs_and_modifiers(const struct tag *tag, const struct cu *cu)
+{
+ struct tag *type = cu__type(cu, tag->type);
+
+ while (type != NULL && (tag__is_typedef(type) || tag__is_modifier(type)))
+ type = cu__type(cu, type->type);
+
+ return type;
+}
+
+size_t __tag__id_not_found_fprintf(FILE *fp, type_id_t id,
+ const char *fn, int line)
+{
+ return fprintf(fp, "<ERROR(%s:%d): %d not found!>\n", fn, line, id);
+}
+
+static struct base_type_name_to_size {
+ const char *name;
+ strings_t sname;
+ size_t size;
+} base_type_name_to_size_table[] = {
+ { .name = "unsigned", .size = 32, },
+ { .name = "signed int", .size = 32, },
+ { .name = "unsigned int", .size = 32, },
+ { .name = "int", .size = 32, },
+ { .name = "short unsigned int", .size = 16, },
+ { .name = "signed short", .size = 16, },
+ { .name = "unsigned short", .size = 16, },
+ { .name = "short int", .size = 16, },
+ { .name = "short", .size = 16, },
+ { .name = "char", .size = 8, },
+ { .name = "signed char", .size = 8, },
+ { .name = "unsigned char", .size = 8, },
+ { .name = "signed long", .size = 0, },
+ { .name = "long int", .size = 0, },
+ { .name = "long", .size = 0, },
+ { .name = "signed long", .size = 0, },
+ { .name = "unsigned long", .size = 0, },
+ { .name = "long unsigned int", .size = 0, },
+ { .name = "bool", .size = 8, },
+ { .name = "_Bool", .size = 8, },
+ { .name = "long long unsigned int", .size = 64, },
+ { .name = "long long int", .size = 64, },
+ { .name = "long long", .size = 64, },
+ { .name = "signed long long", .size = 64, },
+ { .name = "unsigned long long", .size = 64, },
+ { .name = "double", .size = 64, },
+ { .name = "double double", .size = 64, },
+ { .name = "single float", .size = 32, },
+ { .name = "float", .size = 32, },
+ { .name = "long double", .size = sizeof(long double) * 8, },
+ { .name = "long double long double", .size = sizeof(long double) * 8, },
+ { .name = "__int128", .size = 128, },
+ { .name = "unsigned __int128", .size = 128, },
+ { .name = "__int128 unsigned", .size = 128, },
+ { .name = "_Float128", .size = 128, },
+ { .name = NULL },
+};
+
+void base_type_name_to_size_table__init(struct strings *strings)
+{
+ int i = 0;
+
+ while (base_type_name_to_size_table[i].name != NULL) {
+ if (base_type_name_to_size_table[i].sname == 0)
+ base_type_name_to_size_table[i].sname =
+ strings__find(strings,
+ base_type_name_to_size_table[i].name);
+ ++i;
+ }
+}
+
+size_t base_type__name_to_size(struct base_type *bt, struct cu *cu)
+{
+ int i = 0;
+ char bf[64];
+ const char *name, *orig_name;
+
+ if (bt->name_has_encoding)
+ name = s(cu, bt->name);
+ else
+ name = base_type__name(bt, cu, bf, sizeof(bf));
+ orig_name = name;
+try_again:
+ while (base_type_name_to_size_table[i].name != NULL) {
+ if (bt->name_has_encoding) {
+ if (base_type_name_to_size_table[i].sname == bt->name) {
+ size_t size;
+found:
+ size = base_type_name_to_size_table[i].size;
+
+ return size ?: ((size_t)cu->addr_size * 8);
+ }
+ } else if (strcmp(base_type_name_to_size_table[i].name,
+ name) == 0)
+ goto found;
+ ++i;
+ }
+
+ if (strstarts(name, "signed ")) {
+ i = 0;
+ name += sizeof("signed");
+ goto try_again;
+ }
+
+ fprintf(stderr, "%s: %s %s\n",
+ __func__, dwarf_tag_name(bt->tag.tag), orig_name);
+ return 0;
+}
+
+static const char *base_type_fp_type_str[] = {
+ [BT_FP_SINGLE] = "single",
+ [BT_FP_DOUBLE] = "double",
+ [BT_FP_CMPLX] = "complex",
+ [BT_FP_CMPLX_DBL] = "complex double",
+ [BT_FP_CMPLX_LDBL] = "complex long double",
+ [BT_FP_LDBL] = "long double",
+ [BT_FP_INTVL] = "interval",
+ [BT_FP_INTVL_DBL] = "interval double",
+ [BT_FP_INTVL_LDBL] = "interval long double",
+ [BT_FP_IMGRY] = "imaginary",
+ [BT_FP_IMGRY_DBL] = "imaginary double",
+ [BT_FP_IMGRY_LDBL] = "imaginary long double",
+};
+
+const char *base_type__name(const struct base_type *bt, const struct cu *cu,
+ char *bf, size_t len)
+{
+ if (bt->name_has_encoding)
+ return s(cu, bt->name);
+
+ if (bt->float_type)
+ snprintf(bf, len, "%s %s",
+ base_type_fp_type_str[bt->float_type],
+ s(cu, bt->name));
+ else
+ snprintf(bf, len, "%s%s%s",
+ bt->is_bool ? "bool " : "",
+ bt->is_varargs ? "... " : "",
+ s(cu, bt->name));
+ return bf;
+}
+
+void namespace__delete(struct namespace *space, struct cu *cu)
+{
+ struct tag *pos, *n;
+
+ namespace__for_each_tag_safe_reverse(space, pos, n) {
+ list_del_init(&pos->node);
+
+ /* Look for nested namespaces */
+ if (tag__has_namespace(pos))
+ namespace__delete(tag__namespace(pos), cu);
+ tag__delete(pos, cu);
+ }
+
+ tag__delete(&space->tag, cu);
+}
+
+void __type__init(struct type *type)
+{
+ INIT_LIST_HEAD(&type->node);
+ INIT_LIST_HEAD(&type->type_enum);
+ type->sizeof_member = NULL;
+ type->member_prefix = NULL;
+ type->member_prefix_len = 0;
+}
+
+struct class_member *
+ type__find_first_biggest_size_base_type_member(struct type *type,
+ const struct cu *cu)
+{
+ struct class_member *pos, *result = NULL;
+ size_t result_size = 0;
+
+ type__for_each_data_member(type, pos) {
+ if (pos->is_static)
+ continue;
+
+ struct tag *type = cu__type(cu, pos->tag.type);
+ size_t member_size = 0, power2;
+ struct class_member *inner = NULL;
+
+ if (type == NULL) {
+ tag__id_not_found_fprintf(stderr, pos->tag.type);
+ continue;
+ }
+reevaluate:
+ switch (type->tag) {
+ case DW_TAG_base_type:
+ member_size = base_type__size(type);
+ break;
+ case DW_TAG_pointer_type:
+ case DW_TAG_reference_type:
+ member_size = cu->addr_size;
+ break;
+ case DW_TAG_class_type:
+ case DW_TAG_union_type:
+ case DW_TAG_structure_type:
+ if (tag__type(type)->nr_members == 0)
+ continue;
+ inner = type__find_first_biggest_size_base_type_member(tag__type(type), cu);
+ member_size = inner->byte_size;
+ break;
+ case DW_TAG_array_type:
+ case DW_TAG_const_type:
+ case DW_TAG_typedef:
+ case DW_TAG_rvalue_reference_type:
+ case DW_TAG_volatile_type: {
+ struct tag *tag = cu__type(cu, type->type);
+ if (tag == NULL) {
+ tag__id_not_found_fprintf(stderr, type->type);
+ continue;
+ }
+ type = tag;
+ }
+ goto reevaluate;
+ case DW_TAG_enumeration_type:
+ member_size = tag__type(type)->size / 8;
+ break;
+ }
+
+ /* long long */
+ if (member_size > cu->addr_size)
+ return pos;
+
+ for (power2 = cu->addr_size; power2 > result_size; power2 /= 2)
+ if (member_size >= power2) {
+ if (power2 == cu->addr_size)
+ return inner ?: pos;
+ result_size = power2;
+ result = inner ?: pos;
+ }
+ }
+
+ return result;
+}
+
+static void cu__find_class_holes(struct cu *cu)
+{
+ uint32_t id;
+ struct class *pos;
+
+ cu__for_each_struct(cu, id, pos)
+ class__find_holes(pos);
+}
+
+void cus__add(struct cus *cus, struct cu *cu)
+{
+ cus->nr_entries++;
+ list_add_tail(&cu->node, &cus->cus);
+ cu__find_class_holes(cu);
+}
+
+static void ptr_table__init(struct ptr_table *pt)
+{
+ pt->entries = NULL;
+ pt->nr_entries = pt->allocated_entries = 0;
+}
+
+static void ptr_table__exit(struct ptr_table *pt)
+{
+ free(pt->entries);
+ pt->entries = NULL;
+}
+
+static int ptr_table__add(struct ptr_table *pt, void *ptr, uint32_t *idxp)
+{
+ const uint32_t nr_entries = pt->nr_entries + 1;
+ const uint32_t rc = pt->nr_entries;
+
+ if (nr_entries > pt->allocated_entries) {
+ uint32_t allocated_entries = pt->allocated_entries + 256;
+ void *entries = realloc(pt->entries,
+ sizeof(void *) * allocated_entries);
+ if (entries == NULL)
+ return -ENOMEM;
+
+ pt->allocated_entries = allocated_entries;
+ pt->entries = entries;
+ }
+
+ pt->entries[rc] = ptr;
+ pt->nr_entries = nr_entries;
+ *idxp = rc;
+ return 0;
+}
+
+static int ptr_table__add_with_id(struct ptr_table *pt, void *ptr,
+ uint32_t id)
+{
+ /* Assume we won't be fed with the same id more than once */
+ if (id >= pt->allocated_entries) {
+ uint32_t allocated_entries = roundup(id + 1, 256);
+ void *entries = realloc(pt->entries,
+ sizeof(void *) * allocated_entries);
+ if (entries == NULL)
+ return -ENOMEM;
+
+ /* Zero out the new range */
+ memset(entries + pt->allocated_entries * sizeof(void *), 0,
+ (allocated_entries - pt->allocated_entries) * sizeof(void *));
+
+ pt->allocated_entries = allocated_entries;
+ pt->entries = entries;
+ }
+
+ pt->entries[id] = ptr;
+ if (id >= pt->nr_entries)
+ pt->nr_entries = id + 1;
+ return 0;
+}
+
+static void *ptr_table__entry(const struct ptr_table *pt, uint32_t id)
+{
+ return id >= pt->nr_entries ? NULL : pt->entries[id];
+}
+
+static void cu__insert_function(struct cu *cu, struct tag *tag)
+{
+ struct function *function = tag__function(tag);
+ struct rb_node **p = &cu->functions.rb_node;
+ struct rb_node *parent = NULL;
+ struct function *f;
+
+ while (*p != NULL) {
+ parent = *p;
+ f = rb_entry(parent, struct function, rb_node);
+ if (function->lexblock.ip.addr < f->lexblock.ip.addr)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ rb_link_node(&function->rb_node, parent, p);
+ rb_insert_color(&function->rb_node, &cu->functions);
+}
+
+int cu__table_add_tag(struct cu *cu, struct tag *tag, uint32_t *type_id)
+{
+ struct ptr_table *pt = &cu->tags_table;
+
+ if (tag__is_tag_type(tag))
+ pt = &cu->types_table;
+ else if (tag__is_function(tag)) {
+ pt = &cu->functions_table;
+ cu__insert_function(cu, tag);
+ }
+
+ return ptr_table__add(pt, tag, type_id) ? -ENOMEM : 0;
+}
+
+int cu__table_nullify_type_entry(struct cu *cu, uint32_t id)
+{
+ return ptr_table__add_with_id(&cu->types_table, NULL, id);
+}
+
+int cu__add_tag(struct cu *cu, struct tag *tag, uint32_t *id)
+{
+ int err = cu__table_add_tag(cu, tag, id);
+
+ if (err == 0)
+ list_add_tail(&tag->node, &cu->tags);
+
+ return err;
+}
+
+int cu__table_add_tag_with_id(struct cu *cu, struct tag *tag, uint32_t id)
+{
+ struct ptr_table *pt = &cu->tags_table;
+
+ if (tag__is_tag_type(tag)) {
+ pt = &cu->types_table;
+ } else if (tag__is_function(tag)) {
+ pt = &cu->functions_table;
+ cu__insert_function(cu, tag);
+ }
+
+ return ptr_table__add_with_id(pt, tag, id);
+}
+
+int cu__add_tag_with_id(struct cu *cu, struct tag *tag, uint32_t id)
+{
+ int err = cu__table_add_tag_with_id(cu, tag, id);
+
+ if (err == 0)
+ list_add_tail(&tag->node, &cu->tags);
+
+ return err;
+}
+
+struct cu *cu__new(const char *name, uint8_t addr_size,
+ const unsigned char *build_id, int build_id_len,
+ const char *filename)
+{
+ struct cu *cu = malloc(sizeof(*cu) + build_id_len);
+
+ if (cu != NULL) {
+ uint32_t void_id;
+
+ cu->name = strdup(name);
+ cu->filename = strdup(filename);
+ if (cu->name == NULL || cu->filename == NULL)
+ goto out_free;
+
+ obstack_init(&cu->obstack);
+ ptr_table__init(&cu->tags_table);
+ ptr_table__init(&cu->types_table);
+ ptr_table__init(&cu->functions_table);
+ /*
+ * the first entry is historically associated with void,
+ * so make sure we don't use it
+ */
+ if (ptr_table__add(&cu->types_table, NULL, &void_id) < 0)
+ goto out_free_name;
+
+ cu->functions = RB_ROOT;
+
+ cu->dfops = NULL;
+ INIT_LIST_HEAD(&cu->tags);
+ INIT_LIST_HEAD(&cu->tool_list);
+
+ cu->addr_size = addr_size;
+ cu->extra_dbg_info = 0;
+
+ cu->nr_inline_expansions = 0;
+ cu->size_inline_expansions = 0;
+ cu->nr_structures_changed = 0;
+ cu->nr_functions_changed = 0;
+ cu->max_len_changed_item = 0;
+ cu->function_bytes_added = 0;
+ cu->function_bytes_removed = 0;
+ cu->build_id_len = build_id_len;
+ if (build_id_len > 0)
+ memcpy(cu->build_id, build_id, build_id_len);
+ }
+out:
+ return cu;
+out_free_name:
+ free(cu->name);
+ free(cu->filename);
+out_free:
+ free(cu);
+ cu = NULL;
+ goto out;
+}
+
+void cu__delete(struct cu *cu)
+{
+ ptr_table__exit(&cu->tags_table);
+ ptr_table__exit(&cu->types_table);
+ ptr_table__exit(&cu->functions_table);
+ if (cu->dfops && cu->dfops->cu__delete)
+ cu->dfops->cu__delete(cu);
+ obstack_free(&cu->obstack, NULL);
+ free(cu->filename);
+ free(cu->name);
+ free(cu);
+}
+
+bool cu__same_build_id(const struct cu *cu, const struct cu *other)
+{
+ return cu->build_id_len != 0 &&
+ cu->build_id_len == other->build_id_len &&
+ memcmp(cu->build_id, other->build_id, cu->build_id_len) == 0;
+}
+
+struct tag *cu__function(const struct cu *cu, const uint32_t id)
+{
+ return cu ? ptr_table__entry(&cu->functions_table, id) : NULL;
+}
+
+struct tag *cu__tag(const struct cu *cu, const uint32_t id)
+{
+ return cu ? ptr_table__entry(&cu->tags_table, id) : NULL;
+}
+
+struct tag *cu__type(const struct cu *cu, const type_id_t id)
+{
+ return cu ? ptr_table__entry(&cu->types_table, id) : NULL;
+}
+
+struct tag *cu__find_first_typedef_of_type(const struct cu *cu,
+ const type_id_t type)
+{
+ uint32_t id;
+ struct tag *pos;
+
+ if (cu == NULL || type == 0)
+ return NULL;
+
+ cu__for_each_type(cu, id, pos)
+ if (tag__is_typedef(pos) && pos->type == type)
+ return pos;
+
+ return NULL;
+}
+
+struct tag *cu__find_base_type_by_name(const struct cu *cu,
+ const char *name, type_id_t *idp)
+{
+ uint32_t id;
+ struct tag *pos;
+
+ if (cu == NULL || name == NULL)
+ return NULL;
+
+ cu__for_each_type(cu, id, pos) {
+ if (pos->tag != DW_TAG_base_type)
+ continue;
+
+ const struct base_type *bt = tag__base_type(pos);
+ char bf[64];
+ const char *bname = base_type__name(bt, cu, bf, sizeof(bf));
+ if (!bname || strcmp(bname, name) != 0)
+ continue;
+
+ if (idp != NULL)
+ *idp = id;
+ return pos;
+ }
+
+ return NULL;
+}
+
+struct tag *cu__find_base_type_by_sname_and_size(const struct cu *cu,
+ strings_t sname,
+ uint16_t bit_size,
+ type_id_t *idp)
+{
+ uint32_t id;
+ struct tag *pos;
+
+ if (sname == 0)
+ return NULL;
+
+ cu__for_each_type(cu, id, pos) {
+ if (pos->tag == DW_TAG_base_type) {
+ const struct base_type *bt = tag__base_type(pos);
+
+ if (bt->bit_size == bit_size &&
+ bt->name == sname) {
+ if (idp != NULL)
+ *idp = id;
+ return pos;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+struct tag *cu__find_enumeration_by_sname_and_size(const struct cu *cu,
+ strings_t sname,
+ uint16_t bit_size,
+ type_id_t *idp)
+{
+ uint32_t id;
+ struct tag *pos;
+
+ if (sname == 0)
+ return NULL;
+
+ cu__for_each_type(cu, id, pos) {
+ if (pos->tag == DW_TAG_enumeration_type) {
+ const struct type *t = tag__type(pos);
+
+ if (t->size == bit_size &&
+ t->namespace.name == sname) {
+ if (idp != NULL)
+ *idp = id;
+ return pos;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+struct tag *cu__find_enumeration_by_name(const struct cu *cu, const char *name, type_id_t *idp)
+{
+ uint32_t id;
+ struct tag *pos;
+
+ if (name == NULL)
+ return NULL;
+
+ cu__for_each_type(cu, id, pos) {
+ if (pos->tag == DW_TAG_enumeration_type) {
+ const struct type *type = tag__type(pos);
+ const char *tname = type__name(type, cu);
+
+ if (tname && strcmp(tname, name) == 0) {
+ if (idp != NULL)
+ *idp = id;
+ return pos;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+struct tag *cu__find_struct_by_sname(const struct cu *cu, strings_t sname,
+ const int include_decls, type_id_t *idp)
+{
+ uint32_t id;
+ struct tag *pos;
+
+ if (sname == 0)
+ return NULL;
+
+ cu__for_each_type(cu, id, pos) {
+ struct type *type;
+
+ if (!tag__is_struct(pos))
+ continue;
+
+ type = tag__type(pos);
+ if (type->namespace.name == sname) {
+ if (!type->declaration)
+ goto found;
+
+ if (include_decls)
+ goto found;
+ }
+ }
+
+ return NULL;
+found:
+ if (idp != NULL)
+ *idp = id;
+ return pos;
+
+}
+
+struct tag *cu__find_type_by_name(const struct cu *cu, const char *name, const int include_decls, type_id_t *idp)
+{
+ if (cu == NULL || name == NULL)
+ return NULL;
+
+ uint32_t id;
+ struct tag *pos;
+ cu__for_each_type(cu, id, pos) {
+ struct type *type;
+
+ if (!tag__is_type(pos))
+ continue;
+
+ type = tag__type(pos);
+ const char *tname = type__name(type, cu);
+ if (tname && strcmp(tname, name) == 0) {
+ if (!type->declaration)
+ goto found;
+
+ if (include_decls)
+ goto found;
+ }
+ }
+
+ return NULL;
+found:
+ if (idp != NULL)
+ *idp = id;
+ return pos;
+}
+
+struct tag *cus__find_type_by_name(const struct cus *cus, struct cu **cu, const char *name,
+ const int include_decls, type_id_t *id)
+{
+ struct cu *pos;
+
+ list_for_each_entry(pos, &cus->cus, node) {
+ struct tag *tag = cu__find_type_by_name(pos, name, include_decls, id);
+ if (tag != NULL) {
+ if (cu != NULL)
+ *cu = pos;
+ return tag;
+ }
+ }
+
+ return NULL;
+}
+
+static struct tag *__cu__find_struct_by_name(const struct cu *cu, const char *name,
+ const int include_decls, bool unions, type_id_t *idp)
+{
+ if (cu == NULL || name == NULL)
+ return NULL;
+
+ uint32_t id;
+ struct tag *pos;
+ cu__for_each_type(cu, id, pos) {
+ struct type *type;
+
+ if (!(tag__is_struct(pos) || (unions && tag__is_union(pos))))
+ continue;
+
+ type = tag__type(pos);
+ const char *tname = type__name(type, cu);
+ if (tname && strcmp(tname, name) == 0) {
+ if (!type->declaration)
+ goto found;
+
+ if (include_decls)
+ goto found;
+ }
+ }
+
+ return NULL;
+found:
+ if (idp != NULL)
+ *idp = id;
+ return pos;
+}
+
+struct tag *cu__find_struct_by_name(const struct cu *cu, const char *name,
+ const int include_decls, type_id_t *idp)
+{
+ return __cu__find_struct_by_name(cu, name, include_decls, false, idp);
+}
+
+struct tag *cu__find_struct_or_union_by_name(const struct cu *cu, const char *name,
+ const int include_decls, type_id_t *idp)
+{
+ return __cu__find_struct_by_name(cu, name, include_decls, true, idp);
+}
+
+static struct tag *__cus__find_struct_by_name(const struct cus *cus,
+ struct cu **cu, const char *name,
+ const int include_decls, bool unions, type_id_t *id)
+{
+ struct cu *pos;
+
+ list_for_each_entry(pos, &cus->cus, node) {
+ struct tag *tag = __cu__find_struct_by_name(pos, name, include_decls, unions, id);
+ if (tag != NULL) {
+ if (cu != NULL)
+ *cu = pos;
+ return tag;
+ }
+ }
+
+ return NULL;
+}
+
+struct tag *cus__find_struct_by_name(const struct cus *cus, struct cu **cu, const char *name,
+ const int include_decls, type_id_t *idp)
+{
+ return __cus__find_struct_by_name(cus, cu, name, include_decls, false, idp);
+}
+
+struct tag *cus__find_struct_or_union_by_name(const struct cus *cus, struct cu **cu, const char *name,
+ const int include_decls, type_id_t *idp)
+{
+ return __cus__find_struct_by_name(cus, cu, name, include_decls, true, idp);
+}
+
+struct function *cu__find_function_at_addr(const struct cu *cu,
+ uint64_t addr)
+{
+ struct rb_node *n;
+
+ if (cu == NULL)
+ return NULL;
+
+ n = cu->functions.rb_node;
+
+ while (n) {
+ struct function *f = rb_entry(n, struct function, rb_node);
+
+ if (addr < f->lexblock.ip.addr)
+ n = n->rb_left;
+ else if (addr >= f->lexblock.ip.addr + f->lexblock.size)
+ n = n->rb_right;
+ else
+ return f;
+ }
+
+ return NULL;
+
+}
+
+struct function *cus__find_function_at_addr(const struct cus *cus,
+ uint64_t addr, struct cu **cu)
+{
+ struct cu *pos;
+
+ list_for_each_entry(pos, &cus->cus, node) {
+ struct function *f = cu__find_function_at_addr(pos, addr);
+
+ if (f != NULL) {
+ if (cu != NULL)
+ *cu = pos;
+ return f;
+ }
+ }
+ return NULL;
+}
+
+struct cu *cus__find_cu_by_name(const struct cus *cus, const char *name)
+{
+ struct cu *pos;
+
+ list_for_each_entry(pos, &cus->cus, node)
+ if (pos->name && strcmp(pos->name, name) == 0)
+ return pos;
+
+ return NULL;
+}
+
+struct tag *cu__find_function_by_name(const struct cu *cu, const char *name)
+{
+ if (cu == NULL || name == NULL)
+ return NULL;
+
+ uint32_t id;
+ struct function *pos;
+ cu__for_each_function(cu, id, pos) {
+ const char *fname = function__name(pos, cu);
+ if (fname && strcmp(fname, name) == 0)
+ return function__tag(pos);
+ }
+
+ return NULL;
+}
+
+static size_t array_type__nr_entries(const struct array_type *at)
+{
+ int i;
+ size_t nr_entries = 1;
+
+ for (i = 0; i < at->dimensions; ++i)
+ nr_entries *= at->nr_entries[i];
+
+ return nr_entries;
+}
+
+size_t tag__size(const struct tag *tag, const struct cu *cu)
+{
+ size_t size;
+
+ switch (tag->tag) {
+ case DW_TAG_string_type:
+ return tag__string_type(tag)->nr_entries;
+ case DW_TAG_member: {
+ struct class_member *member = tag__class_member(tag);
+ if (member->is_static)
+ return 0;
+ /* Is it cached already? */
+ size = member->byte_size;
+ if (size != 0)
+ return size;
+ break;
+ }
+ case DW_TAG_pointer_type:
+ case DW_TAG_reference_type: return cu->addr_size;
+ case DW_TAG_base_type: return base_type__size(tag);
+ case DW_TAG_enumeration_type: return tag__type(tag)->size / 8;
+ }
+
+ if (tag->type == 0) { /* struct class: unions, structs */
+ struct type *type = tag__type(tag);
+
+ /* empty base optimization trick */
+ if (type->size == 1 && type->nr_members == 0)
+ size = 0;
+ else
+ size = tag__type(tag)->size;
+ } else {
+ const struct tag *type = cu__type(cu, tag->type);
+
+ if (type == NULL) {
+ tag__id_not_found_fprintf(stderr, tag->type);
+ return -1;
+ } else if (tag__has_type_loop(tag, type, NULL, 0, NULL))
+ return -1;
+ size = tag__size(type, cu);
+ }
+
+ if (tag->tag == DW_TAG_array_type)
+ return size * array_type__nr_entries(tag__array_type(tag));
+
+ return size;
+}
+
+const char *variable__name(const struct variable *var, const struct cu *cu)
+{
+ if (cu->dfops && cu->dfops->variable__name)
+ return cu->dfops->variable__name(var, cu);
+ return s(cu, var->name);
+}
+
+const char *variable__type_name(const struct variable *var,
+ const struct cu *cu,
+ char *bf, size_t len)
+{
+ const struct tag *tag = cu__type(cu, var->ip.tag.type);
+ return tag != NULL ? tag__name(tag, cu, bf, len, NULL) : NULL;
+}
+
+void class_member__delete(struct class_member *member, struct cu *cu)
+{
+ obstack_free(&cu->obstack, member);
+}
+
+static struct class_member *class_member__clone(const struct class_member *from,
+ struct cu *cu)
+{
+ struct class_member *member = obstack_alloc(&cu->obstack, sizeof(*member));
+
+ if (member != NULL)
+ memcpy(member, from, sizeof(*member));
+
+ return member;
+}
+
+static void type__delete_class_members(struct type *type, struct cu *cu)
+{
+ struct class_member *pos, *next;
+
+ type__for_each_tag_safe_reverse(type, pos, next) {
+ list_del_init(&pos->tag.node);
+ class_member__delete(pos, cu);
+ }
+}
+
+void class__delete(struct class *class, struct cu *cu)
+{
+ if (class->type.namespace.sname != NULL)
+ free(class->type.namespace.sname);
+ type__delete_class_members(&class->type, cu);
+ obstack_free(&cu->obstack, class);
+}
+
+void type__delete(struct type *type, struct cu *cu)
+{
+ type__delete_class_members(type, cu);
+ obstack_free(&cu->obstack, type);
+}
+
+static void enumerator__delete(struct enumerator *enumerator, struct cu *cu)
+{
+ obstack_free(&cu->obstack, enumerator);
+}
+
+void enumeration__delete(struct type *type, struct cu *cu)
+{
+ struct enumerator *pos, *n;
+ type__for_each_enumerator_safe_reverse(type, pos, n) {
+ list_del_init(&pos->tag.node);
+ enumerator__delete(pos, cu);
+ }
+}
+
+void class__add_vtable_entry(struct class *class, struct function *vtable_entry)
+{
+ ++class->nr_vtable_entries;
+ list_add_tail(&vtable_entry->vtable_node, &class->vtable);
+}
+
+void namespace__add_tag(struct namespace *space, struct tag *tag)
+{
+ ++space->nr_tags;
+ list_add_tail(&tag->node, &space->tags);
+}
+
+void type__add_member(struct type *type, struct class_member *member)
+{
+ if (member->is_static)
+ ++type->nr_static_members;
+ else
+ ++type->nr_members;
+ namespace__add_tag(&type->namespace, &member->tag);
+}
+
+struct class_member *type__last_member(struct type *type)
+{
+ struct class_member *pos;
+
+ list_for_each_entry_reverse(pos, &type->namespace.tags, tag.node)
+ if (pos->tag.tag == DW_TAG_member)
+ return pos;
+ return NULL;
+}
+
+static int type__clone_members(struct type *type, const struct type *from,
+ struct cu *cu)
+{
+ struct class_member *pos;
+
+ type->nr_members = type->nr_static_members = 0;
+ INIT_LIST_HEAD(&type->namespace.tags);
+
+ type__for_each_member(from, pos) {
+ struct class_member *clone = class_member__clone(pos, cu);
+
+ if (clone == NULL)
+ return -1;
+ type__add_member(type, clone);
+ }
+
+ return 0;
+}
+
+struct class *class__clone(const struct class *from,
+ const char *new_class_name, struct cu *cu)
+{
+ struct class *class = obstack_alloc(&cu->obstack, sizeof(*class));
+
+ if (class != NULL) {
+ memcpy(class, from, sizeof(*class));
+ if (new_class_name != NULL) {
+ class->type.namespace.name = 0;
+ class->type.namespace.sname = strdup(new_class_name);
+ if (class->type.namespace.sname == NULL) {
+ free(class);
+ return NULL;
+ }
+ }
+ if (type__clone_members(&class->type, &from->type, cu) != 0) {
+ class__delete(class, cu);
+ class = NULL;
+ }
+ }
+
+ return class;
+}
+
+void enumeration__add(struct type *type, struct enumerator *enumerator)
+{
+ ++type->nr_members;
+ namespace__add_tag(&type->namespace, &enumerator->tag);
+}
+
+void lexblock__add_lexblock(struct lexblock *block, struct lexblock *child)
+{
+ ++block->nr_lexblocks;
+ list_add_tail(&child->ip.tag.node, &block->tags);
+}
+
+const char *function__name(struct function *func, const struct cu *cu)
+{
+ if (cu->dfops && cu->dfops->function__name)
+ return cu->dfops->function__name(func, cu);
+ return s(cu, func->name);
+}
+
+static void parameter__delete(struct parameter *parm, struct cu *cu)
+{
+ obstack_free(&cu->obstack, parm);
+}
+
+void ftype__delete(struct ftype *type, struct cu *cu)
+{
+ struct parameter *pos, *n;
+
+ if (type == NULL)
+ return;
+
+ ftype__for_each_parameter_safe_reverse(type, pos, n) {
+ list_del_init(&pos->tag.node);
+ parameter__delete(pos, cu);
+ }
+ obstack_free(&cu->obstack, type);
+}
+
+void function__delete(struct function *func, struct cu *cu)
+{
+ lexblock__delete_tags(&func->lexblock.ip.tag, cu);
+ ftype__delete(&func->proto, cu);
+}
+
+int ftype__has_parm_of_type(const struct ftype *ftype, const type_id_t target,
+ const struct cu *cu)
+{
+ struct parameter *pos;
+
+ if (ftype->tag.tag == DW_TAG_subprogram) {
+ struct function *func = (struct function *)ftype;
+
+ if (func->btf)
+ ftype = tag__ftype(cu__type(cu, ftype->tag.type));
+ }
+
+ ftype__for_each_parameter(ftype, pos) {
+ struct tag *type = cu__type(cu, pos->tag.type);
+
+ if (type != NULL && tag__is_pointer(type)) {
+ if (type->type == target)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void ftype__add_parameter(struct ftype *ftype, struct parameter *parm)
+{
+ ++ftype->nr_parms;
+ list_add_tail(&parm->tag.node, &ftype->parms);
+}
+
+void lexblock__add_tag(struct lexblock *block, struct tag *tag)
+{
+ list_add_tail(&tag->node, &block->tags);
+}
+
+void lexblock__add_inline_expansion(struct lexblock *block,
+ struct inline_expansion *exp)
+{
+ ++block->nr_inline_expansions;
+ block->size_inline_expansions += exp->size;
+ lexblock__add_tag(block, &exp->ip.tag);
+}
+
+void lexblock__add_variable(struct lexblock *block, struct variable *var)
+{
+ ++block->nr_variables;
+ lexblock__add_tag(block, &var->ip.tag);
+}
+
+void lexblock__add_label(struct lexblock *block, struct label *label)
+{
+ ++block->nr_labels;
+ lexblock__add_tag(block, &label->ip.tag);
+}
+
+const struct class_member *class__find_bit_hole(const struct class *class,
+ const struct class_member *trailer,
+ const uint16_t bit_hole_size)
+{
+ struct class_member *pos;
+ const size_t byte_hole_size = bit_hole_size / 8;
+
+ type__for_each_data_member(&class->type, pos)
+ if (pos == trailer)
+ break;
+ else if (pos->hole >= byte_hole_size ||
+ pos->bit_hole >= bit_hole_size)
+ return pos;
+
+ return NULL;
+}
+
+void class__find_holes(struct class *class)
+{
+ const struct type *ctype = &class->type;
+ struct class_member *pos, *last = NULL;
+ int cur_bitfield_end = ctype->size * 8, cur_bitfield_size = 0;
+ int bit_holes = 0, byte_holes = 0;
+ int bit_start, bit_end;
+ int last_seen_bit = 0;
+ bool in_bitfield = false;
+
+ if (!tag__is_struct(class__tag(class)))
+ return;
+
+ if (class->holes_searched)
+ return;
+
+ class->nr_holes = 0;
+ class->nr_bit_holes = 0;
+
+ type__for_each_member(ctype, pos) {
+ /* XXX for now just skip these */
+ if (pos->tag.tag == DW_TAG_inheritance &&
+ pos->virtuality == DW_VIRTUALITY_virtual)
+ continue;
+
+ if (pos->is_static)
+ continue;
+
+ pos->bit_hole = 0;
+ pos->hole = 0;
+
+ bit_start = pos->bit_offset;
+ if (pos->bitfield_size) {
+ bit_end = bit_start + pos->bitfield_size;
+ } else {
+ bit_end = bit_start + pos->byte_size * 8;
+ }
+
+ bit_holes = 0;
+ byte_holes = 0;
+ if (in_bitfield) {
+ /* check if we have some trailing bitfield bits left */
+ int bitfield_end = min(bit_start, cur_bitfield_end);
+ bit_holes = bitfield_end - last_seen_bit;
+ last_seen_bit = bitfield_end;
+ }
+ if (pos->bitfield_size) {
+ int aligned_start = pos->byte_offset * 8;
+ /* we can have some alignment byte padding left,
+ * but we need to be careful about bitfield spanning
+ * multiple aligned boundaries */
+ if (last_seen_bit < aligned_start && aligned_start <= bit_start) {
+ byte_holes = pos->byte_offset - last_seen_bit / 8;
+ last_seen_bit = aligned_start;
+ }
+ bit_holes += bit_start - last_seen_bit;
+ } else {
+ byte_holes = bit_start/8 - last_seen_bit/8;
+ }
+ last_seen_bit = bit_end;
+
+ if (pos->bitfield_size) {
+ in_bitfield = true;
+ /* if it's a new bitfield set or same, but with
+ * bigger-sized type, readjust size and end bit */
+ if (bit_end > cur_bitfield_end || pos->bit_size > cur_bitfield_size) {
+ cur_bitfield_size = pos->bit_size;
+ cur_bitfield_end = pos->byte_offset * 8 + cur_bitfield_size;
+ /*
+ * if current bitfield "borrowed" bits from
+ * previous bitfield, it will have byte_offset
+ * of previous bitfield's backing integral
+ * type, but its end bit will be in a new
+ * bitfield "area", so we need to adjust
+ * bitfield end appropriately
+ */
+ if (bit_end > cur_bitfield_end) {
+ cur_bitfield_end += cur_bitfield_size;
+ }
+ }
+ } else {
+ in_bitfield = false;
+ cur_bitfield_size = 0;
+ cur_bitfield_end = bit_end;
+ }
+
+ if (last) {
+ last->hole = byte_holes;
+ last->bit_hole = bit_holes;
+ } else {
+ class->pre_hole = byte_holes;
+ class->pre_bit_hole = bit_holes;
+ }
+ if (bit_holes)
+ class->nr_bit_holes++;
+ if (byte_holes)
+ class->nr_holes++;
+
+ last = pos;
+ }
+
+ if (in_bitfield) {
+ int bitfield_end = min(ctype->size * 8, cur_bitfield_end);
+ class->bit_padding = bitfield_end - last_seen_bit;
+ last_seen_bit = bitfield_end;
+ } else {
+ class->bit_padding = 0;
+ }
+ class->padding = ctype->size - last_seen_bit / 8;
+
+ class->holes_searched = true;
+}
+
+static size_t type__natural_alignment(struct type *type, const struct cu *cu);
+
+static size_t tag__natural_alignment(struct tag *tag, const struct cu *cu)
+{
+ size_t natural_alignment = 1;
+
+ if (tag == NULL) // Maybe its a non supported type, like DW_TAG_subrange_type, ADA stuff
+ return natural_alignment;
+
+ if (tag__is_pointer(tag)) {
+ natural_alignment = cu->addr_size;
+ } else if (tag->tag == DW_TAG_base_type) {
+ natural_alignment = base_type__size(tag);
+ } else if (tag__is_enumeration(tag)) {
+ natural_alignment = tag__type(tag)->size / 8;
+ } else if (tag__is_struct(tag) || tag__is_union(tag)) {
+ natural_alignment = type__natural_alignment(tag__type(tag), cu);
+ } else if (tag->tag == DW_TAG_array_type) {
+ tag = tag__strip_typedefs_and_modifiers(tag, cu);
+ if (tag != NULL) // Maybe its a non supported type, like DW_TAG_subrange_type, ADA stuff
+ natural_alignment = tag__natural_alignment(tag, cu);
+ }
+
+ /*
+ * Cope with zero sized types, like:
+ *
+ * struct u64_stats_sync {
+ * #if BITS_PER_LONG==32 && defined(CONFIG_SMP)
+ * seqcount_t seq;
+ * #endif
+ * };
+ *
+ */
+ return natural_alignment ?: 1;
+}
+
+static size_t type__natural_alignment(struct type *type, const struct cu *cu)
+{
+ struct class_member *member;
+
+ if (type->natural_alignment != 0)
+ return type->natural_alignment;
+
+ type__for_each_member(type, member) {
+ /* XXX for now just skip these */
+ if (member->tag.tag == DW_TAG_inheritance &&
+ member->virtuality == DW_VIRTUALITY_virtual)
+ continue;
+ if (member->is_static) continue;
+
+ struct tag *member_type = tag__strip_typedefs_and_modifiers(&member->tag, cu);
+
+ if (member_type == NULL) // Maybe its a DW_TAG_subrange_type, ADA stuff still not supported
+ continue;
+
+ size_t member_natural_alignment = tag__natural_alignment(member_type, cu);
+
+ if (type->natural_alignment < member_natural_alignment)
+ type->natural_alignment = member_natural_alignment;
+ }
+
+ return type->natural_alignment;
+}
+
+/*
+ * Sometimes the only indication that a struct is __packed__ is for it to
+ * appear embedded in another and at an offset that is not natural for it,
+ * so, in !__packed__ parked struct, check for that and mark the types of
+ * members at unnatural alignments.
+ */
+void type__check_structs_at_unnatural_alignments(struct type *type, const struct cu *cu)
+{
+ struct class_member *member;
+
+ type__for_each_member(type, member) {
+ struct tag *member_type = tag__strip_typedefs_and_modifiers(&member->tag, cu);
+
+ if (member_type == NULL) {
+ // just be conservative and ignore
+ // Found first when a FORTRAN95 DWARF file was processed
+ // and the DW_TAG_string_type wasn't yet supported
+ continue;
+ }
+
+ if (!tag__is_struct(member_type))
+ continue;
+
+ size_t natural_alignment = tag__natural_alignment(member_type, cu);
+
+ /* Would this break the natural alignment */
+ if ((member->byte_offset % natural_alignment) != 0) {
+ struct class *cls = tag__class(member_type);
+
+ cls->is_packed = true;
+ cls->type.packed_attributes_inferred = true;
+ }
+ }
+}
+
+bool class__infer_packed_attributes(struct class *cls, const struct cu *cu)
+{
+ struct type *ctype = &cls->type;
+ struct class_member *pos;
+ uint16_t max_natural_alignment = 1;
+
+ if (!tag__is_struct(class__tag(cls)))
+ return false;
+
+ if (ctype->packed_attributes_inferred)
+ return cls->is_packed;
+
+ class__find_holes(cls);
+
+ if (cls->padding != 0 || cls->nr_holes != 0) {
+ type__check_structs_at_unnatural_alignments(ctype, cu);
+ cls->is_packed = false;
+ goto out;
+ }
+
+ type__for_each_member(ctype, pos) {
+ /* XXX for now just skip these */
+ if (pos->tag.tag == DW_TAG_inheritance &&
+ pos->virtuality == DW_VIRTUALITY_virtual)
+ continue;
+
+ if (pos->is_static)
+ continue;
+
+ struct tag *member_type = tag__strip_typedefs_and_modifiers(&pos->tag, cu);
+ size_t natural_alignment = tag__natural_alignment(member_type, cu);
+
+ /* Always aligned: */
+ if (natural_alignment == sizeof(char))
+ continue;
+
+ if (max_natural_alignment < natural_alignment)
+ max_natural_alignment = natural_alignment;
+
+ if ((pos->byte_offset % natural_alignment) == 0)
+ continue;
+
+ cls->is_packed = true;
+ goto out;
+ }
+
+ if ((max_natural_alignment != 1 && ctype->alignment == 1) ||
+ (class__size(cls) % max_natural_alignment) != 0)
+ cls->is_packed = true;
+
+out:
+ ctype->packed_attributes_inferred = true;
+
+ return cls->is_packed;
+}
+
+/*
+ * If structs embedded in unions, nameless or not, have a size which isn't
+ * isn't a multiple of the union size, then it must be packed, even if
+ * it has no holes nor padding, as an array of such unions would have the
+ * natural alignments of non-multiple structs inside it broken.
+ */
+void union__infer_packed_attributes(struct type *type, const struct cu *cu)
+{
+ const uint32_t union_size = type->size;
+ struct class_member *member;
+
+ if (type->packed_attributes_inferred)
+ return;
+
+ type__for_each_member(type, member) {
+ struct tag *member_type = tag__strip_typedefs_and_modifiers(&member->tag, cu);
+
+ if (!tag__is_struct(member_type))
+ continue;
+
+ size_t natural_alignment = tag__natural_alignment(member_type, cu);
+
+ /* Would this break the natural alignment */
+ if ((union_size % natural_alignment) != 0) {
+ struct class *cls = tag__class(member_type);
+
+ cls->is_packed = true;
+ cls->type.packed_attributes_inferred = true;
+ }
+ }
+
+ type->packed_attributes_inferred = true;
+}
+
+/** class__has_hole_ge - check if class has a hole greater or equal to @size
+ * @class - class instance
+ * @size - hole size to check
+ */
+int class__has_hole_ge(const struct class *class, const uint16_t size)
+{
+ struct class_member *pos;
+
+ if (class->nr_holes == 0)
+ return 0;
+
+ type__for_each_data_member(&class->type, pos)
+ if (pos->hole >= size)
+ return 1;
+
+ return 0;
+}
+
+struct class_member *type__find_member_by_name(const struct type *type,
+ const struct cu *cu,
+ const char *name)
+{
+ if (name == NULL)
+ return NULL;
+
+ struct class_member *pos;
+ type__for_each_data_member(type, pos) {
+ const char *curr_name = class_member__name(pos, cu);
+ if (curr_name && strcmp(curr_name, name) == 0)
+ return pos;
+ }
+
+ return NULL;
+}
+
+static int strcommon(const char *a, const char *b)
+{
+ int i = 0;
+
+ while (*a != '\0' && *a == *b) {
+ ++a;
+ ++b;
+ ++i;
+ }
+
+ return i;
+}
+
+void enumeration__calc_prefix(struct type *enumeration, const struct cu *cu)
+{
+ if (enumeration->member_prefix)
+ return;
+
+ const char *previous_name = NULL, *curr_name = "";
+ int common_part = INT32_MAX;
+ struct enumerator *entry;
+
+ type__for_each_enumerator(enumeration, entry) {
+ const char *curr_name = enumerator__name(entry, cu);
+
+ if (previous_name) {
+ int curr_common_part = strcommon(curr_name, previous_name);
+ if (common_part > curr_common_part)
+ common_part = curr_common_part;
+
+ }
+
+ previous_name = curr_name;
+ }
+
+ enumeration->member_prefix = strndup(curr_name, common_part);
+ enumeration->member_prefix_len = common_part == INT32_MAX ? 0 : common_part;
+}
+
+void enumerations__calc_prefix(struct list_head *enumerations)
+{
+ struct tag_cu_node *pos;
+
+ list_for_each_entry(pos, enumerations, node)
+ enumeration__calc_prefix(tag__type(pos->tc.tag), pos->tc.cu);
+}
+
+const char *enumeration__prefix(struct type *enumeration, const struct cu *cu)
+{
+ if (!enumeration->member_prefix)
+ enumeration__calc_prefix(enumeration, cu);
+
+ return enumeration->member_prefix;
+}
+
+uint16_t enumeration__prefix_len(struct type *enumeration, const struct cu *cu)
+{
+ if (!enumeration->member_prefix)
+ enumeration__calc_prefix(enumeration, cu);
+
+ return enumeration->member_prefix_len;
+}
+
+
+uint32_t type__nr_members_of_type(const struct type *type, const type_id_t type_id)
+{
+ struct class_member *pos;
+ uint32_t nr_members_of_type = 0;
+
+ type__for_each_member(type, pos)
+ if (pos->tag.type == type_id)
+ ++nr_members_of_type;
+
+ return nr_members_of_type;
+}
+
+static void lexblock__account_inline_expansions(struct lexblock *block,
+ const struct cu *cu)
+{
+ struct tag *pos, *type;
+
+ if (block->nr_inline_expansions == 0)
+ return;
+
+ list_for_each_entry(pos, &block->tags, node) {
+ if (pos->tag == DW_TAG_lexical_block) {
+ lexblock__account_inline_expansions(tag__lexblock(pos),
+ cu);
+ continue;
+ } else if (pos->tag != DW_TAG_inlined_subroutine)
+ continue;
+
+ type = cu__function(cu, pos->type);
+ if (type != NULL) {
+ struct function *ftype = tag__function(type);
+
+ ftype->cu_total_nr_inline_expansions++;
+ ftype->cu_total_size_inline_expansions +=
+ tag__inline_expansion(pos)->size;
+ }
+
+ }
+}
+
+void cu__account_inline_expansions(struct cu *cu)
+{
+ struct tag *pos;
+ struct function *fpos;
+
+ list_for_each_entry(pos, &cu->tags, node) {
+ if (!tag__is_function(pos))
+ continue;
+ fpos = tag__function(pos);
+ lexblock__account_inline_expansions(&fpos->lexblock, cu);
+ cu->nr_inline_expansions += fpos->lexblock.nr_inline_expansions;
+ cu->size_inline_expansions += fpos->lexblock.size_inline_expansions;
+ }
+}
+
+static int list__for_all_tags(struct list_head *list, struct cu *cu,
+ int (*iterator)(struct tag *tag,
+ struct cu *cu, void *cookie),
+ void *cookie)
+{
+ struct tag *pos, *n;
+
+ if (list_empty(list) || !list->next)
+ return 0;
+
+ list_for_each_entry_safe_reverse(pos, n, list, node) {
+ if (tag__has_namespace(pos)) {
+ struct namespace *space = tag__namespace(pos);
+
+ /*
+ * See comment in type__for_each_enumerator, the
+ * enumerators (enum entries) are shared, but the
+ * enumeration tag must be deleted.
+ */
+ if (!space->shared_tags &&
+ list__for_all_tags(&space->tags, cu,
+ iterator, cookie))
+ return 1;
+ /*
+ * vtable functions are already in the class tags list
+ */
+ } else if (tag__is_function(pos)) {
+ if (list__for_all_tags(&tag__ftype(pos)->parms,
+ cu, iterator, cookie))
+ return 1;
+ if (list__for_all_tags(&tag__function(pos)->lexblock.tags,
+ cu, iterator, cookie))
+ return 1;
+ } else if (pos->tag == DW_TAG_subroutine_type) {
+ if (list__for_all_tags(&tag__ftype(pos)->parms,
+ cu, iterator, cookie))
+ return 1;
+ } else if (pos->tag == DW_TAG_lexical_block) {
+ if (list__for_all_tags(&tag__lexblock(pos)->tags,
+ cu, iterator, cookie))
+ return 1;
+ }
+
+ if (iterator(pos, cu, cookie))
+ return 1;
+ }
+ return 0;
+}
+
+int cu__for_all_tags(struct cu *cu,
+ int (*iterator)(struct tag *tag,
+ struct cu *cu, void *cookie),
+ void *cookie)
+{
+ return list__for_all_tags(&cu->tags, cu, iterator, cookie);
+}
+
+void cus__for_each_cu(struct cus *cus,
+ int (*iterator)(struct cu *cu, void *cookie),
+ void *cookie,
+ struct cu *(*filter)(struct cu *cu))
+{
+ struct cu *pos;
+
+ list_for_each_entry(pos, &cus->cus, node) {
+ struct cu *cu = pos;
+ if (filter != NULL) {
+ cu = filter(pos);
+ if (cu == NULL)
+ continue;
+ }
+ if (iterator(cu, cookie))
+ break;
+ }
+}
+
+int cus__load_dir(struct cus *cus, struct conf_load *conf,
+ const char *dirname, const char *filename_mask,
+ const int recursive)
+{
+ struct dirent *entry;
+ int err = -1;
+ DIR *dir = opendir(dirname);
+
+ if (dir == NULL)
+ goto out;
+
+ err = 0;
+ while ((entry = readdir(dir)) != NULL) {
+ char pathname[PATH_MAX];
+ struct stat st;
+
+ if (strcmp(entry->d_name, ".") == 0 ||
+ strcmp(entry->d_name, "..") == 0)
+ continue;
+
+ snprintf(pathname, sizeof(pathname), "%.*s/%s",
+ (int)(sizeof(pathname) - sizeof(entry->d_name) - 1), dirname, entry->d_name);
+
+ err = lstat(pathname, &st);
+ if (err != 0)
+ break;
+
+ if (S_ISDIR(st.st_mode)) {
+ if (!recursive)
+ continue;
+
+ err = cus__load_dir(cus, conf, pathname,
+ filename_mask, recursive);
+ if (err != 0)
+ break;
+ } else if (fnmatch(filename_mask, entry->d_name, 0) == 0) {
+ err = cus__load_file(cus, conf, pathname);
+ if (err != 0)
+ break;
+ }
+ }
+
+ if (err == -1)
+ puts(dirname);
+ closedir(dir);
+out:
+ return err;
+}
+
+/*
+ * This should really do demand loading of DSOs, STABS anyone? 8-)
+ */
+extern struct debug_fmt_ops dwarf__ops, ctf__ops, btf_elf__ops;
+
+static struct debug_fmt_ops *debug_fmt_table[] = {
+ &dwarf__ops,
+ &btf_elf__ops,
+ &ctf__ops,
+ NULL,
+};
+
+struct debug_fmt_ops *dwarves__active_loader;
+
+static int debugging_formats__loader(const char *name)
+{
+ int i = 0;
+ while (debug_fmt_table[i] != NULL) {
+ if (strcmp(debug_fmt_table[i]->name, name) == 0)
+ return i;
+ ++i;
+ }
+ return -1;
+}
+
+int cus__load_file(struct cus *cus, struct conf_load *conf,
+ const char *filename)
+{
+ int i = 0, err = 0;
+ int loader;
+
+ if (conf && conf->format_path != NULL) {
+ char *fpath = strdup(conf->format_path);
+ if (fpath == NULL)
+ return -ENOMEM;
+ char *fp = fpath;
+ while (1) {
+ char *sep = strchr(fp, ',');
+
+ if (sep != NULL)
+ *sep = '\0';
+
+ err = -ENOTSUP;
+ loader = debugging_formats__loader(fp);
+ if (loader == -1)
+ break;
+
+ if (conf->conf_fprintf)
+ conf->conf_fprintf->has_alignment_info = debug_fmt_table[loader]->has_alignment_info;
+
+ err = 0;
+ dwarves__active_loader = debug_fmt_table[loader];
+ if (debug_fmt_table[loader]->load_file(cus, conf,
+ filename) == 0)
+ break;
+
+ err = -EINVAL;
+ if (sep == NULL)
+ break;
+
+ fp = sep + 1;
+ }
+ free(fpath);
+ dwarves__active_loader = NULL;
+ return err;
+ }
+
+ while (debug_fmt_table[i] != NULL) {
+ if (conf && conf->conf_fprintf)
+ conf->conf_fprintf->has_alignment_info = debug_fmt_table[i]->has_alignment_info;
+ dwarves__active_loader = debug_fmt_table[i];
+ if (debug_fmt_table[i]->load_file(cus, conf, filename) == 0)
+ return 0;
+ ++i;
+ }
+
+ dwarves__active_loader = NULL;
+ return -EINVAL;
+}
+
+#define BUILD_ID_SIZE 20
+#define SBUILD_ID_SIZE (BUILD_ID_SIZE * 2 + 1)
+
+#define NOTE_ALIGN(sz) (((sz) + 3) & ~3)
+
+#define NT_GNU_BUILD_ID 3
+
+#ifndef min
+#define min(x, y) ({ \
+ typeof(x) _min1 = (x); \
+ typeof(y) _min2 = (y); \
+ (void) (&_min1 == &_min2); \
+ _min1 < _min2 ? _min1 : _min2; })
+#endif
+
+/* Force a compilation error if condition is true, but also produce a
+ result (of value 0 and type size_t), so the expression can be used
+ e.g. in a structure initializer (or where-ever else comma expressions
+ aren't permitted). */
+#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
+
+/* Are two types/vars the same type (ignoring qualifiers)? */
+#ifndef __same_type
+# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
+#endif
+
+/* &a[0] degrades to a pointer: a different type from an array */
+#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
+
+static int sysfs__read_build_id(const char *filename, void *build_id, size_t size)
+{
+ int fd, err = -1;
+
+ if (size < BUILD_ID_SIZE)
+ goto out;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ while (1) {
+ char bf[BUFSIZ];
+ GElf_Nhdr nhdr;
+ size_t namesz, descsz;
+
+ if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr))
+ break;
+
+ namesz = NOTE_ALIGN(nhdr.n_namesz);
+ descsz = NOTE_ALIGN(nhdr.n_descsz);
+ if (nhdr.n_type == NT_GNU_BUILD_ID &&
+ nhdr.n_namesz == sizeof("GNU")) {
+ if (read(fd, bf, namesz) != (ssize_t)namesz)
+ break;
+ if (memcmp(bf, "GNU", sizeof("GNU")) == 0) {
+ size_t sz = min(descsz, size);
+ if (read(fd, build_id, sz) == (ssize_t)sz) {
+ memset(build_id + sz, 0, size - sz);
+ err = 0;
+ break;
+ }
+ } else if (read(fd, bf, descsz) != (ssize_t)descsz)
+ break;
+ } else {
+ int n = namesz + descsz;
+
+ if (n > (int)sizeof(bf)) {
+ n = sizeof(bf);
+ fprintf(stderr, "%s: truncating reading of build id in sysfs file %s: n_namesz=%u, n_descsz=%u.\n",
+ __func__, filename, nhdr.n_namesz, nhdr.n_descsz);
+ }
+ if (read(fd, bf, n) != n)
+ break;
+ }
+ }
+ close(fd);
+out:
+ return err;
+}
+
+static int elf_read_build_id(Elf *elf, void *bf, size_t size)
+{
+ int err = -1;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ Elf_Data *data;
+ Elf_Scn *sec;
+ Elf_Kind ek;
+ void *ptr;
+
+ if (size < BUILD_ID_SIZE)
+ goto out;
+
+ ek = elf_kind(elf);
+ if (ek != ELF_K_ELF)
+ goto out;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ fprintf(stderr, "%s: cannot get elf header.\n", __func__);
+ goto out;
+ }
+
+ /*
+ * Check following sections for notes:
+ * '.note.gnu.build-id'
+ * '.notes'
+ * '.note' (VDSO specific)
+ */
+ do {
+ sec = elf_section_by_name(elf, &ehdr, &shdr,
+ ".note.gnu.build-id", NULL);
+ if (sec)
+ break;
+
+ sec = elf_section_by_name(elf, &ehdr, &shdr,
+ ".notes", NULL);
+ if (sec)
+ break;
+
+ sec = elf_section_by_name(elf, &ehdr, &shdr,
+ ".note", NULL);
+ if (sec)
+ break;
+
+ return err;
+
+ } while (0);
+
+ data = elf_getdata(sec, NULL);
+ if (data == NULL)
+ goto out;
+
+ ptr = data->d_buf;
+ while (ptr < (data->d_buf + data->d_size)) {
+ GElf_Nhdr *nhdr = ptr;
+ size_t namesz = NOTE_ALIGN(nhdr->n_namesz),
+ descsz = NOTE_ALIGN(nhdr->n_descsz);
+ const char *name;
+
+ ptr += sizeof(*nhdr);
+ name = ptr;
+ ptr += namesz;
+ if (nhdr->n_type == NT_GNU_BUILD_ID &&
+ nhdr->n_namesz == sizeof("GNU")) {
+ if (memcmp(name, "GNU", sizeof("GNU")) == 0) {
+ size_t sz = min(size, descsz);
+ memcpy(bf, ptr, sz);
+ memset(bf + sz, 0, size - sz);
+ err = descsz;
+ break;
+ }
+ }
+ ptr += descsz;
+ }
+
+out:
+ return err;
+}
+
+static int filename__read_build_id(const char *filename, void *bf, size_t size)
+{
+ int fd, err = -1;
+ Elf *elf;
+
+ if (size < BUILD_ID_SIZE)
+ goto out;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ elf = elf_begin(fd, ELF_C_READ, NULL);
+ if (elf == NULL) {
+ fprintf(stderr, "%s: cannot read %s ELF file.\n", __func__, filename);
+ goto out_close;
+ }
+
+ err = elf_read_build_id(elf, bf, size);
+
+ elf_end(elf);
+out_close:
+ close(fd);
+out:
+ return err;
+}
+
+static int build_id__sprintf(const unsigned char *build_id, int len, char *bf)
+{
+ char *bid = bf;
+ const unsigned char *raw = build_id;
+ int i;
+
+ for (i = 0; i < len; ++i) {
+ sprintf(bid, "%02x", *raw);
+ ++raw;
+ bid += 2;
+ }
+
+ return (bid - bf) + 1;
+}
+
+static int sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id)
+{
+ char notes[PATH_MAX];
+ unsigned char build_id[BUILD_ID_SIZE];
+ int ret;
+
+ if (!root_dir)
+ root_dir = "";
+
+ snprintf(notes, sizeof(notes), "%s/sys/kernel/notes", root_dir);
+
+ ret = sysfs__read_build_id(notes, build_id, sizeof(build_id));
+ if (ret < 0)
+ return ret;
+
+ return build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
+}
+
+static int filename__sprintf_build_id(const char *pathname, char *sbuild_id)
+{
+ unsigned char build_id[BUILD_ID_SIZE];
+ int ret;
+
+ ret = filename__read_build_id(pathname, build_id, sizeof(build_id));
+ if (ret < 0)
+ return ret;
+ else if (ret != sizeof(build_id))
+ return -EINVAL;
+
+ return build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
+}
+
+#define zfree(ptr) ({ free(*ptr); *ptr = NULL; })
+
+static int vmlinux_path__nr_entries;
+static char **vmlinux_path;
+
+static void vmlinux_path__exit(void)
+{
+ while (--vmlinux_path__nr_entries >= 0)
+ zfree(&vmlinux_path[vmlinux_path__nr_entries]);
+ vmlinux_path__nr_entries = 0;
+
+ zfree(&vmlinux_path);
+}
+
+static const char * const vmlinux_paths[] = {
+ "vmlinux",
+ "/boot/vmlinux"
+};
+
+static const char * const vmlinux_paths_upd[] = {
+ "/boot/vmlinux-%s",
+ "/usr/lib/debug/boot/vmlinux-%s",
+ "/lib/modules/%s/build/vmlinux",
+ "/usr/lib/debug/lib/modules/%s/vmlinux",
+ "/usr/lib/debug/boot/vmlinux-%s.debug"
+};
+
+static int vmlinux_path__add(const char *new_entry)
+{
+ vmlinux_path[vmlinux_path__nr_entries] = strdup(new_entry);
+ if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+ return -1;
+ ++vmlinux_path__nr_entries;
+
+ return 0;
+}
+
+static int vmlinux_path__init(void)
+{
+ struct utsname uts;
+ char bf[PATH_MAX];
+ char *kernel_version;
+ unsigned int i;
+
+ vmlinux_path = malloc(sizeof(char *) * (ARRAY_SIZE(vmlinux_paths) +
+ ARRAY_SIZE(vmlinux_paths_upd)));
+ if (vmlinux_path == NULL)
+ return -1;
+
+ for (i = 0; i < ARRAY_SIZE(vmlinux_paths); i++)
+ if (vmlinux_path__add(vmlinux_paths[i]) < 0)
+ goto out_fail;
+
+ if (uname(&uts) < 0)
+ goto out_fail;
+
+ kernel_version = uts.release;
+
+ for (i = 0; i < ARRAY_SIZE(vmlinux_paths_upd); i++) {
+ snprintf(bf, sizeof(bf), vmlinux_paths_upd[i], kernel_version);
+ if (vmlinux_path__add(bf) < 0)
+ goto out_fail;
+ }
+
+ return 0;
+
+out_fail:
+ vmlinux_path__exit();
+ return -1;
+}
+
+static int cus__load_running_kernel(struct cus *cus, struct conf_load *conf)
+{
+ int i, err = 0;
+ char running_sbuild_id[SBUILD_ID_SIZE];
+
+ if ((!conf || conf->format_path == NULL || strncmp(conf->format_path, "btf", 3) == 0) &&
+ access("/sys/kernel/btf/vmlinux", R_OK) == 0) {
+ int loader = debugging_formats__loader("btf");
+ if (loader == -1)
+ goto try_elf;
+
+ if (conf && conf->conf_fprintf)
+ conf->conf_fprintf->has_alignment_info = debug_fmt_table[loader]->has_alignment_info;
+
+ dwarves__active_loader = debug_fmt_table[loader];
+ if (debug_fmt_table[loader]->load_file(cus, conf, "/sys/kernel/btf/vmlinux") == 0)
+ return 0;
+ dwarves__active_loader = NULL;
+ }
+try_elf:
+ elf_version(EV_CURRENT);
+ vmlinux_path__init();
+
+ sysfs__sprintf_build_id(NULL, running_sbuild_id);
+
+ for (i = 0; i < vmlinux_path__nr_entries; ++i) {
+ char sbuild_id[SBUILD_ID_SIZE];
+
+ if (filename__sprintf_build_id(vmlinux_path[i], sbuild_id) > 0 &&
+ strcmp(sbuild_id, running_sbuild_id) == 0) {
+ err = cus__load_file(cus, conf, vmlinux_path[i]);
+ break;
+ }
+ }
+
+ vmlinux_path__exit();
+
+ return err;
+}
+
+int cus__load_files(struct cus *cus, struct conf_load *conf,
+ char *filenames[])
+{
+ int i = 0;
+
+ while (filenames[i] != NULL) {
+ if (cus__load_file(cus, conf, filenames[i]))
+ return -++i;
+ ++i;
+ }
+
+ return i ? 0 : cus__load_running_kernel(cus, conf);
+}
+
+int cus__fprintf_load_files_err(struct cus *cus, const char *tool, char *argv[], int err, FILE *output)
+{
+ /* errno is not properly preserved in some cases, sigh */
+ return fprintf(output, "%s: %s: %s\n", tool, argv[-err - 1],
+ errno ? strerror(errno) : "No debugging information found");
+}
+
+struct cus *cus__new(void)
+{
+ struct cus *cus = malloc(sizeof(*cus));
+
+ if (cus != NULL) {
+ cus->nr_entries = 0;
+ INIT_LIST_HEAD(&cus->cus);
+ }
+
+ return cus;
+}
+
+void cus__delete(struct cus *cus)
+{
+ struct cu *pos, *n;
+
+ if (cus == NULL)
+ return;
+
+ list_for_each_entry_safe(pos, n, &cus->cus, node) {
+ list_del_init(&pos->node);
+ cu__delete(pos);
+ }
+
+ free(cus);
+}
+
+void dwarves__fprintf_init(uint16_t user_cacheline_size);
+
+int dwarves__init(uint16_t user_cacheline_size)
+{
+ dwarves__fprintf_init(user_cacheline_size);
+
+ int i = 0;
+ int err = 0;
+
+ while (debug_fmt_table[i] != NULL) {
+ if (debug_fmt_table[i]->init) {
+ err = debug_fmt_table[i]->init();
+ if (err)
+ goto out_fail;
+ }
+ ++i;
+ }
+
+ return 0;
+out_fail:
+ while (i-- != 0)
+ if (debug_fmt_table[i]->exit)
+ debug_fmt_table[i]->exit();
+ return err;
+}
+
+void dwarves__exit(void)
+{
+ int i = 0;
+
+ while (debug_fmt_table[i] != NULL) {
+ if (debug_fmt_table[i]->exit)
+ debug_fmt_table[i]->exit();
+ ++i;
+ }
+}
+
+struct argp_state;
+
+void dwarves_print_version(FILE *fp, struct argp_state *state __unused)
+{
+ fprintf(fp, "v%u.%u\n", DWARVES_MAJOR_VERSION, DWARVES_MINOR_VERSION);
+}
+
+bool print_numeric_version;
+
+void dwarves_print_numeric_version(FILE *fp)
+{
+ fprintf(fp, "%u%u\n", DWARVES_MAJOR_VERSION, DWARVES_MINOR_VERSION);
+}
diff --git a/dwarves.h b/dwarves.h
new file mode 100644
index 0000000..24405b7
--- /dev/null
+++ b/dwarves.h
@@ -0,0 +1,1337 @@
+#ifndef _DWARVES_H_
+#define _DWARVES_H_ 1
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2006 Mandriva Conectiva S.A.
+ Copyright (C) 2006..2019 Arnaldo Carvalho de Melo <acme@redhat.com>
+*/
+
+
+#include <stdint.h>
+#include <stdio.h>
+#include <obstack.h>
+#include <dwarf.h>
+#include <elfutils/libdwfl.h>
+#include <sys/types.h>
+
+#include "dutil.h"
+#include "list.h"
+#include "rbtree.h"
+#include "pahole_strings.h"
+
+struct cu;
+
+enum load_steal_kind {
+ LSK__KEEPIT,
+ LSK__DELETE,
+ LSK__STOP_LOADING,
+};
+
+/*
+ * BTF combines all the types into one big CU using btf_dedup(), so for something
+ * like a allyesconfig vmlinux kernel we can get over 65535 types.
+ */
+typedef uint32_t type_id_t;
+
+struct conf_fprintf;
+
+/** struct conf_load - load configuration
+ * @extra_dbg_info - keep original debugging format extra info
+ * (e.g. DWARF's decl_{line,file}, id, etc)
+ * @fixup_silly_bitfields - Fixup silly things such as "int foo:32;"
+ * @get_addr_info - wheter to load DW_AT_location and other addr info
+ */
+struct conf_load {
+ enum load_steal_kind (*steal)(struct cu *cu,
+ struct conf_load *conf);
+ void *cookie;
+ char *format_path;
+ bool extra_dbg_info;
+ bool fixup_silly_bitfields;
+ bool get_addr_info;
+ struct conf_fprintf *conf_fprintf;
+};
+
+/** struct conf_fprintf - hints to the __fprintf routines
+ *
+ * @count - Just like 'dd', stop pretty printing input after 'count' records
+ * @skip - Just like 'dd', skip 'count' records when pretty printing input
+ * @seek_bytes - Number of bytes to seek, if stdin only from start, when we have --pretty FILE, then from the end as well with negative numbers,
+ * may be of the form $header.MEMBER_NAME when using with --header.
+ * @size_bytes - Number of bytes to read, similar to seek_bytes, and when both are in place, first seek seek_bytes then read size_bytes
+ * @range - data structure field in --header to determine --seek_bytes and --size_bytes, must have 'offset' and 'size' fields
+ * @flat_arrays - a->foo[10][2] becomes a->foo[20]
+ * @classes_as_structs - class f becomes struct f, CTF doesn't have a "class"
+ * @cachelinep - pointer to current cacheline, so that when expanding types we keep track of it,
+ * needs to be "global", i.e. not set at each recursion.
+ * @suppress_force_paddings: This makes sense only if the debugging format has struct alignment information,
+ * So allow for it to be disabled and disable it automatically for things like BTF,
+ * that don't have such info.
+ */
+struct conf_fprintf {
+ const char *prefix;
+ const char *suffix;
+ int32_t type_spacing;
+ int32_t name_spacing;
+ uint32_t base_offset;
+ uint32_t count;
+ uint32_t *cachelinep;
+ const char *seek_bytes;
+ const char *size_bytes;
+ const char *header_type;
+ const char *range;
+ uint32_t skip;
+ uint8_t indent;
+ uint8_t expand_types:1;
+ uint8_t expand_pointers:1;
+ uint8_t rel_offset:1;
+ uint8_t emit_stats:1;
+ uint8_t suppress_comments:1;
+ uint8_t has_alignment_info:1;
+ uint8_t suppress_aligned_attribute:1;
+ uint8_t suppress_offset_comment:1;
+ uint8_t suppress_force_paddings:1;
+ uint8_t suppress_packed:1;
+ uint8_t show_decl_info:1;
+ uint8_t show_only_data_members:1;
+ uint8_t no_semicolon:1;
+ uint8_t show_first_biggest_size_base_type_member:1;
+ uint8_t flat_arrays:1;
+ uint8_t first_member:1;
+ uint8_t last_member:1;
+ uint8_t union_member:1;
+ uint8_t no_parm_names:1;
+ uint8_t classes_as_structs:1;
+ uint8_t hex_fmt:1;
+ uint8_t strip_inline:1;
+};
+
+struct cus {
+ uint32_t nr_entries;
+ struct list_head cus;
+};
+
+struct cus *cus__new(void);
+void cus__delete(struct cus *cus);
+
+int cus__load_file(struct cus *cus, struct conf_load *conf,
+ const char *filename);
+int cus__load_files(struct cus *cus, struct conf_load *conf,
+ char *filenames[]);
+int cus__fprintf_load_files_err(struct cus *cus, const char *tool,
+ char *argv[], int err, FILE *output);
+int cus__load_dir(struct cus *cus, struct conf_load *conf,
+ const char *dirname, const char *filename_mask,
+ const int recursive);
+void cus__add(struct cus *cus, struct cu *cu);
+void cus__print_error_msg(const char *progname, const struct cus *cus,
+ const char *filename, const int err);
+struct cu *cus__find_cu_by_name(const struct cus *cus, const char *name);
+struct tag *cus__find_struct_by_name(const struct cus *cus, struct cu **cu,
+ const char *name, const int include_decls,
+ type_id_t *id);
+struct tag *cus__find_struct_or_union_by_name(const struct cus *cus, struct cu **cu,
+ const char *name, const int include_decls, type_id_t *id);
+struct tag *cu__find_type_by_name(const struct cu *cu, const char *name, const int include_decls, type_id_t *idp);
+struct tag *cus__find_type_by_name(const struct cus *cus, struct cu **cu, const char *name,
+ const int include_decls, type_id_t *id);
+struct function *cus__find_function_at_addr(const struct cus *cus,
+ uint64_t addr, struct cu **cu);
+void cus__for_each_cu(struct cus *cus, int (*iterator)(struct cu *cu, void *cookie),
+ void *cookie,
+ struct cu *(*filter)(struct cu *cu));
+
+struct ptr_table {
+ void **entries;
+ uint32_t nr_entries;
+ uint32_t allocated_entries;
+};
+
+struct function;
+struct tag;
+struct cu;
+struct variable;
+
+/* Same as DW_LANG, so that we don't have to include dwarf.h in CTF */
+enum dwarf_languages {
+ LANG_C89 = 0x01, /* ISO C:1989 */
+ LANG_C = 0x02, /* C */
+ LANG_Ada83 = 0x03, /* ISO Ada:1983 */
+ LANG_C_plus_plus = 0x04, /* ISO C++:1998 */
+ LANG_Cobol74 = 0x05, /* ISO Cobol:1974 */
+ LANG_Cobol85 = 0x06, /* ISO Cobol:1985 */
+ LANG_Fortran77 = 0x07, /* ISO FORTRAN 77 */
+ LANG_Fortran90 = 0x08, /* ISO Fortran 90 */
+ LANG_Pascal83 = 0x09, /* ISO Pascal:1983 */
+ LANG_Modula2 = 0x0a, /* ISO Modula-2:1996 */
+ LANG_Java = 0x0b, /* Java */
+ LANG_C99 = 0x0c, /* ISO C:1999 */
+ LANG_Ada95 = 0x0d, /* ISO Ada:1995 */
+ LANG_Fortran95 = 0x0e, /* ISO Fortran 95 */
+ LANG_PL1 = 0x0f, /* ISO PL/1:1976 */
+ LANG_Objc = 0x10, /* Objective-C */
+ LANG_ObjC_plus_plus = 0x11, /* Objective-C++ */
+ LANG_UPC = 0x12, /* Unified Parallel C */
+ LANG_D = 0x13, /* D */
+};
+
+/** struct debug_fmt_ops - specific to the underlying debug file format
+ *
+ * @function__name - will be called by function__name(), giving a chance to
+ * formats such as CTF to get this from some other place
+ * than the global strings table. CTF does this by storing
+ * GElf_Sym->st_name in function->name, and by using
+ * function->name as an index into the .strtab ELF section.
+ * @variable__name - will be called by variable__name(), see @function_name
+ * cu__delete - called at cu__delete(), to give a chance to formats such as
+ * CTF to keep the .strstab ELF section available till the cu is
+ * deleted. See @function__name
+ */
+struct debug_fmt_ops {
+ const char *name;
+ int (*init)(void);
+ void (*exit)(void);
+ int (*load_file)(struct cus *cus,
+ struct conf_load *conf,
+ const char *filename);
+ const char *(*tag__decl_file)(const struct tag *tag,
+ const struct cu *cu);
+ uint32_t (*tag__decl_line)(const struct tag *tag,
+ const struct cu *cu);
+ unsigned long long (*tag__orig_id)(const struct tag *tag,
+ const struct cu *cu);
+ void (*tag__free_orig_info)(struct tag *tag,
+ struct cu *cu);
+ const char *(*function__name)(struct function *tag,
+ const struct cu *cu);
+ const char *(*variable__name)(const struct variable *var,
+ const struct cu *cu);
+ const char *(*strings__ptr)(const struct cu *cu, strings_t s);
+ void (*cu__delete)(struct cu *cu);
+ bool has_alignment_info;
+};
+
+extern struct debug_fmt_ops *dwarves__active_loader;
+
+struct cu {
+ struct list_head node;
+ struct list_head tags;
+ struct list_head tool_list; /* To be used by tools such as ctracer */
+ struct ptr_table types_table;
+ struct ptr_table functions_table;
+ struct ptr_table tags_table;
+ struct rb_root functions;
+ char *name;
+ char *filename;
+ void *priv;
+ struct obstack obstack;
+ struct debug_fmt_ops *dfops;
+ Elf *elf;
+ Dwfl_Module *dwfl;
+ uint32_t cached_symtab_nr_entries;
+ uint8_t addr_size;
+ uint8_t extra_dbg_info:1;
+ uint8_t has_addr_info:1;
+ uint8_t uses_global_strings:1;
+ uint8_t little_endian:1;
+ uint16_t language;
+ unsigned long nr_inline_expansions;
+ size_t size_inline_expansions;
+ uint32_t nr_functions_changed;
+ uint32_t nr_structures_changed;
+ size_t max_len_changed_item;
+ size_t function_bytes_added;
+ size_t function_bytes_removed;
+ int build_id_len;
+ unsigned char build_id[0];
+};
+
+struct cu *cu__new(const char *name, uint8_t addr_size,
+ const unsigned char *build_id, int build_id_len,
+ const char *filename);
+void cu__delete(struct cu *cu);
+
+const char *cu__string(const struct cu *cu, strings_t s);
+
+static inline int cu__cache_symtab(struct cu *cu)
+{
+ int err = dwfl_module_getsymtab(cu->dwfl);
+ if (err > 0)
+ cu->cached_symtab_nr_entries = dwfl_module_getsymtab(cu->dwfl);
+ return err;
+}
+
+static inline __pure bool cu__is_c_plus_plus(const struct cu *cu)
+{
+ return cu->language == LANG_C_plus_plus;
+}
+
+/**
+ * cu__for_each_cached_symtab_entry - iterate thru the cached symtab entries
+ * @cu: struct cu instance
+ * @id: uint32_t tag id
+ * @pos: struct GElf_Sym iterator
+ * @name: char pointer where the symbol_name will be stored
+ */
+#define cu__for_each_cached_symtab_entry(cu, id, pos, name) \
+ for (id = 1, \
+ name = dwfl_module_getsym(cu->dwfl, id, &sym, NULL); \
+ id < cu->cached_symtab_nr_entries; \
+ ++id, name = dwfl_module_getsym(cu->dwfl, id, &sym, NULL))
+
+/**
+ * cu__for_each_type - iterate thru all the type tags
+ * @cu: struct cu instance to iterate
+ * @id: type_id_t id
+ * @pos: struct tag iterator
+ *
+ * See cu__table_nullify_type_entry and users for the reason for
+ * the NULL test (hint: CTF Unknown types)
+ */
+#define cu__for_each_type(cu, id, pos) \
+ for (id = 1; id < cu->types_table.nr_entries; ++id) \
+ if (!(pos = cu->types_table.entries[id])) \
+ continue; \
+ else
+
+/**
+ * cu__for_each_struct - iterate thru all the struct tags
+ * @cu: struct cu instance to iterate
+ * @pos: struct class iterator
+ * @id: type_id_t id
+ */
+#define cu__for_each_struct(cu, id, pos) \
+ for (id = 1; id < cu->types_table.nr_entries; ++id) \
+ if (!(pos = tag__class(cu->types_table.entries[id])) || \
+ !tag__is_struct(class__tag(pos))) \
+ continue; \
+ else
+
+/**
+ * cu__for_each_struct_or_union - iterate thru all the struct and union tags
+ * @cu: struct cu instance to iterate
+ * @pos: struct class iterator
+ * @id: type_id_t tag id
+ */
+#define cu__for_each_struct_or_union(cu, id, pos) \
+ for (id = 1; id < cu->types_table.nr_entries; ++id) \
+ if (!(pos = tag__class(cu->types_table.entries[id])) || \
+ !(tag__is_struct(class__tag(pos)) || \
+ tag__is_union(class__tag(pos)))) \
+ continue; \
+ else
+
+/**
+ * cu__for_each_function - iterate thru all the function tags
+ * @cu: struct cu instance to iterate
+ * @pos: struct function iterator
+ * @id: uint32_t tag id
+ */
+#define cu__for_each_function(cu, id, pos) \
+ for (id = 0; id < cu->functions_table.nr_entries; ++id) \
+ if (!(pos = tag__function(cu->functions_table.entries[id]))) \
+ continue; \
+ else
+
+/**
+ * cu__for_each_variable - iterate thru all the global variable tags
+ * @cu: struct cu instance to iterate
+ * @pos: struct tag iterator
+ * @id: uint32_t tag id
+ */
+#define cu__for_each_variable(cu, id, pos) \
+ for (id = 0; id < cu->tags_table.nr_entries; ++id) \
+ if (!(pos = cu->tags_table.entries[id]) || \
+ !tag__is_variable(pos)) \
+ continue; \
+ else
+
+int cu__add_tag(struct cu *cu, struct tag *tag, uint32_t *id);
+int cu__add_tag_with_id(struct cu *cu, struct tag *tag, uint32_t id);
+int cu__table_add_tag(struct cu *cu, struct tag *tag, uint32_t *id);
+int cu__table_add_tag_with_id(struct cu *cu, struct tag *tag, uint32_t id);
+int cu__table_nullify_type_entry(struct cu *cu, uint32_t id);
+struct tag *cu__find_base_type_by_name(const struct cu *cu, const char *name,
+ type_id_t *id);
+struct tag *cu__find_base_type_by_sname_and_size(const struct cu *cu,
+ strings_t name,
+ uint16_t bit_size,
+ type_id_t *idp);
+struct tag *cu__find_enumeration_by_name(const struct cu *cu, const char *name, type_id_t *idp);
+struct tag *cu__find_enumeration_by_sname_and_size(const struct cu *cu,
+ strings_t sname,
+ uint16_t bit_size,
+ type_id_t *idp);
+struct tag *cu__find_first_typedef_of_type(const struct cu *cu,
+ const type_id_t type);
+struct tag *cu__find_function_by_name(const struct cu *cu, const char *name);
+struct tag *cu__find_struct_by_sname(const struct cu *cu, strings_t sname,
+ const int include_decls, type_id_t *idp);
+struct function *cu__find_function_at_addr(const struct cu *cu,
+ uint64_t addr);
+struct tag *cu__function(const struct cu *cu, const uint32_t id);
+struct tag *cu__tag(const struct cu *cu, const uint32_t id);
+struct tag *cu__type(const struct cu *cu, const type_id_t id);
+struct tag *cu__find_struct_by_name(const struct cu *cu, const char *name,
+ const int include_decls, type_id_t *id);
+struct tag *cu__find_struct_or_union_by_name(const struct cu *cu, const char *name,
+ const int include_decls, type_id_t *id);
+bool cu__same_build_id(const struct cu *cu, const struct cu *other);
+void cu__account_inline_expansions(struct cu *cu);
+int cu__for_all_tags(struct cu *cu,
+ int (*iterator)(struct tag *tag,
+ struct cu *cu, void *cookie),
+ void *cookie);
+
+/** struct tag - basic representation of a debug info element
+ * @priv - extra data, for instance, DWARF offset, id, decl_{file,line}
+ * @top_level -
+ */
+struct tag {
+ struct list_head node;
+ type_id_t type;
+ uint16_t tag;
+ bool visited;
+ bool top_level;
+ uint16_t recursivity_level;
+ void *priv;
+};
+
+// To use with things like type->type_enum == perf_event_type+perf_user_event_type
+struct tag_cu {
+ struct tag *tag;
+ struct cu *cu;
+};
+
+void tag__delete(struct tag *tag, struct cu *cu);
+
+static inline int tag__is_enumeration(const struct tag *tag)
+{
+ return tag->tag == DW_TAG_enumeration_type;
+}
+
+static inline int tag__is_namespace(const struct tag *tag)
+{
+ return tag->tag == DW_TAG_namespace;
+}
+
+static inline int tag__is_struct(const struct tag *tag)
+{
+ return tag->tag == DW_TAG_structure_type ||
+ tag->tag == DW_TAG_interface_type ||
+ tag->tag == DW_TAG_class_type;
+}
+
+static inline int tag__is_typedef(const struct tag *tag)
+{
+ return tag->tag == DW_TAG_typedef;
+}
+
+static inline int tag__is_rvalue_reference_type(const struct tag *tag)
+{
+ return tag->tag == DW_TAG_rvalue_reference_type;
+}
+
+static inline int tag__is_union(const struct tag *tag)
+{
+ return tag->tag == DW_TAG_union_type;
+}
+
+static inline int tag__is_const(const struct tag *tag)
+{
+ return tag->tag == DW_TAG_const_type;
+}
+
+static inline int tag__is_pointer(const struct tag *tag)
+{
+ return tag->tag == DW_TAG_pointer_type;
+}
+
+static inline int tag__is_pointer_to(const struct tag *tag, type_id_t type)
+{
+ return tag__is_pointer(tag) && tag->type == type;
+}
+
+static inline bool tag__is_variable(const struct tag *tag)
+{
+ return tag->tag == DW_TAG_variable;
+}
+
+static inline bool tag__is_volatile(const struct tag *tag)
+{
+ return tag->tag == DW_TAG_volatile_type;
+}
+
+static inline bool tag__is_restrict(const struct tag *tag)
+{
+ return tag->tag == DW_TAG_restrict_type;
+}
+
+static inline int tag__is_modifier(const struct tag *tag)
+{
+ return tag__is_const(tag) ||
+ tag__is_volatile(tag) ||
+ tag__is_restrict(tag);
+}
+
+static inline bool tag__has_namespace(const struct tag *tag)
+{
+ return tag__is_struct(tag) ||
+ tag__is_union(tag) ||
+ tag__is_namespace(tag) ||
+ tag__is_enumeration(tag);
+}
+
+/**
+ * tag__is_tag_type - is this tag derived from the 'type' class?
+ * @tag - tag queried
+ */
+static inline int tag__is_type(const struct tag *tag)
+{
+ return tag__is_union(tag) ||
+ tag__is_struct(tag) ||
+ tag__is_typedef(tag) ||
+ tag__is_rvalue_reference_type(tag) ||
+ tag__is_enumeration(tag);
+}
+
+/**
+ * tag__is_tag_type - is this one of the possible types for a tag?
+ * @tag - tag queried
+ */
+static inline int tag__is_tag_type(const struct tag *tag)
+{
+ return tag__is_type(tag) ||
+ tag->tag == DW_TAG_array_type ||
+ tag->tag == DW_TAG_string_type ||
+ tag->tag == DW_TAG_base_type ||
+ tag->tag == DW_TAG_const_type ||
+ tag->tag == DW_TAG_pointer_type ||
+ tag->tag == DW_TAG_rvalue_reference_type ||
+ tag->tag == DW_TAG_ptr_to_member_type ||
+ tag->tag == DW_TAG_reference_type ||
+ tag->tag == DW_TAG_restrict_type ||
+ tag->tag == DW_TAG_subroutine_type ||
+ tag->tag == DW_TAG_unspecified_type ||
+ tag->tag == DW_TAG_volatile_type;
+}
+
+static inline const char *tag__decl_file(const struct tag *tag,
+ const struct cu *cu)
+{
+ if (cu->dfops && cu->dfops->tag__decl_file)
+ return cu->dfops->tag__decl_file(tag, cu);
+ return NULL;
+}
+
+static inline uint32_t tag__decl_line(const struct tag *tag,
+ const struct cu *cu)
+{
+ if (cu->dfops && cu->dfops->tag__decl_line)
+ return cu->dfops->tag__decl_line(tag, cu);
+ return 0;
+}
+
+static inline unsigned long long tag__orig_id(const struct tag *tag,
+ const struct cu *cu)
+{
+ if (cu->dfops && cu->dfops->tag__orig_id)
+ return cu->dfops->tag__orig_id(tag, cu);
+ return 0;
+}
+
+static inline void tag__free_orig_info(struct tag *tag, struct cu *cu)
+{
+ if (cu->dfops && cu->dfops->tag__free_orig_info)
+ cu->dfops->tag__free_orig_info(tag, cu);
+}
+
+size_t tag__fprintf_decl_info(const struct tag *tag,
+ const struct cu *cu, FILE *fp);
+size_t tag__fprintf(struct tag *tag, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp);
+
+const char *tag__name(const struct tag *tag, const struct cu *cu,
+ char *bf, size_t len, const struct conf_fprintf *conf);
+void tag__not_found_die(const char *file, int line, const char *func);
+
+#define tag__assert_search_result(tag) \
+ do { if (!tag) tag__not_found_die(__FILE__,\
+ __LINE__, __func__); } while (0)
+
+size_t tag__size(const struct tag *tag, const struct cu *cu);
+size_t tag__nr_cachelines(const struct tag *tag, const struct cu *cu);
+struct tag *tag__follow_typedef(const struct tag *tag, const struct cu *cu);
+struct tag *tag__strip_typedefs_and_modifiers(const struct tag *tag, const struct cu *cu);
+
+size_t __tag__id_not_found_fprintf(FILE *fp, type_id_t id,
+ const char *fn, int line);
+#define tag__id_not_found_fprintf(fp, id) \
+ __tag__id_not_found_fprintf(fp, id, __func__, __LINE__)
+
+int __tag__has_type_loop(const struct tag *tag, const struct tag *type,
+ char *bf, size_t len, FILE *fp,
+ const char *fn, int line);
+#define tag__has_type_loop(tag, type, bf, len, fp) \
+ __tag__has_type_loop(tag, type, bf, len, fp, __func__, __LINE__)
+
+struct ptr_to_member_type {
+ struct tag tag;
+ type_id_t containing_type;
+};
+
+static inline struct ptr_to_member_type *
+ tag__ptr_to_member_type(const struct tag *tag)
+{
+ return (struct ptr_to_member_type *)tag;
+}
+
+/** struct namespace - base class for enums, structs, unions, typedefs, etc
+ *
+ * @sname - for clones, for instance, where we can't always add a new string
+ * @tags - class_member, enumerators, etc
+ * @shared_tags: if this bit is set, don't free the entries in @tags
+ */
+struct namespace {
+ struct tag tag;
+ strings_t name;
+ uint16_t nr_tags;
+ uint8_t shared_tags;
+ char * sname;
+ struct list_head tags;
+};
+
+static inline struct namespace *tag__namespace(const struct tag *tag)
+{
+ return (struct namespace *)tag;
+}
+
+void namespace__delete(struct namespace *nspace, struct cu *cu);
+
+/**
+ * namespace__for_each_tag - iterate thru all the tags
+ * @nspace: struct namespace instance to iterate
+ * @pos: struct tag iterator
+ */
+#define namespace__for_each_tag(nspace, pos) \
+ list_for_each_entry(pos, &(nspace)->tags, node)
+
+/**
+ * namespace__for_each_tag_safe_reverse - safely iterate thru all the tags, in reverse order
+ * @nspace: struct namespace instance to iterate
+ * @pos: struct tag iterator
+ * @n: struct class_member temp iterator
+ */
+#define namespace__for_each_tag_safe_reverse(nspace, pos, n) \
+ list_for_each_entry_safe_reverse(pos, n, &(nspace)->tags, node)
+
+void namespace__add_tag(struct namespace *nspace, struct tag *tag);
+
+struct ip_tag {
+ struct tag tag;
+ uint64_t addr;
+};
+
+struct inline_expansion {
+ struct ip_tag ip;
+ size_t size;
+ uint64_t high_pc;
+};
+
+static inline struct inline_expansion *
+ tag__inline_expansion(const struct tag *tag)
+{
+ return (struct inline_expansion *)tag;
+}
+
+struct label {
+ struct ip_tag ip;
+ strings_t name;
+};
+
+static inline struct label *tag__label(const struct tag *tag)
+{
+ return (struct label *)tag;
+}
+
+static inline const char *label__name(const struct label *label,
+ const struct cu *cu)
+{
+ return cu__string(cu, label->name);
+}
+
+enum vscope {
+ VSCOPE_UNKNOWN,
+ VSCOPE_LOCAL,
+ VSCOPE_GLOBAL,
+ VSCOPE_REGISTER,
+ VSCOPE_OPTIMIZED
+} __attribute__((packed));
+
+struct location {
+ Dwarf_Op *expr;
+ size_t exprlen;
+};
+
+struct variable {
+ struct ip_tag ip;
+ strings_t name;
+ uint8_t external:1;
+ uint8_t declaration:1;
+ enum vscope scope;
+ struct location location;
+ struct hlist_node tool_hnode;
+ struct variable *spec;
+};
+
+static inline struct variable *tag__variable(const struct tag *tag)
+{
+ return (struct variable *)tag;
+}
+
+enum vscope variable__scope(const struct variable *var);
+const char *variable__scope_str(const struct variable *var);
+
+const char *variable__name(const struct variable *var, const struct cu *cu);
+
+const char *variable__type_name(const struct variable *var,
+ const struct cu *cu, char *bf, size_t len);
+
+struct lexblock {
+ struct ip_tag ip;
+ struct list_head tags;
+ uint32_t size;
+ uint16_t nr_inline_expansions;
+ uint16_t nr_labels;
+ uint16_t nr_variables;
+ uint16_t nr_lexblocks;
+ uint32_t size_inline_expansions;
+};
+
+static inline struct lexblock *tag__lexblock(const struct tag *tag)
+{
+ return (struct lexblock *)tag;
+}
+
+void lexblock__delete(struct lexblock *lexblock, struct cu *cu);
+
+struct function;
+
+void lexblock__add_inline_expansion(struct lexblock *lexblock,
+ struct inline_expansion *exp);
+void lexblock__add_label(struct lexblock *lexblock, struct label *label);
+void lexblock__add_lexblock(struct lexblock *lexblock, struct lexblock *child);
+void lexblock__add_tag(struct lexblock *lexblock, struct tag *tag);
+void lexblock__add_variable(struct lexblock *lexblock, struct variable *var);
+size_t lexblock__fprintf(const struct lexblock *lexblock, const struct cu *cu,
+ struct function *function, uint16_t indent,
+ const struct conf_fprintf *conf, FILE *fp);
+
+struct parameter {
+ struct tag tag;
+ strings_t name;
+};
+
+static inline struct parameter *tag__parameter(const struct tag *tag)
+{
+ return (struct parameter *)tag;
+}
+
+static inline const char *parameter__name(const struct parameter *parm,
+ const struct cu *cu)
+{
+ return cu__string(cu, parm->name);
+}
+
+/*
+ * tag.tag can be DW_TAG_subprogram_type or DW_TAG_subroutine_type.
+ */
+struct ftype {
+ struct tag tag;
+ struct list_head parms;
+ uint16_t nr_parms;
+ uint8_t unspec_parms; /* just one bit is needed */
+};
+
+static inline struct ftype *tag__ftype(const struct tag *tag)
+{
+ return (struct ftype *)tag;
+}
+
+void ftype__delete(struct ftype *ftype, struct cu *cu);
+
+/**
+ * ftype__for_each_parameter - iterate thru all the parameters
+ * @ftype: struct ftype instance to iterate
+ * @pos: struct parameter iterator
+ */
+#define ftype__for_each_parameter(ftype, pos) \
+ list_for_each_entry(pos, &(ftype)->parms, tag.node)
+
+/**
+ * ftype__for_each_parameter_safe - safely iterate thru all the parameters
+ * @ftype: struct ftype instance to iterate
+ * @pos: struct parameter iterator
+ * @n: struct parameter temp iterator
+ */
+#define ftype__for_each_parameter_safe(ftype, pos, n) \
+ list_for_each_entry_safe(pos, n, &(ftype)->parms, tag.node)
+
+/**
+ * ftype__for_each_parameter_safe_reverse - safely iterate thru all the parameters, in reverse order
+ * @ftype: struct ftype instance to iterate
+ * @pos: struct parameter iterator
+ * @n: struct parameter temp iterator
+ */
+#define ftype__for_each_parameter_safe_reverse(ftype, pos, n) \
+ list_for_each_entry_safe_reverse(pos, n, &(ftype)->parms, tag.node)
+
+void ftype__add_parameter(struct ftype *ftype, struct parameter *parm);
+size_t ftype__fprintf(const struct ftype *ftype, const struct cu *cu,
+ const char *name, const int inlined,
+ const int is_pointer, const int type_spacing, bool is_prototype,
+ const struct conf_fprintf *conf, FILE *fp);
+size_t ftype__fprintf_parms(const struct ftype *ftype,
+ const struct cu *cu, int indent,
+ const struct conf_fprintf *conf, FILE *fp);
+int ftype__has_parm_of_type(const struct ftype *ftype, const type_id_t target,
+ const struct cu *cu);
+
+struct function {
+ struct ftype proto;
+ struct lexblock lexblock;
+ struct rb_node rb_node;
+ strings_t name;
+ strings_t linkage_name;
+ uint32_t cu_total_size_inline_expansions;
+ uint16_t cu_total_nr_inline_expansions;
+ uint8_t inlined:2;
+ uint8_t abstract_origin:1;
+ uint8_t external:1;
+ uint8_t accessibility:2; /* DW_ACCESS_{public,protected,private} */
+ uint8_t virtuality:2; /* DW_VIRTUALITY_{none,virtual,pure_virtual} */
+ uint8_t declaration:1;
+ uint8_t btf:1;
+ int32_t vtable_entry;
+ struct list_head vtable_node;
+ /* fields used by tools */
+ union {
+ struct list_head tool_node;
+ struct hlist_node tool_hnode;
+ };
+ void *priv;
+};
+
+static inline struct function *tag__function(const struct tag *tag)
+{
+ return (struct function *)tag;
+}
+
+static inline struct tag *function__tag(const struct function *func)
+{
+ return (struct tag *)func;
+}
+
+void function__delete(struct function *func, struct cu *cu);
+
+static __pure inline int tag__is_function(const struct tag *tag)
+{
+ return tag->tag == DW_TAG_subprogram;
+}
+
+/**
+ * function__for_each_parameter - iterate thru all the parameters
+ * @func: struct function instance to iterate
+ * @pos: struct parameter iterator
+ */
+#define function__for_each_parameter(func, cu, pos) \
+ ftype__for_each_parameter(func->btf ? tag__ftype(cu__type(cu, func->proto.tag.type)) : &func->proto, pos)
+
+const char *function__name(struct function *func, const struct cu *cu);
+
+static inline const char *function__linkage_name(const struct function *func,
+ const struct cu *cu)
+{
+ return cu__string(cu, func->linkage_name);
+}
+
+size_t function__fprintf_stats(const struct tag *tag_func,
+ const struct cu *cu,
+ const struct conf_fprintf *conf,
+ FILE *fp);
+const char *function__prototype(const struct function *func,
+ const struct cu *cu, char *bf, size_t len);
+
+static __pure inline uint64_t function__addr(const struct function *func)
+{
+ return func->lexblock.ip.addr;
+}
+
+static __pure inline uint32_t function__size(const struct function *func)
+{
+ return func->lexblock.size;
+}
+
+static inline int function__declared_inline(const struct function *func)
+{
+ return (func->inlined == DW_INL_declared_inlined ||
+ func->inlined == DW_INL_declared_not_inlined);
+}
+
+static inline int function__inlined(const struct function *func)
+{
+ return (func->inlined == DW_INL_inlined ||
+ func->inlined == DW_INL_declared_inlined);
+}
+
+/* struct class_member - struct, union, class member
+ *
+ * @bit_offset - offset in bits from the start of the struct
+ * @bit_size - cached bit size, can be smaller than the integral type if in a bitfield
+ * @byte_offset - offset in bytes from the start of the struct
+ * @byte_size - cached byte size, integral type byte size for bitfields
+ * @bitfield_offset - offset in the current bitfield
+ * @bitfield_size - size in the current bitfield
+ * @bit_hole - If there is a bit hole before the next one (or the end of the struct)
+ * @bitfield_end - Is this the last entry in a bitfield?
+ * @alignment - DW_AT_alignement, zero if not present, gcc emits since circa 7.3.1
+ * @accessibility - DW_ACCESS_{public,protected,private}
+ * @virtuality - DW_VIRTUALITY_{none,virtual,pure_virtual}
+ * @hole - If there is a hole before the next one (or the end of the struct)
+ */
+struct class_member {
+ struct tag tag;
+ strings_t name;
+ uint32_t bit_offset;
+ uint32_t bit_size;
+ uint32_t byte_offset;
+ size_t byte_size;
+ int8_t bitfield_offset;
+ uint8_t bitfield_size;
+ uint8_t bit_hole;
+ uint8_t bitfield_end:1;
+ uint64_t const_value;
+ uint32_t alignment;
+ uint8_t visited:1;
+ uint8_t is_static:1;
+ uint8_t accessibility:2;
+ uint8_t virtuality:2;
+ uint16_t hole;
+};
+
+void class_member__delete(struct class_member *member, struct cu *cu);
+
+static inline struct class_member *tag__class_member(const struct tag *tag)
+{
+ return (struct class_member *)tag;
+}
+
+static inline const char *class_member__name(const struct class_member *member,
+ const struct cu *cu)
+{
+ return cu__string(cu, member->name);
+}
+
+static __pure inline int tag__is_class_member(const struct tag *tag)
+{
+ return tag->tag == DW_TAG_member;
+}
+
+int tag__is_base_type(const struct tag *tag, const struct cu *cu);
+bool tag__is_array(const struct tag *tag, const struct cu *cu);
+
+struct class_member_filter;
+
+struct tag_cu_node {
+ struct list_head node;
+ struct tag_cu tc;
+};
+
+/**
+ * struct type - base type for enumerations, structs and unions
+ *
+ * @nnr_members: number of non static DW_TAG_member entries
+ * @nr_static_members: number of static DW_TAG_member entries
+ * @nr_tags: number of tags
+ * @alignment: DW_AT_alignement, zero if not present, gcc emits since circa 7.3.1
+ * @natural_alignment: For inferring __packed__, normally the widest scalar in it, recursively
+ * @sizeof_member: Use this to find the size of the record
+ * @type_member: Use this to select a member from where to get an id on an enum to find a type
+ * to cast for, needs to be used with the upcoming type_enum.
+ * @type_enum: enumeration(s) to use together with type_member to find a type to cast
+ * @member_prefix: the common prefix for all members, say in an enum, this should be calculated on demand
+ * @member_prefix_len: the lenght of the common prefix for all members
+ */
+struct type {
+ struct namespace namespace;
+ struct list_head node;
+ uint32_t size;
+ int32_t size_diff;
+ uint16_t nr_static_members;
+ uint16_t nr_members;
+ uint32_t alignment;
+ struct class_member *sizeof_member;
+ struct class_member *type_member;
+ struct class_member_filter *filter;
+ struct list_head type_enum;
+ char *member_prefix;
+ uint16_t member_prefix_len;
+ uint16_t max_tag_name_len;
+ uint16_t natural_alignment;
+ bool packed_attributes_inferred;
+ uint8_t declaration; /* only one bit used */
+ uint8_t definition_emitted:1;
+ uint8_t fwd_decl_emitted:1;
+ uint8_t resized:1;
+};
+
+void __type__init(struct type *type);
+
+static inline struct class *type__class(const struct type *type)
+{
+ return (struct class *)type;
+}
+
+static inline struct tag *type__tag(const struct type *type)
+{
+ return (struct tag *)type;
+}
+
+void type__delete(struct type *type, struct cu *cu);
+
+/**
+ * type__for_each_tag - iterate thru all the tags
+ * @type: struct type instance to iterate
+ * @pos: struct tag iterator
+ */
+#define type__for_each_tag(type, pos) \
+ list_for_each_entry(pos, &(type)->namespace.tags, node)
+
+/**
+ * type__for_each_enumerator - iterate thru the enumerator entries
+ * @type: struct type instance to iterate
+ * @pos: struct enumerator iterator
+ */
+#define type__for_each_enumerator(type, pos) \
+ struct list_head *__type__for_each_enumerator_head = \
+ (type)->namespace.shared_tags ? \
+ (type)->namespace.tags.next : \
+ &(type)->namespace.tags; \
+ list_for_each_entry(pos, __type__for_each_enumerator_head, tag.node)
+
+/**
+ * type__for_each_enumerator_safe_reverse - safely iterate thru the enumerator entries, in reverse order
+ * @type: struct type instance to iterate
+ * @pos: struct enumerator iterator
+ * @n: struct enumerator temp iterator
+ */
+#define type__for_each_enumerator_safe_reverse(type, pos, n) \
+ if ((type)->namespace.shared_tags) /* Do nothing */ ; else \
+ list_for_each_entry_safe_reverse(pos, n, &(type)->namespace.tags, tag.node)
+
+/**
+ * type__for_each_member - iterate thru the entries that use space
+ * (data members and inheritance entries)
+ * @type: struct type instance to iterate
+ * @pos: struct class_member iterator
+ */
+#define type__for_each_member(type, pos) \
+ list_for_each_entry(pos, &(type)->namespace.tags, tag.node) \
+ if (!(pos->tag.tag == DW_TAG_member || \
+ pos->tag.tag == DW_TAG_inheritance)) \
+ continue; \
+ else
+
+/**
+ * type__for_each_data_member - iterate thru the data member entries
+ * @type: struct type instance to iterate
+ * @pos: struct class_member iterator
+ */
+#define type__for_each_data_member(type, pos) \
+ list_for_each_entry(pos, &(type)->namespace.tags, tag.node) \
+ if (pos->tag.tag != DW_TAG_member) \
+ continue; \
+ else
+
+/**
+ * type__for_each_member_safe - safely iterate thru the entries that use space
+ * (data members and inheritance entries)
+ * @type: struct type instance to iterate
+ * @pos: struct class_member iterator
+ * @n: struct class_member temp iterator
+ */
+#define type__for_each_member_safe(type, pos, n) \
+ list_for_each_entry_safe(pos, n, &(type)->namespace.tags, tag.node) \
+ if (pos->tag.tag != DW_TAG_member) \
+ continue; \
+ else
+
+/**
+ * type__for_each_data_member_safe - safely iterate thru the data member entries
+ * @type: struct type instance to iterate
+ * @pos: struct class_member iterator
+ * @n: struct class_member temp iterator
+ */
+#define type__for_each_data_member_safe(type, pos, n) \
+ list_for_each_entry_safe(pos, n, &(type)->namespace.tags, tag.node) \
+ if (pos->tag.tag != DW_TAG_member) \
+ continue; \
+ else
+
+/**
+ * type__for_each_tag_safe_reverse - safely iterate thru all tags in a type, in reverse order
+ * @type: struct type instance to iterate
+ * @pos: struct class_member iterator
+ * @n: struct class_member temp iterator
+ */
+#define type__for_each_tag_safe_reverse(type, pos, n) \
+ list_for_each_entry_safe_reverse(pos, n, &(type)->namespace.tags, tag.node)
+
+void type__add_member(struct type *type, struct class_member *member);
+struct class_member *
+ type__find_first_biggest_size_base_type_member(struct type *type,
+ const struct cu *cu);
+
+struct class_member *type__find_member_by_name(const struct type *type,
+ const struct cu *cu,
+ const char *name);
+uint32_t type__nr_members_of_type(const struct type *type, const type_id_t oftype);
+struct class_member *type__last_member(struct type *type);
+
+void enumeration__calc_prefix(struct type *type, const struct cu *cu);
+const char *enumeration__prefix(struct type *type, const struct cu *cu);
+uint16_t enumeration__prefix_len(struct type *type, const struct cu *cu);
+int enumeration__max_entry_name_len(struct type *type, const struct cu *cu);
+
+void enumerations__calc_prefix(struct list_head *enumerations);
+
+size_t typedef__fprintf(const struct tag *tag_type, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp);
+
+static inline struct type *tag__type(const struct tag *tag)
+{
+ return (struct type *)tag;
+}
+
+struct class {
+ struct type type;
+ struct list_head vtable;
+ uint16_t nr_vtable_entries;
+ uint8_t nr_holes;
+ uint8_t nr_bit_holes;
+ uint16_t pre_hole;
+ uint16_t padding;
+ uint8_t pre_bit_hole;
+ uint8_t bit_padding;
+ bool holes_searched;
+ bool is_packed;
+ void *priv;
+};
+
+static inline struct class *tag__class(const struct tag *tag)
+{
+ return (struct class *)tag;
+}
+
+static inline struct tag *class__tag(const struct class *cls)
+{
+ return (struct tag *)cls;
+}
+
+struct class *class__clone(const struct class *from,
+ const char *new_class_name, struct cu *cu);
+void class__delete(struct class *cls, struct cu *cu);
+
+static inline struct list_head *class__tags(struct class *cls)
+{
+ return &cls->type.namespace.tags;
+}
+
+static __pure inline const char *namespace__name(const struct namespace *nspace,
+ const struct cu *cu)
+{
+ return nspace->sname ?: cu__string(cu, nspace->name);
+}
+
+static __pure inline const char *type__name(const struct type *type,
+ const struct cu *cu)
+{
+ return namespace__name(&type->namespace, cu);
+}
+
+static __pure inline const char *class__name(struct class *cls,
+ const struct cu *cu)
+{
+ return type__name(&cls->type, cu);
+}
+
+static inline int class__is_struct(const struct class *cls)
+{
+ return tag__is_struct(&cls->type.namespace.tag);
+}
+
+void class__find_holes(struct class *cls);
+int class__has_hole_ge(const struct class *cls, const uint16_t size);
+
+bool class__infer_packed_attributes(struct class *cls, const struct cu *cu);
+
+void union__infer_packed_attributes(struct type *type, const struct cu *cu);
+
+void type__check_structs_at_unnatural_alignments(struct type *type, const struct cu *cu);
+
+size_t class__fprintf(struct class *cls, const struct cu *cu, FILE *fp);
+
+void class__add_vtable_entry(struct class *cls, struct function *vtable_entry);
+static inline struct class_member *
+ class__find_member_by_name(const struct class *cls,
+ const struct cu *cu, const char *name)
+{
+ return type__find_member_by_name(&cls->type, cu, name);
+}
+
+static inline uint16_t class__nr_members(const struct class *cls)
+{
+ return cls->type.nr_members;
+}
+
+static inline uint32_t class__size(const struct class *cls)
+{
+ return cls->type.size;
+}
+
+static inline int class__is_declaration(const struct class *cls)
+{
+ return cls->type.declaration;
+}
+
+const struct class_member *class__find_bit_hole(const struct class *cls,
+ const struct class_member *trailer,
+ const uint16_t bit_hole_size);
+
+#define class__for_each_member_from(cls, from, pos) \
+ pos = list_prepare_entry(from, class__tags(cls), tag.node); \
+ list_for_each_entry_from(pos, class__tags(cls), tag.node) \
+ if (!tag__is_class_member(&pos->tag)) \
+ continue; \
+ else
+
+#define class__for_each_member_safe_from(cls, from, pos, tmp) \
+ pos = list_prepare_entry(from, class__tags(cls), tag.node); \
+ list_for_each_entry_safe_from(pos, tmp, class__tags(cls), tag.node) \
+ if (!tag__is_class_member(&pos->tag)) \
+ continue; \
+ else
+
+#define class__for_each_member_continue(cls, from, pos) \
+ pos = list_prepare_entry(from, class__tags(cls), tag.node); \
+ list_for_each_entry_continue(pos, class__tags(cls), tag.node) \
+ if (!tag__is_class_member(&pos->tag)) \
+ continue; \
+ else
+
+#define class__for_each_member_reverse(cls, member) \
+ list_for_each_entry_reverse(member, class__tags(cls), tag.node) \
+ if (member->tag.tag != DW_TAG_member) \
+ continue; \
+ else
+
+enum base_type_float_type {
+ BT_FP_SINGLE = 1,
+ BT_FP_DOUBLE,
+ BT_FP_CMPLX,
+ BT_FP_CMPLX_DBL,
+ BT_FP_CMPLX_LDBL,
+ BT_FP_LDBL,
+ BT_FP_INTVL,
+ BT_FP_INTVL_DBL,
+ BT_FP_INTVL_LDBL,
+ BT_FP_IMGRY,
+ BT_FP_IMGRY_DBL,
+ BT_FP_IMGRY_LDBL
+};
+
+struct base_type {
+ struct tag tag;
+ strings_t name;
+ uint16_t bit_size;
+ uint8_t name_has_encoding:1;
+ uint8_t is_signed:1;
+ uint8_t is_bool:1;
+ uint8_t is_varargs:1;
+ uint8_t float_type:4;
+};
+
+static inline struct base_type *tag__base_type(const struct tag *tag)
+{
+ return (struct base_type *)tag;
+}
+
+static inline uint16_t base_type__size(const struct tag *tag)
+{
+ return tag__base_type(tag)->bit_size / 8;
+}
+
+const char *base_type__name(const struct base_type *btype, const struct cu *cu,
+ char *bf, size_t len);
+
+void base_type_name_to_size_table__init(struct strings *strings);
+size_t base_type__name_to_size(struct base_type *btype, struct cu *cu);
+
+struct array_type {
+ struct tag tag;
+ uint32_t *nr_entries;
+ uint8_t dimensions;
+ bool is_vector;
+};
+
+static inline struct array_type *tag__array_type(const struct tag *tag)
+{
+ return (struct array_type *)tag;
+}
+
+struct string_type {
+ struct tag tag;
+ uint32_t nr_entries;
+};
+
+static inline struct string_type *tag__string_type(const struct tag *tag)
+{
+ return (struct string_type *)tag;
+}
+
+struct enumerator {
+ struct tag tag;
+ strings_t name;
+ uint32_t value;
+ struct tag_cu type_enum; // To cache the type_enum searches
+};
+
+static inline const char *enumerator__name(const struct enumerator *enumerator,
+ const struct cu *cu)
+{
+ return cu__string(cu, enumerator->name);
+}
+
+void enumeration__delete(struct type *type, struct cu *cu);
+void enumeration__add(struct type *type, struct enumerator *enumerator);
+size_t enumeration__fprintf(const struct tag *tag_enum, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp);
+
+int dwarves__init(uint16_t user_cacheline_size);
+void dwarves__exit(void);
+
+const char *dwarf_tag_name(const uint32_t tag);
+
+struct argp_state;
+
+void dwarves_print_version(FILE *fp, struct argp_state *state);
+void dwarves_print_numeric_version(FILE *fp);
+
+extern bool print_numeric_version;;
+
+extern bool no_bitfield_type_recode;
+
+extern const char tabs[];
+
+#endif /* _DWARVES_H_ */
diff --git a/dwarves_emit.c b/dwarves_emit.c
new file mode 100644
index 0000000..434e339
--- /dev/null
+++ b/dwarves_emit.c
@@ -0,0 +1,346 @@
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2006 Mandriva Conectiva S.A.
+ Copyright (C) 2006 Arnaldo Carvalho de Melo <acme@mandriva.com>
+ Copyright (C) 2007 Red Hat Inc.
+ Copyright (C) 2007 Arnaldo Carvalho de Melo <acme@redhat.com>
+*/
+
+#include <string.h>
+
+#include "list.h"
+#include "dwarves_emit.h"
+#include "dwarves.h"
+
+void type_emissions__init(struct type_emissions *emissions)
+{
+ INIT_LIST_HEAD(&emissions->definitions);
+ INIT_LIST_HEAD(&emissions->fwd_decls);
+}
+
+static void type_emissions__add_definition(struct type_emissions *emissions,
+ struct type *type)
+{
+ type->definition_emitted = 1;
+ if (!list_empty(&type->node))
+ list_del(&type->node);
+ list_add_tail(&type->node, &emissions->definitions);
+}
+
+static void type_emissions__add_fwd_decl(struct type_emissions *emissions,
+ struct type *type)
+{
+ type->fwd_decl_emitted = 1;
+ if (list_empty(&type->node))
+ list_add_tail(&type->node, &emissions->fwd_decls);
+}
+
+struct type *type_emissions__find_definition(const struct type_emissions *emissions,
+ const struct cu *cu,
+ const char *name)
+{
+ struct type *pos;
+
+ if (name == NULL)
+ return NULL;
+
+ list_for_each_entry(pos, &emissions->definitions, node)
+ if (type__name(pos, cu) != NULL &&
+ strcmp(type__name(pos, cu), name) == 0)
+ return pos;
+
+ return NULL;
+}
+
+static struct type *type_emissions__find_fwd_decl(const struct type_emissions *emissions,
+ const struct cu *cu,
+ const char *name)
+{
+ struct type *pos;
+
+ if (name == NULL)
+ return NULL;
+
+ list_for_each_entry(pos, &emissions->fwd_decls, node) {
+ const char *curr_name = type__name(pos, cu);
+
+ if (curr_name && strcmp(curr_name, name) == 0)
+ return pos;
+ }
+
+ return NULL;
+}
+
+static int enumeration__emit_definitions(struct tag *tag, struct cu *cu,
+ struct type_emissions *emissions,
+ const struct conf_fprintf *conf,
+ FILE *fp)
+{
+ struct type *etype = tag__type(tag);
+
+ /* Have we already emitted this in this CU? */
+ if (etype->definition_emitted)
+ return 0;
+
+ /* Ok, lets look at the previous CUs: */
+ if (type_emissions__find_definition(emissions, cu,
+ type__name(etype, cu)) != NULL) {
+ /*
+ * Yes, so lets mark it visited on this CU too,
+ * to speed up the lookup.
+ */
+ etype->definition_emitted = 1;
+ return 0;
+ }
+
+ enumeration__fprintf(tag, cu, conf, fp);
+ fputs(";\n", fp);
+ type_emissions__add_definition(emissions, etype);
+ return 1;
+}
+
+static int tag__emit_definitions(struct tag *tag, struct cu *cu,
+ struct type_emissions *emissions, FILE *fp);
+
+static int typedef__emit_definitions(struct tag *tdef, struct cu *cu,
+ struct type_emissions *emissions, FILE *fp)
+{
+ struct type *def = tag__type(tdef);
+ struct tag *type, *ptr_type;
+
+ /* Have we already emitted this in this CU? */
+ if (def->definition_emitted)
+ return 0;
+
+ /* Ok, lets look at the previous CUs: */
+ if (type_emissions__find_definition(emissions, cu,
+ type__name(def, cu)) != NULL) {
+ /*
+ * Yes, so lets mark it visited on this CU too,
+ * to speed up the lookup.
+ */
+ def->definition_emitted = 1;
+ return 0;
+ }
+
+ type = cu__type(cu, tdef->type);
+ tag__assert_search_result(type);
+
+ switch (type->tag) {
+ case DW_TAG_array_type:
+ tag__emit_definitions(type, cu, emissions, fp);
+ break;
+ case DW_TAG_typedef:
+ typedef__emit_definitions(type, cu, emissions, fp);
+ break;
+ case DW_TAG_pointer_type:
+ ptr_type = cu__type(cu, type->type);
+ /* void ** can make ptr_type be NULL */
+ if (ptr_type == NULL)
+ break;
+ if (ptr_type->tag == DW_TAG_typedef) {
+ typedef__emit_definitions(ptr_type, cu, emissions, fp);
+ break;
+ } else if (ptr_type->tag != DW_TAG_subroutine_type)
+ break;
+ type = ptr_type;
+ /* Fall thru */
+ case DW_TAG_subroutine_type:
+ ftype__emit_definitions(tag__ftype(type), cu, emissions, fp);
+ break;
+ case DW_TAG_enumeration_type: {
+ struct type *ctype = tag__type(type);
+ struct conf_fprintf conf = {
+ .suffix = NULL,
+ };
+
+ if (type__name(ctype, cu) == NULL) {
+ fputs("typedef ", fp);
+ conf.suffix = type__name(def, cu);
+ enumeration__emit_definitions(type, cu, emissions,
+ &conf, fp);
+ goto out;
+ } else
+ enumeration__emit_definitions(type, cu, emissions,
+ &conf, fp);
+ }
+ break;
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type: {
+ struct type *ctype = tag__type(type);
+
+ if (type__name(ctype, cu) == NULL) {
+ if (type__emit_definitions(type, cu, emissions, fp))
+ type__emit(type, cu, "typedef",
+ type__name(def, cu), fp);
+ goto out;
+ } else if (type__emit_definitions(type, cu, emissions, fp))
+ type__emit(type, cu, NULL, NULL, fp);
+ }
+ }
+
+ /*
+ * Recheck if the typedef was emitted, as there are cases, like
+ * wait_queue_t in the Linux kernel, that is against struct
+ * __wait_queue, that has a wait_queue_func_t member, a function
+ * typedef that has as one of its parameters a... wait_queue_t, that
+ * will thus be emitted before the function typedef, making a no go to
+ * redefine the typedef after struct __wait_queue.
+ */
+ if (!def->definition_emitted) {
+ typedef__fprintf(tdef, cu, NULL, fp);
+ fputs(";\n", fp);
+ }
+out:
+ type_emissions__add_definition(emissions, def);
+ return 1;
+}
+
+int type__emit_fwd_decl(struct type *ctype, const struct cu *cu,
+ struct type_emissions *emissions, FILE *fp)
+{
+ /* Have we already emitted this in this CU? */
+ if (ctype->fwd_decl_emitted)
+ return 0;
+
+ const char *name = type__name(ctype, cu);
+ if (name == NULL)
+ return 0;
+
+ /* Ok, lets look at the previous CUs: */
+ if (type_emissions__find_fwd_decl(emissions, cu, name) != NULL) {
+ /*
+ * Yes, so lets mark it visited on this CU too,
+ * to speed up the lookup.
+ */
+ ctype->fwd_decl_emitted = 1;
+ return 0;
+ }
+
+ fprintf(fp, "%s %s;\n",
+ tag__is_union(&ctype->namespace.tag) ? "union" : "struct",
+ type__name(ctype, cu));
+ type_emissions__add_fwd_decl(emissions, ctype);
+ return 1;
+}
+
+static int tag__emit_definitions(struct tag *tag, struct cu *cu,
+ struct type_emissions *emissions, FILE *fp)
+{
+ struct tag *type = cu__type(cu, tag->type);
+ int pointer = 0;
+
+ if (type == NULL)
+ return 0;
+next_indirection:
+ switch (type->tag) {
+ case DW_TAG_pointer_type:
+ case DW_TAG_reference_type:
+ pointer = 1;
+ /* Fall thru */
+ case DW_TAG_array_type:
+ case DW_TAG_const_type:
+ case DW_TAG_volatile_type:
+ type = cu__type(cu, type->type);
+ if (type == NULL)
+ return 0;
+ goto next_indirection;
+ case DW_TAG_typedef:
+ return typedef__emit_definitions(type, cu, emissions, fp);
+ case DW_TAG_enumeration_type:
+ if (type__name(tag__type(type), cu) != NULL) {
+ struct conf_fprintf conf = {
+ .suffix = NULL,
+ };
+ return enumeration__emit_definitions(type, cu, emissions,
+ &conf, fp);
+ }
+ break;
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ if (pointer) {
+ /*
+ * Struct defined inline, no name, need to have its
+ * members types emitted.
+ */
+ if (type__name(tag__type(type), cu) == NULL)
+ type__emit_definitions(type, cu, emissions, fp);
+
+ return type__emit_fwd_decl(tag__type(type), cu,
+ emissions, fp);
+ }
+ if (type__emit_definitions(type, cu, emissions, fp))
+ type__emit(type, cu, NULL, NULL, fp);
+ return 1;
+ case DW_TAG_subroutine_type:
+ return ftype__emit_definitions(tag__ftype(type), cu,
+ emissions, fp);
+ }
+
+ return 0;
+}
+
+int ftype__emit_definitions(struct ftype *ftype, struct cu *cu,
+ struct type_emissions *emissions, FILE *fp)
+{
+ struct parameter *pos;
+ /* First check the function return type */
+ int printed = tag__emit_definitions(&ftype->tag, cu, emissions, fp);
+
+ /* Then its parameters */
+ list_for_each_entry(pos, &ftype->parms, tag.node)
+ if (tag__emit_definitions(&pos->tag, cu, emissions, fp))
+ printed = 1;
+
+ if (printed)
+ fputc('\n', fp);
+ return printed;
+}
+
+int type__emit_definitions(struct tag *tag, struct cu *cu,
+ struct type_emissions *emissions, FILE *fp)
+{
+ struct type *ctype = tag__type(tag);
+ struct class_member *pos;
+
+ if (ctype->definition_emitted)
+ return 0;
+
+ /* Ok, lets look at the previous CUs: */
+ if (type_emissions__find_definition(emissions, cu,
+ type__name(ctype, cu)) != NULL) {
+ ctype->definition_emitted = 1;
+ return 0;
+ }
+
+ if (tag__is_typedef(tag))
+ return typedef__emit_definitions(tag, cu, emissions, fp);
+
+ type_emissions__add_definition(emissions, ctype);
+
+ type__check_structs_at_unnatural_alignments(ctype, cu);
+
+ type__for_each_member(ctype, pos)
+ if (tag__emit_definitions(&pos->tag, cu, emissions, fp))
+ fputc('\n', fp);
+
+ return 1;
+}
+
+void type__emit(struct tag *tag, struct cu *cu,
+ const char *prefix, const char *suffix, FILE *fp)
+{
+ struct type *ctype = tag__type(tag);
+
+ if (type__name(ctype, cu) != NULL ||
+ suffix != NULL || prefix != NULL) {
+ struct conf_fprintf conf = {
+ .prefix = prefix,
+ .suffix = suffix,
+ .emit_stats = 1,
+ };
+ tag__fprintf(tag, cu, &conf, fp);
+ fputc('\n', fp);
+ }
+}
diff --git a/dwarves_emit.h b/dwarves_emit.h
new file mode 100644
index 0000000..5d8c7ab
--- /dev/null
+++ b/dwarves_emit.h
@@ -0,0 +1,38 @@
+#ifndef _DWARVES_EMIT_H_
+#define _DWARVES_EMIT_H_ 1
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2006 Mandriva Conectiva S.A.
+ Copyright (C) 2006 Arnaldo Carvalho de Melo <acme@mandriva.com>
+ Copyright (C) 2007 Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
+*/
+
+#include <stdio.h>
+#include "list.h"
+
+struct cu;
+struct ftype;
+struct tag;
+struct type;
+
+struct type_emissions {
+ struct list_head definitions; /* struct type entries */
+ struct list_head fwd_decls; /* struct class entries */
+};
+
+void type_emissions__init(struct type_emissions *temissions);
+
+int ftype__emit_definitions(struct ftype *ftype, struct cu *cu,
+ struct type_emissions *emissions, FILE *fp);
+int type__emit_definitions(struct tag *tag, struct cu *cu,
+ struct type_emissions *emissions, FILE *fp);
+int type__emit_fwd_decl(struct type *ctype, const struct cu *cu,
+ struct type_emissions *emissions, FILE *fp);
+void type__emit(struct tag *tag_type, struct cu *cu,
+ const char *prefix, const char *suffix, FILE *fp);
+struct type *type_emissions__find_definition(const struct type_emissions *temissions,
+ const struct cu *cu,
+ const char *name);
+
+#endif /* _DWARVES_EMIT_H_ */
diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c
new file mode 100644
index 0000000..c96a6fb
--- /dev/null
+++ b/dwarves_fprintf.c
@@ -0,0 +1,1967 @@
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2006 Mandriva Conectiva S.A.
+ Copyright (C) 2006 Arnaldo Carvalho de Melo <acme@mandriva.com>
+ Copyright (C) 2007..2009 Red Hat Inc.
+ Copyright (C) 2007..2009 Arnaldo Carvalho de Melo <acme@redhat.com>
+*/
+
+#include <dwarf.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <elfutils/version.h>
+
+#include "config.h"
+#include "dwarves.h"
+
+static const char *dwarf_tag_names[] = {
+ [DW_TAG_array_type] = "array_type",
+ [DW_TAG_class_type] = "class_type",
+ [DW_TAG_entry_point] = "entry_point",
+ [DW_TAG_enumeration_type] = "enumeration_type",
+ [DW_TAG_formal_parameter] = "formal_parameter",
+ [DW_TAG_imported_declaration] = "imported_declaration",
+ [DW_TAG_label] = "label",
+ [DW_TAG_lexical_block] = "lexical_block",
+ [DW_TAG_member] = "member",
+ [DW_TAG_pointer_type] = "pointer_type",
+ [DW_TAG_reference_type] = "reference_type",
+ [DW_TAG_compile_unit] = "compile_unit",
+ [DW_TAG_string_type] = "string_type",
+ [DW_TAG_structure_type] = "structure_type",
+ [DW_TAG_subroutine_type] = "subroutine_type",
+ [DW_TAG_typedef] = "typedef",
+ [DW_TAG_union_type] = "union_type",
+ [DW_TAG_unspecified_parameters] = "unspecified_parameters",
+ [DW_TAG_variant] = "variant",
+ [DW_TAG_common_block] = "common_block",
+ [DW_TAG_common_inclusion] = "common_inclusion",
+ [DW_TAG_inheritance] = "inheritance",
+ [DW_TAG_inlined_subroutine] = "inlined_subroutine",
+ [DW_TAG_module] = "module",
+ [DW_TAG_ptr_to_member_type] = "ptr_to_member_type",
+ [DW_TAG_set_type] = "set_type",
+ [DW_TAG_subrange_type] = "subrange_type",
+ [DW_TAG_with_stmt] = "with_stmt",
+ [DW_TAG_access_declaration] = "access_declaration",
+ [DW_TAG_base_type] = "base_type",
+ [DW_TAG_catch_block] = "catch_block",
+ [DW_TAG_const_type] = "const_type",
+ [DW_TAG_constant] = "constant",
+ [DW_TAG_enumerator] = "enumerator",
+ [DW_TAG_file_type] = "file_type",
+ [DW_TAG_friend] = "friend",
+ [DW_TAG_namelist] = "namelist",
+ [DW_TAG_namelist_item] = "namelist_item",
+ [DW_TAG_packed_type] = "packed_type",
+ [DW_TAG_subprogram] = "subprogram",
+ [DW_TAG_template_type_parameter] = "template_type_parameter",
+ [DW_TAG_template_value_parameter] = "template_value_parameter",
+ [DW_TAG_thrown_type] = "thrown_type",
+ [DW_TAG_try_block] = "try_block",
+ [DW_TAG_variant_part] = "variant_part",
+ [DW_TAG_variable] = "variable",
+ [DW_TAG_volatile_type] = "volatile_type",
+ [DW_TAG_dwarf_procedure] = "dwarf_procedure",
+ [DW_TAG_restrict_type] = "restrict_type",
+ [DW_TAG_interface_type] = "interface_type",
+ [DW_TAG_namespace] = "namespace",
+ [DW_TAG_imported_module] = "imported_module",
+ [DW_TAG_unspecified_type] = "unspecified_type",
+ [DW_TAG_partial_unit] = "partial_unit",
+ [DW_TAG_imported_unit] = "imported_unit",
+ [DW_TAG_condition] = "condition",
+ [DW_TAG_shared_type] = "shared_type",
+#ifdef STB_GNU_UNIQUE
+ [DW_TAG_type_unit] = "type_unit",
+ [DW_TAG_rvalue_reference_type] = "rvalue_reference_type",
+#endif
+};
+
+static const char *dwarf_gnu_tag_names[] = {
+ [DW_TAG_MIPS_loop - DW_TAG_MIPS_loop] = "MIPS_loop",
+ [DW_TAG_format_label - DW_TAG_MIPS_loop] = "format_label",
+ [DW_TAG_function_template - DW_TAG_MIPS_loop] = "function_template",
+ [DW_TAG_class_template - DW_TAG_MIPS_loop] = "class_template",
+#ifdef STB_GNU_UNIQUE
+ [DW_TAG_GNU_BINCL - DW_TAG_MIPS_loop] = "GNU_BINCL",
+ [DW_TAG_GNU_EINCL - DW_TAG_MIPS_loop] = "GNU_EINCL",
+ [DW_TAG_GNU_template_template_param - DW_TAG_MIPS_loop] = "GNU_template_template_param",
+ [DW_TAG_GNU_template_parameter_pack - DW_TAG_MIPS_loop] = "GNU_template_parameter_pack",
+ [DW_TAG_GNU_formal_parameter_pack - DW_TAG_MIPS_loop] = "GNU_formal_parameter_pack",
+#endif
+#if _ELFUTILS_PREREQ(0, 153)
+ [DW_TAG_GNU_call_site - DW_TAG_MIPS_loop] = "GNU_call_site",
+ [DW_TAG_GNU_call_site_parameter - DW_TAG_MIPS_loop] = "GNU_call_site_parameter",
+#endif
+};
+
+const char *dwarf_tag_name(const uint32_t tag)
+{
+ if (tag >= DW_TAG_array_type && tag <=
+#ifdef STB_GNU_UNIQUE
+ DW_TAG_rvalue_reference_type
+#else
+ DW_TAG_shared_type
+#endif
+ )
+ return dwarf_tag_names[tag];
+ else if (tag >= DW_TAG_MIPS_loop && tag <=
+#if _ELFUTILS_PREREQ(0, 153)
+ DW_TAG_GNU_call_site_parameter
+#elif STB_GNU_UNIQUE
+ DW_TAG_GNU_formal_parameter_pack
+#else
+ DW_TAG_class_template
+#endif
+ )
+ return dwarf_gnu_tag_names[tag - DW_TAG_MIPS_loop];
+ return "INVALID";
+}
+
+static const struct conf_fprintf conf_fprintf__defaults = {
+ .name_spacing = 23,
+ .type_spacing = 26,
+ .emit_stats = 1,
+};
+
+const char tabs[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+
+static size_t cacheline_size;
+
+size_t tag__nr_cachelines(const struct tag *tag, const struct cu *cu)
+{
+ return (tag__size(tag, cu) + cacheline_size - 1) / cacheline_size;
+}
+
+static const char *tag__accessibility(const struct tag *tag)
+{
+ int a;
+
+ switch (tag->tag) {
+ case DW_TAG_inheritance:
+ case DW_TAG_member:
+ a = tag__class_member(tag)->accessibility;
+ break;
+ case DW_TAG_subprogram:
+ a = tag__function(tag)->accessibility;
+ break;
+ default:
+ return NULL;
+ }
+
+ switch (a) {
+ case DW_ACCESS_public: return "public";
+ case DW_ACCESS_private: return "private";
+ case DW_ACCESS_protected: return "protected";
+ }
+
+ return NULL;
+}
+
+static size_t __tag__id_not_found_snprintf(char *bf, size_t len, uint32_t id,
+ const char *fn, int line)
+{
+ return snprintf(bf, len, "<ERROR(%s:%d): %#llx not found!>", fn, line,
+ (unsigned long long)id);
+}
+
+#define tag__id_not_found_snprintf(bf, len, id) \
+ __tag__id_not_found_snprintf(bf, len, id, __func__, __LINE__)
+
+size_t tag__fprintf_decl_info(const struct tag *tag,
+ const struct cu *cu, FILE *fp)
+{
+ return fprintf(fp, "/* <%llx> %s:%u */\n", tag__orig_id(tag, cu),
+ tag__decl_file(tag, cu), tag__decl_line(tag, cu));
+ return 0;
+}
+
+static size_t __class__fprintf(struct class *class, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp);
+static size_t type__fprintf(struct tag *type, const struct cu *cu,
+ const char *name, const struct conf_fprintf *conf,
+ FILE *fp);
+
+static size_t array_type__fprintf(const struct tag *tag,
+ const struct cu *cu, const char *name,
+ const struct conf_fprintf *conf,
+ FILE *fp)
+{
+ struct array_type *at = tag__array_type(tag);
+ struct tag *type = cu__type(cu, tag->type);
+ size_t printed;
+ unsigned long long flat_dimensions = 0;
+ int i;
+
+ if (type == NULL)
+ return tag__id_not_found_fprintf(fp, tag->type);
+
+ /* Zero sized arrays? */
+ if (at->dimensions >= 1 && at->nr_entries[0] == 0 && tag__is_const(type))
+ type = cu__type(cu, type->type);
+
+ printed = type__fprintf(type, cu, name, conf, fp);
+ for (i = 0; i < at->dimensions; ++i) {
+ if (conf->flat_arrays || at->is_vector) {
+ /*
+ * Seen on the Linux kernel on tun_filter:
+ *
+ * __u8 addr[0][ETH_ALEN];
+ */
+ if (at->nr_entries[i] == 0 && i == 0)
+ break;
+ if (!flat_dimensions)
+ flat_dimensions = at->nr_entries[i];
+ else
+ flat_dimensions *= at->nr_entries[i];
+ } else {
+ bool single_member = conf->last_member && conf->first_member;
+
+ if (at->nr_entries[i] != 0 || !conf->last_member || single_member || conf->union_member)
+ printed += fprintf(fp, "[%u]", at->nr_entries[i]);
+ else
+ printed += fprintf(fp, "[]");
+ }
+ }
+
+ if (at->is_vector) {
+ type = tag__follow_typedef(tag, cu);
+
+ if (flat_dimensions == 0)
+ flat_dimensions = 1;
+ printed += fprintf(fp, " __attribute__ ((__vector_size__ (%llu)))",
+ flat_dimensions * tag__size(type, cu));
+ } else if (conf->flat_arrays) {
+ bool single_member = conf->last_member && conf->first_member;
+
+ if (flat_dimensions != 0 || !conf->last_member || single_member || conf->union_member)
+ printed += fprintf(fp, "[%llu]", flat_dimensions);
+ else
+ printed += fprintf(fp, "[]");
+ }
+
+ return printed;
+}
+
+static size_t string_type__fprintf(const struct tag *tag,
+ const struct cu *cu, const char *name,
+ const struct conf_fprintf *conf,
+ FILE *fp)
+{
+ struct string_type *st = tag__string_type(tag);
+
+ return fprintf(fp, "string %*s[%u]", conf->type_spacing - 5, name, st->nr_entries);
+}
+
+size_t typedef__fprintf(const struct tag *tag, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp)
+{
+ struct type *type = tag__type(tag);
+ const struct conf_fprintf *pconf = conf ?: &conf_fprintf__defaults;
+ const struct tag *tag_type;
+ const struct tag *ptr_type;
+ char bf[512];
+ int is_pointer = 0;
+ size_t printed;
+
+ /*
+ * Check for void (humm, perhaps we should have a fake void tag instance
+ * to avoid all these checks?
+ */
+ if (tag->type == 0)
+ return fprintf(fp, "typedef void %s", type__name(type, cu));
+
+ tag_type = cu__type(cu, tag->type);
+ if (tag_type == NULL) {
+ printed = fprintf(fp, "typedef ");
+ printed += tag__id_not_found_fprintf(fp, tag->type);
+ return printed + fprintf(fp, " %s", type__name(type, cu));
+ }
+
+ switch (tag_type->tag) {
+ case DW_TAG_array_type:
+ printed = fprintf(fp, "typedef ");
+ return printed + array_type__fprintf(tag_type, cu,
+ type__name(type, cu),
+ pconf, fp);
+ case DW_TAG_pointer_type:
+ if (tag_type->type == 0) /* void pointer */
+ break;
+ ptr_type = cu__type(cu, tag_type->type);
+ if (ptr_type == NULL) {
+ printed = fprintf(fp, "typedef ");
+ printed += tag__id_not_found_fprintf(fp, tag_type->type);
+ return printed + fprintf(fp, " *%s",
+ type__name(type, cu));
+ }
+ if (ptr_type->tag != DW_TAG_subroutine_type)
+ break;
+ tag_type = ptr_type;
+ is_pointer = 1;
+ /* Fall thru */
+ case DW_TAG_subroutine_type:
+ printed = fprintf(fp, "typedef ");
+ return printed + ftype__fprintf(tag__ftype(tag_type), cu,
+ type__name(type, cu),
+ 0, is_pointer, 0,
+ true, pconf, fp);
+ case DW_TAG_class_type:
+ case DW_TAG_structure_type: {
+ struct type *ctype = tag__type(tag_type);
+
+ if (type__name(ctype, cu) != NULL)
+ return fprintf(fp, "typedef struct %s %s",
+ type__name(ctype, cu),
+ type__name(type, cu));
+
+ struct conf_fprintf tconf = *pconf;
+
+ tconf.suffix = type__name(type, cu);
+ return fprintf(fp, "typedef ") + __class__fprintf(tag__class(tag_type), cu, &tconf, fp);
+ }
+ case DW_TAG_enumeration_type: {
+ struct type *ctype = tag__type(tag_type);
+
+ if (type__name(ctype, cu) != NULL)
+ return fprintf(fp, "typedef enum %s %s", type__name(ctype, cu), type__name(type, cu));
+
+ struct conf_fprintf tconf = *pconf;
+
+ tconf.suffix = type__name(type, cu);
+ return fprintf(fp, "typedef ") + enumeration__fprintf(tag_type, cu, &tconf, fp);
+ }
+ }
+
+ return fprintf(fp, "typedef %s %s",
+ tag__name(tag_type, cu, bf, sizeof(bf), pconf),
+ type__name(type, cu));
+}
+
+static size_t imported_declaration__fprintf(const struct tag *tag,
+ const struct cu *cu, FILE *fp)
+{
+ char bf[BUFSIZ];
+ size_t printed = fprintf(fp, "using ::");
+ const struct tag *decl = cu__function(cu, tag->type);
+
+ if (decl == NULL) {
+ decl = cu__tag(cu, tag->type);
+ if (decl == NULL)
+ return printed + tag__id_not_found_fprintf(fp, tag->type);
+ }
+
+ return printed + fprintf(fp, "%s", tag__name(decl, cu, bf, sizeof(bf), NULL));
+}
+
+static size_t imported_module__fprintf(const struct tag *tag,
+ const struct cu *cu, FILE *fp)
+{
+ const struct tag *module = cu__tag(cu, tag->type);
+ const char *name = "<IMPORTED MODULE ERROR!>";
+
+ if (tag__is_namespace(module))
+ name = namespace__name(tag__namespace(module), cu);
+
+ return fprintf(fp, "using namespace %s", name);
+}
+
+int enumeration__max_entry_name_len(struct type *type, const struct cu *cu)
+{
+ if (type->max_tag_name_len)
+ goto out;
+
+ struct enumerator *pos;
+
+ type__for_each_enumerator(type, pos) {
+ int len = strlen(enumerator__name(pos, cu));
+
+ if (type->max_tag_name_len < len)
+ type->max_tag_name_len = len;
+ }
+out:
+ return type->max_tag_name_len;
+}
+
+size_t enumeration__fprintf(const struct tag *tag, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp)
+{
+ struct type *type = tag__type(tag);
+ struct enumerator *pos;
+ int max_entry_name_len = enumeration__max_entry_name_len(type, cu);
+ size_t printed = fprintf(fp, "enum%s%s {\n",
+ type__name(type, cu) ? " " : "",
+ type__name(type, cu) ?: "");
+ int indent = conf->indent;
+
+ if (indent >= (int)sizeof(tabs))
+ indent = sizeof(tabs) - 1;
+
+ type__for_each_enumerator(type, pos)
+ printed += fprintf(fp, "%.*s\t%-*s = %u,\n", indent, tabs,
+ max_entry_name_len, enumerator__name(pos, cu), pos->value);
+
+ printed += fprintf(fp, "%.*s}", indent, tabs);
+
+ /*
+ * XXX: find out how to precisely determine the max size for an
+ * enumeration, use sizeof(int) for now.
+ */
+ if (type->size / 8 != sizeof(int))
+ printed += fprintf(fp, " %s", "__attribute__((__packed__))");
+
+ if (conf->suffix)
+ printed += fprintf(fp, " %s", conf->suffix);
+
+ return printed;
+}
+
+static const char *tag__prefix(const struct cu *cu, const uint32_t tag,
+ const struct conf_fprintf *conf)
+{
+ switch (tag) {
+ case DW_TAG_enumeration_type: return "enum ";
+ case DW_TAG_structure_type:
+ return (!conf->classes_as_structs &&
+ cu->language == DW_LANG_C_plus_plus) ? "class " :
+ "struct ";
+ case DW_TAG_class_type:
+ return conf->classes_as_structs ? "struct " : "class ";
+ case DW_TAG_union_type: return "union ";
+ case DW_TAG_pointer_type: return " *";
+ case DW_TAG_reference_type: return " &";
+ }
+
+ return "";
+}
+
+static const char *__tag__name(const struct tag *tag, const struct cu *cu,
+ char *bf, size_t len,
+ const struct conf_fprintf *conf);
+
+static const char *tag__ptr_name(const struct tag *tag, const struct cu *cu,
+ char *bf, size_t len, const char *ptr_suffix)
+{
+ if (tag->type == 0) /* No type == void */
+ snprintf(bf, len, "void %s", ptr_suffix);
+ else {
+ const struct tag *type = cu__type(cu, tag->type);
+
+ if (type == NULL) {
+ size_t l = tag__id_not_found_snprintf(bf, len, tag->type);
+ snprintf(bf + l, len - l, " %s", ptr_suffix);
+ } else if (!tag__has_type_loop(tag, type, bf, len, NULL)) {
+ char tmpbf[1024];
+ const char *const_pointer = "";
+
+ if (tag__is_const(type)) {
+ struct tag *next_type = cu__type(cu, type->type);
+
+ if (next_type && tag__is_pointer(next_type)) {
+ const_pointer = "const ";
+ type = next_type;
+ }
+ }
+
+ snprintf(bf, len, "%s %s%s",
+ __tag__name(type, cu,
+ tmpbf, sizeof(tmpbf), NULL),
+ const_pointer,
+ ptr_suffix);
+ }
+ }
+
+ return bf;
+}
+
+static const char *__tag__name(const struct tag *tag, const struct cu *cu,
+ char *bf, size_t len,
+ const struct conf_fprintf *conf)
+{
+ struct tag *type;
+ const struct conf_fprintf *pconf = conf ?: &conf_fprintf__defaults;
+
+ if (tag == NULL)
+ strncpy(bf, "void", len);
+ else switch (tag->tag) {
+ case DW_TAG_base_type: {
+ const struct base_type *bt = tag__base_type(tag);
+ const char *name = "nameless base type!";
+ char bf2[64];
+
+ if (bt->name)
+ name = base_type__name(tag__base_type(tag), cu,
+ bf2, sizeof(bf2));
+
+ strncpy(bf, name, len);
+ }
+ break;
+ case DW_TAG_subprogram:
+ strncpy(bf, function__name(tag__function(tag), cu), len);
+ break;
+ case DW_TAG_pointer_type:
+ return tag__ptr_name(tag, cu, bf, len, "*");
+ case DW_TAG_reference_type:
+ return tag__ptr_name(tag, cu, bf, len, "&");
+ case DW_TAG_ptr_to_member_type: {
+ char suffix[512];
+ type_id_t id = tag__ptr_to_member_type(tag)->containing_type;
+
+ type = cu__type(cu, id);
+ if (type != NULL)
+ snprintf(suffix, sizeof(suffix), "%s::*",
+ class__name(tag__class(type), cu));
+ else {
+ size_t l = tag__id_not_found_snprintf(suffix,
+ sizeof(suffix),
+ id);
+ snprintf(suffix + l, sizeof(suffix) - l, "::*");
+ }
+
+ return tag__ptr_name(tag, cu, bf, len, suffix);
+ }
+ case DW_TAG_volatile_type:
+ case DW_TAG_const_type:
+ case DW_TAG_restrict_type:
+ case DW_TAG_unspecified_type:
+ type = cu__type(cu, tag->type);
+ if (type == NULL && tag->type != 0)
+ tag__id_not_found_snprintf(bf, len, tag->type);
+ else if (!tag__has_type_loop(tag, type, bf, len, NULL)) {
+ char tmpbf[128];
+ const char *prefix = "", *suffix = "",
+ *type_str = __tag__name(type, cu, tmpbf,
+ sizeof(tmpbf),
+ pconf);
+ switch (tag->tag) {
+ case DW_TAG_volatile_type: prefix = "volatile "; break;
+ case DW_TAG_const_type: prefix = "const "; break;
+ case DW_TAG_restrict_type: suffix = " restrict"; break;
+ }
+ snprintf(bf, len, "%s%s%s ", prefix, type_str, suffix);
+ }
+ break;
+ case DW_TAG_array_type:
+ type = cu__type(cu, tag->type);
+ if (type == NULL)
+ tag__id_not_found_snprintf(bf, len, tag->type);
+ else if (!tag__has_type_loop(tag, type, bf, len, NULL))
+ return __tag__name(type, cu, bf, len, pconf);
+ break;
+ case DW_TAG_subroutine_type: {
+ FILE *bfp = fmemopen(bf, len, "w");
+
+ if (bfp != NULL) {
+ ftype__fprintf(tag__ftype(tag), cu, NULL, 0, 0, 0, true, pconf, bfp);
+ fclose(bfp);
+ } else
+ snprintf(bf, len, "<ERROR(%s): fmemopen failed!>",
+ __func__);
+ }
+ break;
+ case DW_TAG_member:
+ snprintf(bf, len, "%s", class_member__name(tag__class_member(tag), cu));
+ break;
+ case DW_TAG_variable:
+ snprintf(bf, len, "%s", variable__name(tag__variable(tag), cu));
+ break;
+ default:
+ snprintf(bf, len, "%s%s", tag__prefix(cu, tag->tag, pconf),
+ type__name(tag__type(tag), cu) ?: "");
+ break;
+ }
+
+ return bf;
+}
+
+const char *tag__name(const struct tag *tag, const struct cu *cu,
+ char *bf, size_t len, const struct conf_fprintf *conf)
+{
+ int printed = 0;
+
+ if (tag == NULL) {
+ strncpy(bf, "void", len);
+ return bf;
+ }
+
+ __tag__name(tag, cu, bf + printed, len - printed, conf);
+
+ return bf;
+}
+
+static const char *variable__prefix(const struct variable *var)
+{
+ switch (variable__scope(var)) {
+ case VSCOPE_REGISTER:
+ return "register ";
+ case VSCOPE_UNKNOWN:
+ if (var->external && var->declaration)
+ return "extern ";
+ break;
+ case VSCOPE_GLOBAL:
+ if (!var->external)
+ return "static ";
+ break;
+ case VSCOPE_LOCAL:
+ case VSCOPE_OPTIMIZED:
+ break;
+ }
+ return NULL;
+}
+
+static size_t type__fprintf_stats(struct type *type, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp)
+{
+ size_t printed = fprintf(fp, "\n%.*s/* size: %d, cachelines: %zd, members: %u",
+ conf->indent, tabs, type->size,
+ tag__nr_cachelines(type__tag(type), cu), type->nr_members);
+
+ if (type->nr_static_members != 0)
+ printed += fprintf(fp, ", static members: %u */\n", type->nr_static_members);
+ else
+ printed += fprintf(fp, " */\n");
+
+ return printed;
+}
+
+static size_t union__fprintf(struct type *type, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp);
+
+static size_t type__fprintf(struct tag *type, const struct cu *cu,
+ const char *name, const struct conf_fprintf *conf,
+ FILE *fp)
+{
+ char tbf[128];
+ char namebf[256];
+ char namebfptr[258];
+ struct type *ctype;
+ struct tag *type_expanded = NULL;
+ struct conf_fprintf tconf = {
+ .type_spacing = conf->type_spacing,
+ };
+ size_t printed = 0;
+ int expand_types = conf->expand_types;
+ int suppress_offset_comment = conf->suppress_offset_comment;
+
+ if (type == NULL)
+ goto out_type_not_found;
+
+ if (conf->expand_pointers) {
+ int nr_indirections = 0;
+
+ while (tag__is_pointer(type) && type->type != 0) {
+ struct tag *ttype = cu__type(cu, type->type);
+ if (ttype == NULL)
+ goto out_type_not_found;
+ else {
+ printed = tag__has_type_loop(type, ttype,
+ NULL, 0, fp);
+ if (printed)
+ return printed;
+ }
+ type = ttype;
+ ++nr_indirections;
+ }
+
+ if (nr_indirections > 0) {
+ const size_t len = strlen(name);
+ if (len + nr_indirections >= sizeof(namebf))
+ goto out_type_not_found;
+ memset(namebf, '*', nr_indirections);
+ memcpy(namebf + nr_indirections, name, len);
+ namebf[len + nr_indirections] = '\0';
+ name = namebf;
+ }
+
+ expand_types = nr_indirections;
+ if (!suppress_offset_comment)
+ suppress_offset_comment = !!nr_indirections;
+
+ /* Avoid loops */
+ if (type->recursivity_level != 0)
+ expand_types = 0;
+ ++type->recursivity_level;
+ type_expanded = type;
+ }
+
+ if (expand_types) {
+ int typedef_expanded = 0;
+
+ while (tag__is_typedef(type)) {
+ struct tag *type_type;
+ int n;
+
+ ctype = tag__type(type);
+ if (typedef_expanded)
+ printed += fprintf(fp, " -> %s",
+ type__name(ctype, cu));
+ else {
+ printed += fprintf(fp, "/* typedef %s",
+ type__name(ctype, cu));
+ typedef_expanded = 1;
+ }
+ type_type = cu__type(cu, type->type);
+ if (type_type == NULL)
+ goto out_type_not_found;
+ n = tag__has_type_loop(type, type_type, NULL, 0, fp);
+ if (n)
+ return printed + n;
+ type = type_type;
+ }
+ if (typedef_expanded)
+ printed += fprintf(fp, " */ ");
+ }
+
+ tconf = *conf;
+
+ if (tag__is_struct(type) || tag__is_union(type) ||
+ tag__is_enumeration(type)) {
+inner_struct:
+ tconf.prefix = NULL;
+ tconf.suffix = name;
+ tconf.emit_stats = 0;
+ tconf.suppress_offset_comment = suppress_offset_comment;
+ }
+
+next_type:
+ switch (type->tag) {
+ case DW_TAG_pointer_type:
+ if (type->type != 0) {
+ int n;
+ struct tag *ptype = cu__type(cu, type->type);
+ if (ptype == NULL)
+ goto out_type_not_found;
+ n = tag__has_type_loop(type, ptype, NULL, 0, fp);
+ if (n)
+ return printed + n;
+ if (ptype->tag == DW_TAG_subroutine_type) {
+ printed += ftype__fprintf(tag__ftype(ptype),
+ cu, name, 0, 1,
+ tconf.type_spacing, true,
+ &tconf, fp);
+ break;
+ }
+ if ((tag__is_struct(ptype) || tag__is_union(ptype) ||
+ tag__is_enumeration(ptype)) && type__name(tag__type(ptype), cu) == NULL) {
+ if (name == namebfptr)
+ goto out_type_not_found;
+ snprintf(namebfptr, sizeof(namebfptr), "* %.*s", (int)sizeof(namebfptr) - 3, name);
+ tconf.rel_offset = 1;
+ name = namebfptr;
+ type = ptype;
+ tconf.type_spacing -= 8;
+ goto inner_struct;
+ }
+ }
+ /* Fall Thru */
+ default:
+print_default:
+ printed += fprintf(fp, "%-*s %s", tconf.type_spacing,
+ tag__name(type, cu, tbf, sizeof(tbf), &tconf),
+ name);
+ break;
+ case DW_TAG_subroutine_type:
+ printed += ftype__fprintf(tag__ftype(type), cu, name, 0, 0,
+ tconf.type_spacing, true, &tconf, fp);
+ break;
+ case DW_TAG_const_type: {
+ size_t const_printed = fprintf(fp, "%s ", "const");
+ tconf.type_spacing -= const_printed;
+ printed += const_printed;
+
+ struct tag *ttype = cu__type(cu, type->type);
+ if (ttype) {
+ type = ttype;
+ goto next_type;
+ }
+ }
+ goto print_default;
+
+ case DW_TAG_array_type:
+ printed += array_type__fprintf(type, cu, name, &tconf, fp);
+ break;
+ case DW_TAG_string_type:
+ printed += string_type__fprintf(type, cu, name, &tconf, fp);
+ break;
+ case DW_TAG_class_type:
+ case DW_TAG_structure_type:
+ ctype = tag__type(type);
+
+ if (type__name(ctype, cu) != NULL && !expand_types) {
+ printed += fprintf(fp, "%s %-*s %s",
+ (type->tag == DW_TAG_class_type &&
+ !tconf.classes_as_structs) ? "class" : "struct",
+ tconf.type_spacing - 7,
+ type__name(ctype, cu), name);
+ } else {
+ struct class *cclass = tag__class(type);
+
+ if (!tconf.suppress_comments)
+ class__find_holes(cclass);
+
+ tconf.type_spacing -= 8;
+ printed += __class__fprintf(cclass, cu, &tconf, fp);
+ }
+ break;
+ case DW_TAG_union_type:
+ ctype = tag__type(type);
+
+ if (type__name(ctype, cu) != NULL && !expand_types) {
+ printed += fprintf(fp, "union %-*s %s",
+ tconf.type_spacing - 6,
+ type__name(ctype, cu), name);
+ } else {
+ tconf.type_spacing -= 8;
+ printed += union__fprintf(ctype, cu, &tconf, fp);
+ }
+ break;
+ case DW_TAG_enumeration_type:
+ ctype = tag__type(type);
+
+ if (type__name(ctype, cu) != NULL)
+ printed += fprintf(fp, "enum %-*s %s",
+ tconf.type_spacing - 5,
+ type__name(ctype, cu), name);
+ else
+ printed += enumeration__fprintf(type, cu, &tconf, fp);
+ break;
+ }
+out:
+ if (type_expanded)
+ --type_expanded->recursivity_level;
+
+ return printed;
+out_type_not_found:
+ printed = fprintf(fp, "%-*s%s> %s", tconf.type_spacing, "<ERROR",
+ name == namebfptr ? ": pointer to pointer to inner struct/union/enum?" : "", name);
+ goto out;
+}
+
+static size_t class__fprintf_cacheline_boundary(struct conf_fprintf *conf,
+ uint32_t offset,
+ FILE *fp);
+
+static size_t class_member__fprintf(struct class_member *member, bool union_member,
+ struct tag *type, const struct cu *cu,
+ struct conf_fprintf *conf, FILE *fp)
+{
+ const int size = member->byte_size;
+ struct conf_fprintf sconf = *conf;
+ uint32_t offset = member->byte_offset;
+ size_t printed = 0, printed_cacheline = 0;
+ const char *cm_name = class_member__name(member, cu),
+ *name = cm_name;
+
+ if (!sconf.rel_offset) {
+ offset += sconf.base_offset;
+ if (!union_member)
+ sconf.base_offset = offset;
+ }
+
+ if (member->bitfield_offset < 0)
+ offset += member->byte_size;
+
+ if (!conf->suppress_comments)
+ printed_cacheline = class__fprintf_cacheline_boundary(conf, offset, fp);
+
+ if (member->tag.tag == DW_TAG_inheritance) {
+ name = "<ancestor>";
+ printed += fprintf(fp, "/* ");
+ }
+
+ if (member->is_static)
+ printed += fprintf(fp, "static ");
+
+ printed += type__fprintf(type, cu, name, &sconf, fp);
+
+ if (member->is_static) {
+ if (member->const_value != 0)
+ printed += fprintf(fp, " = %" PRIu64, member->const_value);
+ } else if (member->bitfield_size != 0) {
+ printed += fprintf(fp, ":%u", member->bitfield_size);
+ }
+
+ if (!sconf.suppress_aligned_attribute && member->alignment != 0)
+ printed += fprintf(fp, " __attribute__((__aligned__(%u)))", member->alignment);
+
+ fputc(';', fp);
+ ++printed;
+
+ if ((tag__is_union(type) || tag__is_struct(type) ||
+ tag__is_enumeration(type)) &&
+ /* Look if is a type defined inline */
+ type__name(tag__type(type), cu) == NULL) {
+ if (!sconf.suppress_offset_comment) {
+ /* Check if this is a anonymous union */
+ int slen = cm_name ? (int)strlen(cm_name) : -1;
+ int size_spacing = 5;
+
+ if (tag__is_struct(type) && tag__class(type)->is_packed && !conf->suppress_packed) {
+ int packed_len = sizeof("__attribute__((__packed__))");
+ slen += packed_len;
+ }
+
+ if (tag__type(type)->alignment != 0 && !conf->suppress_aligned_attribute) {
+ char bftmp[64];
+ int aligned_len = snprintf(bftmp, sizeof(bftmp), " __attribute__((__aligned__(%u)))", tag__type(type)->alignment);
+ slen += aligned_len;
+ }
+
+ printed += fprintf(fp, sconf.hex_fmt ?
+ "%*s/* %#5x" :
+ "%*s/* %5u",
+ (sconf.type_spacing +
+ sconf.name_spacing - slen - 3),
+ " ", offset);
+
+ if (member->bitfield_size != 0) {
+ unsigned int bitfield_offset = member->bitfield_offset;
+
+ if (member->bitfield_offset < 0)
+ bitfield_offset = member->byte_size * 8 + member->bitfield_offset;
+
+ printed += fprintf(fp, sconf.hex_fmt ? ":%#2x" : ":%2u", bitfield_offset);
+ size_spacing -= 3;
+ }
+
+ printed += fprintf(fp, sconf.hex_fmt ? " %#*x */" : " %*u */", size_spacing, size);
+ }
+ } else {
+ int spacing = sconf.type_spacing + sconf.name_spacing - printed;
+
+ if (member->tag.tag == DW_TAG_inheritance) {
+ const size_t p = fprintf(fp, " */");
+ printed += p;
+ spacing -= p;
+ }
+ if (!sconf.suppress_offset_comment) {
+ int size_spacing = 5;
+
+ printed += fprintf(fp, sconf.hex_fmt ?
+ "%*s/* %#5x" : "%*s/* %5u",
+ spacing > 0 ? spacing : 0, " ",
+ offset);
+
+ if (member->bitfield_size != 0) {
+ unsigned int bitfield_offset = member->bitfield_offset;
+
+ if (member->bitfield_offset < 0)
+ bitfield_offset = member->byte_size * 8 + member->bitfield_offset;
+
+ printed += fprintf(fp, sconf.hex_fmt ?
+ ":%#2x" : ":%2u",
+ bitfield_offset);
+ size_spacing -= 3;
+ }
+
+ printed += fprintf(fp, sconf.hex_fmt ?
+ " %#*x */" : " %*u */",
+ size_spacing, size);
+ }
+ }
+ return printed + printed_cacheline;
+}
+
+static size_t struct_member__fprintf(struct class_member *member,
+ struct tag *type, const struct cu *cu,
+ struct conf_fprintf *conf, FILE *fp)
+{
+ return class_member__fprintf(member, false, type, cu, conf, fp);
+}
+
+static size_t union_member__fprintf(struct class_member *member,
+ struct tag *type, const struct cu *cu,
+ struct conf_fprintf *conf, FILE *fp)
+{
+ return class_member__fprintf(member, true, type, cu, conf, fp);
+}
+
+static size_t union__fprintf(struct type *type, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp)
+{
+ struct class_member *pos;
+ size_t printed = 0;
+ int indent = conf->indent;
+ struct conf_fprintf uconf;
+ uint32_t initial_union_cacheline;
+ uint32_t cacheline = 0; /* This will only be used if this is the outermost union */
+
+ if (indent >= (int)sizeof(tabs))
+ indent = sizeof(tabs) - 1;
+
+ if (conf->prefix != NULL)
+ printed += fprintf(fp, "%s ", conf->prefix);
+ printed += fprintf(fp, "union%s%s {\n", type__name(type, cu) ? " " : "",
+ type__name(type, cu) ?: "");
+
+ uconf = *conf;
+ uconf.indent = indent + 1;
+
+ /*
+ * If structs embedded in unions, nameless or not, have a size which isn't
+ * isn't a multiple of the union size, then it must be packed, even if
+ * it has no holes nor padding, as an array of such unions would have the
+ * natural alignments of non-multiple structs inside it broken.
+ */
+ union__infer_packed_attributes(type, cu);
+
+ /*
+ * We may be called directly or from tag__fprintf, so keep sure
+ * we keep track of the cacheline we're in.
+ *
+ * If we're being called from an outer structure, i.e. union within
+ * struct, class or another union, then this will already have a
+ * value and we'll continue to use it.
+ */
+ if (uconf.cachelinep == NULL)
+ uconf.cachelinep = &cacheline;
+ /*
+ * Save the cacheline we're in, then, after each union member, get
+ * back to it. Else we'll end up showing cacheline boundaries in
+ * just the first of a multi struct union, for instance.
+ */
+ initial_union_cacheline = *uconf.cachelinep;
+ type__for_each_member(type, pos) {
+ struct tag *pos_type = cu__type(cu, pos->tag.type);
+
+ if (pos_type == NULL) {
+ printed += fprintf(fp, "%.*s", uconf.indent, tabs);
+ printed += tag__id_not_found_fprintf(fp, pos->tag.type);
+ continue;
+ }
+
+ uconf.union_member = 1;
+ printed += fprintf(fp, "%.*s", uconf.indent, tabs);
+ printed += union_member__fprintf(pos, pos_type, cu, &uconf, fp);
+ fputc('\n', fp);
+ ++printed;
+ *uconf.cachelinep = initial_union_cacheline;
+ }
+
+ return printed + fprintf(fp, "%.*s}%s%s", indent, tabs,
+ conf->suffix ? " " : "", conf->suffix ?: "");
+}
+
+const char *function__prototype(const struct function *func,
+ const struct cu *cu, char *bf, size_t len)
+{
+ FILE *bfp = fmemopen(bf, len, "w");
+
+ if (bfp != NULL) {
+ ftype__fprintf(&func->proto, cu, NULL, 0, 0, 0, true,
+ &conf_fprintf__defaults, bfp);
+ fclose(bfp);
+ } else
+ snprintf(bf, len, "<ERROR(%s): fmemopen failed!>", __func__);
+
+ return bf;
+}
+
+size_t ftype__fprintf_parms(const struct ftype *ftype,
+ const struct cu *cu, int indent,
+ const struct conf_fprintf *conf, FILE *fp)
+{
+ struct parameter *pos;
+ int first_parm = 1;
+ char sbf[128];
+ struct tag *type;
+ const char *name, *stype;
+ size_t printed = fprintf(fp, "(");
+
+ ftype__for_each_parameter(ftype, pos) {
+ if (!first_parm) {
+ if (indent == 0)
+ printed += fprintf(fp, ", ");
+ else
+ printed += fprintf(fp, ",\n%.*s",
+ indent, tabs);
+ } else
+ first_parm = 0;
+ name = conf->no_parm_names ? NULL : parameter__name(pos, cu);
+ type = cu__type(cu, pos->tag.type);
+ if (type == NULL) {
+ snprintf(sbf, sizeof(sbf),
+ "<ERROR: type %d not found>", pos->tag.type);
+ stype = sbf;
+ goto print_it;
+ }
+ if (tag__is_pointer(type)) {
+ if (type->type != 0) {
+ int n;
+ struct tag *ptype = cu__type(cu, type->type);
+ if (ptype == NULL) {
+ printed +=
+ tag__id_not_found_fprintf(fp, type->type);
+ continue;
+ }
+ n = tag__has_type_loop(type, ptype, NULL, 0, fp);
+ if (n)
+ return printed + n;
+ if (ptype->tag == DW_TAG_subroutine_type) {
+ printed +=
+ ftype__fprintf(tag__ftype(ptype),
+ cu, name, 0, 1, 0,
+ true, conf, fp);
+ continue;
+ }
+ }
+ } else if (type->tag == DW_TAG_subroutine_type) {
+ printed += ftype__fprintf(tag__ftype(type), cu, name,
+ true, 0, 0, 0, conf, fp);
+ continue;
+ }
+ stype = tag__name(type, cu, sbf, sizeof(sbf), conf);
+print_it:
+ printed += fprintf(fp, "%s%s%s", stype, name ? " " : "",
+ name ?: "");
+ }
+
+ /* No parameters? */
+ if (first_parm)
+ printed += fprintf(fp, "void)");
+ else if (ftype->unspec_parms)
+ printed += fprintf(fp, ", ...)");
+ else
+ printed += fprintf(fp, ")");
+ return printed;
+}
+
+static size_t function__tag_fprintf(const struct tag *tag, const struct cu *cu,
+ struct function *function, uint16_t indent,
+ const struct conf_fprintf *conf, FILE *fp)
+{
+ char bf[512];
+ size_t printed = 0, n;
+ const void *vtag = tag;
+ int c;
+
+ if (indent >= sizeof(tabs))
+ indent = sizeof(tabs) - 1;
+ c = indent * 8;
+
+ switch (tag->tag) {
+ case DW_TAG_inlined_subroutine: {
+ const struct inline_expansion *exp = vtag;
+ const struct tag *talias = cu__function(cu, exp->ip.tag.type);
+ struct function *alias = tag__function(talias);
+ const char *name;
+
+ if (alias == NULL) {
+ printed += tag__id_not_found_fprintf(fp, exp->ip.tag.type);
+ break;
+ }
+ printed = fprintf(fp, "%.*s", indent, tabs);
+ name = function__name(alias, cu);
+ n = fprintf(fp, "%s", name);
+ size_t namelen = 0;
+ if (name != NULL)
+ namelen = strlen(name);
+ n += ftype__fprintf_parms(&alias->proto, cu,
+ indent + (namelen + 7) / 8,
+ conf, fp);
+ n += fprintf(fp, "; /* size=%zd, low_pc=%#llx */",
+ exp->size, (unsigned long long)exp->ip.addr);
+#if 0
+ n = fprintf(fp, "%s(); /* size=%zd, low_pc=%#llx */",
+ function__name(alias, cu), exp->size,
+ (unsigned long long)exp->ip.addr);
+#endif
+ c = 69;
+ printed += n;
+ }
+ break;
+ case DW_TAG_variable:
+ printed = fprintf(fp, "%.*s", indent, tabs);
+ n = fprintf(fp, "%s %s; /* scope: %s */",
+ variable__type_name(vtag, cu, bf, sizeof(bf)),
+ variable__name(vtag, cu),
+ variable__scope_str(vtag));
+ c += n;
+ printed += n;
+ break;
+ case DW_TAG_label: {
+ const struct label *label = vtag;
+ printed = fprintf(fp, "%.*s", indent, tabs);
+ fputc('\n', fp);
+ ++printed;
+ c = fprintf(fp, "%s:", label__name(label, cu));
+ printed += c;
+ }
+ break;
+ case DW_TAG_lexical_block:
+ printed = lexblock__fprintf(vtag, cu, function, indent,
+ conf, fp);
+ fputc('\n', fp);
+ return printed + 1;
+ default:
+ printed = fprintf(fp, "%.*s", indent, tabs);
+ n = fprintf(fp, "%s <%llx>", dwarf_tag_name(tag->tag),
+ tag__orig_id(tag, cu));
+ c += n;
+ printed += n;
+ break;
+ }
+ return printed + fprintf(fp, "%-*.*s// %5u\n", 70 - c, 70 - c, " ",
+ tag__decl_line(tag, cu));
+}
+
+size_t lexblock__fprintf(const struct lexblock *block, const struct cu *cu,
+ struct function *function, uint16_t indent,
+ const struct conf_fprintf *conf, FILE *fp)
+{
+ struct tag *pos;
+ size_t printed;
+
+ if (indent >= sizeof(tabs))
+ indent = sizeof(tabs) - 1;
+ printed = fprintf(fp, "%.*s{", indent, tabs);
+ if (block->ip.addr != 0) {
+ uint64_t offset = block->ip.addr - function->lexblock.ip.addr;
+
+ if (offset == 0)
+ printed += fprintf(fp, " /* low_pc=%#llx */",
+ (unsigned long long)block->ip.addr);
+ else
+ printed += fprintf(fp, " /* %s+%#llx */",
+ function__name(function, cu),
+ (unsigned long long)offset);
+ }
+ printed += fprintf(fp, "\n");
+ list_for_each_entry(pos, &block->tags, node)
+ printed += function__tag_fprintf(pos, cu, function, indent + 1,
+ conf, fp);
+ printed += fprintf(fp, "%.*s}", indent, tabs);
+
+ if (function->lexblock.ip.addr != block->ip.addr)
+ printed += fprintf(fp, " /* lexblock size=%d */", block->size);
+
+ return printed;
+}
+
+size_t ftype__fprintf(const struct ftype *ftype, const struct cu *cu,
+ const char *name, const int inlined,
+ const int is_pointer, int type_spacing, bool is_prototype,
+ const struct conf_fprintf *conf, FILE *fp)
+{
+ struct tag *type = cu__type(cu, ftype->tag.type);
+ char sbf[128];
+ const char *stype = tag__name(type, cu, sbf, sizeof(sbf), conf);
+ size_t printed = fprintf(fp, "%s%-*s %s%s%s%s",
+ inlined ? "inline " : "",
+ type_spacing, stype,
+ is_prototype ? "(" : "",
+ is_pointer ? "*" : "", name ?: "",
+ is_prototype ? ")" : "");
+
+ return printed + ftype__fprintf_parms(ftype, cu, 0, conf, fp);
+}
+
+static size_t function__fprintf(const struct tag *tag, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp)
+{
+ struct function *func = tag__function(tag);
+ struct ftype *ftype = func->btf ? tag__ftype(cu__type(cu, func->proto.tag.type)) : &func->proto;
+ size_t printed = 0;
+ bool inlined = !conf->strip_inline && function__declared_inline(func);
+
+ if (func->virtuality == DW_VIRTUALITY_virtual ||
+ func->virtuality == DW_VIRTUALITY_pure_virtual)
+ printed += fprintf(fp, "virtual ");
+
+ printed += ftype__fprintf(ftype, cu, function__name(func, cu),
+ inlined, 0, 0, false, conf, fp);
+
+ if (func->virtuality == DW_VIRTUALITY_pure_virtual)
+ printed += fprintf(fp, " = 0");
+
+ return printed;
+}
+
+size_t function__fprintf_stats(const struct tag *tag, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp)
+{
+ struct function *func = tag__function(tag);
+ size_t printed = lexblock__fprintf(&func->lexblock, cu, func, 0, conf, fp);
+
+ printed += fprintf(fp, "/* size: %d", function__size(func));
+ if (func->lexblock.nr_variables > 0)
+ printed += fprintf(fp, ", variables: %u",
+ func->lexblock.nr_variables);
+ if (func->lexblock.nr_labels > 0)
+ printed += fprintf(fp, ", goto labels: %u",
+ func->lexblock.nr_labels);
+ if (func->lexblock.nr_inline_expansions > 0)
+ printed += fprintf(fp, ", inline expansions: %u (%d bytes)",
+ func->lexblock.nr_inline_expansions,
+ func->lexblock.size_inline_expansions);
+ return printed + fprintf(fp, " */\n");
+}
+
+static size_t class__fprintf_cacheline_boundary(struct conf_fprintf *conf,
+ uint32_t offset,
+ FILE *fp)
+{
+ int indent = conf->indent;
+ uint32_t cacheline = offset / cacheline_size;
+ size_t printed = 0;
+
+ if (cacheline > *conf->cachelinep) {
+ const uint32_t cacheline_pos = offset % cacheline_size;
+ const uint32_t cacheline_in_bytes = offset - cacheline_pos;
+
+ if (cacheline_pos == 0)
+ printed += fprintf(fp, "/* --- cacheline %u boundary "
+ "(%u bytes) --- */\n", cacheline,
+ cacheline_in_bytes);
+ else
+ printed += fprintf(fp, "/* --- cacheline %u boundary "
+ "(%u bytes) was %u bytes ago --- "
+ "*/\n", cacheline,
+ cacheline_in_bytes, cacheline_pos);
+
+ printed += fprintf(fp, "%.*s", indent, tabs);
+
+ *conf->cachelinep = cacheline;
+ }
+ return printed;
+}
+
+static size_t class__vtable_fprintf(struct class *class, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp)
+{
+ struct function *pos;
+ size_t printed = 0;
+
+ if (class->nr_vtable_entries == 0)
+ goto out;
+
+ printed += fprintf(fp, "%.*s/* vtable has %u entries: {\n",
+ conf->indent, tabs, class->nr_vtable_entries);
+
+ list_for_each_entry(pos, &class->vtable, vtable_node) {
+ printed += fprintf(fp, "%.*s [%d] = %s(%s), \n",
+ conf->indent, tabs, pos->vtable_entry,
+ function__name(pos, cu),
+ function__linkage_name(pos, cu));
+ }
+
+ printed += fprintf(fp, "%.*s} */", conf->indent, tabs);
+out:
+ return printed;
+}
+
+static size_t __class__fprintf(struct class *class, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp)
+{
+ struct type *type = &class->type;
+ size_t last_size = 0, size;
+ uint8_t newline = 0;
+ uint16_t nr_paddings = 0;
+ uint16_t nr_forced_alignments = 0, nr_forced_alignment_holes = 0;
+ uint32_t sum_forced_alignment_holes = 0;
+ uint32_t sum_bytes = 0, sum_bits = 0;
+ uint32_t sum_holes = 0;
+ uint32_t sum_paddings = 0;
+ uint32_t sum_bit_holes = 0;
+ uint32_t cacheline = 0;
+ int size_diff = 0;
+ int first = 1;
+ struct class_member *pos, *last = NULL;
+ struct tag *tag_pos;
+ const char *current_accessibility = NULL;
+ struct conf_fprintf cconf = conf ? *conf : conf_fprintf__defaults;
+ const uint16_t t = type->namespace.tag.tag;
+ size_t printed = fprintf(fp, "%s%s%s%s%s",
+ cconf.prefix ?: "", cconf.prefix ? " " : "",
+ ((cconf.classes_as_structs ||
+ t == DW_TAG_structure_type) ? "struct" :
+ t == DW_TAG_class_type ? "class" :
+ "interface"),
+ type__name(type, cu) ? " " : "",
+ type__name(type, cu) ?: "");
+ int indent = cconf.indent;
+
+ if (indent >= (int)sizeof(tabs))
+ indent = sizeof(tabs) - 1;
+
+ if (cconf.cachelinep == NULL)
+ cconf.cachelinep = &cacheline;
+
+ cconf.indent = indent + 1;
+ cconf.no_semicolon = 0;
+
+ class__infer_packed_attributes(class, cu);
+
+ /* First look if we have DW_TAG_inheritance */
+ type__for_each_tag(type, tag_pos) {
+ const char *accessibility;
+
+ if (tag_pos->tag != DW_TAG_inheritance)
+ continue;
+
+ if (first) {
+ printed += fprintf(fp, " :");
+ first = 0;
+ } else
+ printed += fprintf(fp, ",");
+
+ pos = tag__class_member(tag_pos);
+
+ if (pos->virtuality == DW_VIRTUALITY_virtual)
+ printed += fprintf(fp, " virtual");
+
+ accessibility = tag__accessibility(tag_pos);
+ if (accessibility != NULL)
+ printed += fprintf(fp, " %s", accessibility);
+
+ struct tag *pos_type = cu__type(cu, tag_pos->type);
+ if (pos_type != NULL)
+ printed += fprintf(fp, " %s",
+ type__name(tag__type(pos_type), cu));
+ else
+ printed += tag__id_not_found_fprintf(fp, tag_pos->type);
+ }
+
+ printed += fprintf(fp, " {\n");
+
+ if (class->pre_bit_hole > 0 && !cconf.suppress_comments) {
+ if (!newline++) {
+ fputc('\n', fp);
+ ++printed;
+ }
+ printed += fprintf(fp, "%.*s/* XXX %d bit%s hole, "
+ "try to pack */\n", cconf.indent, tabs,
+ class->pre_bit_hole,
+ class->pre_bit_hole != 1 ? "s" : "");
+ sum_bit_holes += class->pre_bit_hole;
+ }
+
+ if (class->pre_hole > 0 && !cconf.suppress_comments) {
+ if (!newline++) {
+ fputc('\n', fp);
+ ++printed;
+ }
+ printed += fprintf(fp, "%.*s/* XXX %d byte%s hole, "
+ "try to pack */\n",
+ cconf.indent, tabs, class->pre_hole,
+ class->pre_hole != 1 ? "s" : "");
+ sum_holes += class->pre_hole;
+ }
+
+ type__for_each_tag(type, tag_pos) {
+ const char *accessibility = tag__accessibility(tag_pos);
+
+ if (accessibility != NULL &&
+ accessibility != current_accessibility) {
+ current_accessibility = accessibility;
+ printed += fprintf(fp, "%.*s%s:\n\n",
+ cconf.indent - 1, tabs,
+ accessibility);
+ }
+
+ if (tag_pos->tag != DW_TAG_member &&
+ tag_pos->tag != DW_TAG_inheritance) {
+ if (!cconf.show_only_data_members) {
+ printed += tag__fprintf(tag_pos, cu, &cconf, fp);
+ printed += fprintf(fp, "\n\n");
+ }
+ continue;
+ }
+ pos = tag__class_member(tag_pos);
+
+ if (!cconf.suppress_aligned_attribute && pos->alignment != 0) {
+ uint32_t forced_alignment_hole = last ? last->hole : class->pre_hole;
+
+ if (forced_alignment_hole != 0) {
+ ++nr_forced_alignment_holes;
+ sum_forced_alignment_holes += forced_alignment_hole;
+ }
+ ++nr_forced_alignments;
+ }
+ /*
+ * These paranoid checks doesn't make much sense on
+ * DW_TAG_inheritance, have to understand why virtual public
+ * ancestors make the offset go backwards...
+ */
+ if (last != NULL && tag_pos->tag == DW_TAG_member &&
+ /*
+ * kmemcheck bitfield tricks use zero sized arrays as markers
+ * all over the place.
+ */
+ last_size != 0) {
+ if (last->bit_hole != 0 && pos->bitfield_size) {
+ uint8_t bitfield_size = last->bit_hole;
+ struct tag *pos_type = cu__type(cu, pos->tag.type);
+
+ if (pos_type == NULL) {
+ printed += fprintf(fp, "%.*s", cconf.indent, tabs);
+ printed += tag__id_not_found_fprintf(fp, pos->tag.type);
+ continue;
+ }
+ /*
+ * Now check if this isn't something like 'unsigned :N' with N > 0,
+ * i.e. _explicitely_ adding a bit hole.
+ */
+ if (last->byte_offset != pos->byte_offset) {
+ printed += fprintf(fp, "\n%.*s/* Force alignment to the next boundary: */\n", cconf.indent, tabs);
+ bitfield_size = 0;
+ }
+
+ printed += fprintf(fp, "%.*s", cconf.indent, tabs);
+ printed += type__fprintf(pos_type, cu, "", &cconf, fp);
+ printed += fprintf(fp, ":%u;\n", bitfield_size);
+ }
+
+ if (pos->byte_offset < last->byte_offset ||
+ (pos->byte_offset == last->byte_offset &&
+ last->bitfield_size == 0 &&
+ /*
+ * This is just when transitioning from a non-bitfield to
+ * a bitfield, think about zero sized arrays in the middle
+ * of a struct.
+ */
+ pos->bitfield_size != 0)) {
+ if (!cconf.suppress_comments) {
+ if (!newline++) {
+ fputc('\n', fp);
+ ++printed;
+ }
+ printed += fprintf(fp, "%.*s/* Bitfield combined"
+ " with previous fields */\n",
+ cconf.indent, tabs);
+ }
+ } else {
+ const ssize_t cc_last_size = ((ssize_t)pos->byte_offset -
+ (ssize_t)last->byte_offset);
+
+ if (cc_last_size > 0 &&
+ (size_t)cc_last_size < last_size) {
+ if (!cconf.suppress_comments) {
+ if (!newline++) {
+ fputc('\n', fp);
+ ++printed;
+ }
+ printed += fprintf(fp, "%.*s/* Bitfield combined"
+ " with next fields */\n",
+ cconf.indent, tabs);
+ }
+ }
+ }
+ }
+
+ if (newline) {
+ fputc('\n', fp);
+ newline = 0;
+ ++printed;
+ }
+
+ struct tag *pos_type = cu__type(cu, pos->tag.type);
+ if (pos_type == NULL) {
+ printed += fprintf(fp, "%.*s", cconf.indent, tabs);
+ printed += tag__id_not_found_fprintf(fp, pos->tag.type);
+ continue;
+ }
+
+ cconf.last_member = list_is_last(&tag_pos->node, &type->namespace.tags);
+ cconf.first_member = last == NULL;
+
+ size = pos->byte_size;
+ printed += fprintf(fp, "%.*s", cconf.indent, tabs);
+ printed += struct_member__fprintf(pos, pos_type, cu, &cconf, fp);
+
+ if (tag__is_struct(pos_type) && !cconf.suppress_comments) {
+ struct class *tclass = tag__class(pos_type);
+ uint16_t padding;
+ /*
+ * We may not yet have looked for holes and paddings
+ * in this member's struct type.
+ */
+ class__find_holes(tclass);
+ class__infer_packed_attributes(tclass, cu);
+
+ padding = tclass->padding;
+ if (padding > 0) {
+ ++nr_paddings;
+ sum_paddings += padding;
+ if (!newline++) {
+ fputc('\n', fp);
+ ++printed;
+ }
+
+ printed += fprintf(fp, "\n%.*s/* XXX last "
+ "struct has %d byte%s of "
+ "padding */", cconf.indent,
+ tabs, padding,
+ padding != 1 ? "s" : "");
+ }
+ }
+
+ if (pos->bit_hole != 0 && !cconf.suppress_comments) {
+ if (!newline++) {
+ fputc('\n', fp);
+ ++printed;
+ }
+ printed += fprintf(fp, "\n%.*s/* XXX %d bit%s hole, "
+ "try to pack */", cconf.indent, tabs,
+ pos->bit_hole,
+ pos->bit_hole != 1 ? "s" : "");
+ sum_bit_holes += pos->bit_hole;
+ }
+
+ if (pos->hole > 0 && !cconf.suppress_comments) {
+ if (!newline++) {
+ fputc('\n', fp);
+ ++printed;
+ }
+ printed += fprintf(fp, "\n%.*s/* XXX %d byte%s hole, "
+ "try to pack */",
+ cconf.indent, tabs, pos->hole,
+ pos->hole != 1 ? "s" : "");
+ sum_holes += pos->hole;
+ }
+
+ fputc('\n', fp);
+ ++printed;
+
+ /* XXX for now just skip these */
+ if (tag_pos->tag == DW_TAG_inheritance)
+ continue;
+#if 0
+ /*
+ * This one was being skipped but caused problems with:
+ * http://article.gmane.org/gmane.comp.debugging.dwarves/185
+ * http://www.spinics.net/lists/dwarves/msg00119.html
+ */
+ if (pos->virtuality == DW_VIRTUALITY_virtual)
+ continue;
+#endif
+
+ if (pos->bitfield_size) {
+ sum_bits += pos->bitfield_size;
+ } else {
+ sum_bytes += pos->byte_size;
+ }
+
+ if (last == NULL || /* First member */
+ /*
+ * Last member was a zero sized array, typedef, struct, etc
+ */
+ last_size == 0 ||
+ /*
+ * We moved to a new offset
+ */
+ last->byte_offset != pos->byte_offset) {
+ last_size = size;
+ } else if (last->bitfield_size == 0 && pos->bitfield_size != 0) {
+ /*
+ * Transitioned from from a non-bitfield to a
+ * bitfield sharing the same offset
+ */
+ /*
+ * Compensate by removing the size of the
+ * last member that is "inside" this new
+ * member at the same offset.
+ *
+ * E.g.:
+ * struct foo {
+ * u8 a; / 0 1 /
+ * int b:1; / 0:23 4 /
+ * }
+ */
+ last_size = size;
+ }
+
+ last = pos;
+ }
+
+ /*
+ * BTF doesn't have alignment info, for now use this infor from the loader
+ * to avoid adding the forced bitfield paddings and have btfdiff happy.
+ */
+ if (class->padding != 0 && type->alignment == 0 && cconf.has_alignment_info &&
+ !cconf.suppress_force_paddings && last != NULL) {
+ tag_pos = cu__type(cu, last->tag.type);
+ size = tag__size(tag_pos, cu);
+
+ if (is_power_of_2(size) && class->padding > cu->addr_size) {
+ int added_padding;
+ int bit_size = size * 8;
+
+ printed += fprintf(fp, "\n%.*s/* Force padding: */\n", cconf.indent, tabs);
+
+ for (added_padding = 0; added_padding < class->padding; added_padding += size) {
+ printed += fprintf(fp, "%.*s", cconf.indent, tabs);
+ printed += type__fprintf(tag_pos, cu, "", &cconf, fp);
+ printed += fprintf(fp, ":%u;\n", bit_size);
+ }
+ }
+ }
+
+ if (!cconf.show_only_data_members)
+ class__vtable_fprintf(class, cu, &cconf, fp);
+
+ if (!cconf.emit_stats)
+ goto out;
+
+ printed += type__fprintf_stats(type, cu, &cconf, fp);
+
+ if (sum_holes > 0 || sum_bit_holes > 0) {
+ if (sum_bytes > 0) {
+ printed += fprintf(fp, "%.*s/* sum members: %u",
+ cconf.indent, tabs, sum_bytes);
+ if (sum_holes > 0)
+ printed += fprintf(fp, ", holes: %d, sum holes: %u",
+ class->nr_holes, sum_holes);
+ printed += fprintf(fp, " */\n");
+ }
+ if (sum_bits > 0) {
+ printed += fprintf(fp, "%.*s/* sum bitfield members: %u bits",
+ cconf.indent, tabs, sum_bits);
+ if (sum_bit_holes > 0)
+ printed += fprintf(fp, ", bit holes: %d, sum bit holes: %u bits",
+ class->nr_bit_holes, sum_bit_holes);
+ else
+ printed += fprintf(fp, " (%u bytes)", sum_bits / 8);
+ printed += fprintf(fp, " */\n");
+ }
+ }
+ if (class->padding > 0)
+ printed += fprintf(fp, "%.*s/* padding: %u */\n",
+ cconf.indent,
+ tabs, class->padding);
+ if (nr_paddings > 0)
+ printed += fprintf(fp, "%.*s/* paddings: %u, sum paddings: "
+ "%u */\n",
+ cconf.indent, tabs,
+ nr_paddings, sum_paddings);
+ if (class->bit_padding > 0)
+ printed += fprintf(fp, "%.*s/* bit_padding: %u bits */\n",
+ cconf.indent, tabs,
+ class->bit_padding);
+ if (!cconf.suppress_aligned_attribute && nr_forced_alignments != 0) {
+ printed += fprintf(fp, "%.*s/* forced alignments: %u",
+ cconf.indent, tabs,
+ nr_forced_alignments);
+ if (nr_forced_alignment_holes != 0) {
+ printed += fprintf(fp, ", forced holes: %u, sum forced holes: %u",
+ nr_forced_alignment_holes,
+ sum_forced_alignment_holes);
+ }
+ printed += fprintf(fp, " */\n");
+ }
+ cacheline = (cconf.base_offset + type->size) % cacheline_size;
+ if (cacheline != 0)
+ printed += fprintf(fp, "%.*s/* last cacheline: %u bytes */\n",
+ cconf.indent, tabs,
+ cacheline);
+ if (cconf.show_first_biggest_size_base_type_member &&
+ type->nr_members != 0) {
+ struct class_member *m = type__find_first_biggest_size_base_type_member(type, cu);
+
+ printed += fprintf(fp, "%.*s/* first biggest size base type member: %s %u %zd */\n",
+ cconf.indent, tabs,
+ class_member__name(m, cu), m->byte_offset,
+ m->byte_size);
+ }
+
+ size_diff = type->size * 8 - (sum_bytes * 8 + sum_bits + sum_holes * 8 + sum_bit_holes +
+ class->padding * 8 + class->bit_padding);
+ if (size_diff && type->nr_members != 0)
+ printed += fprintf(fp, "\n%.*s/* BRAIN FART ALERT! %d bytes != "
+ "%u (member bytes) + %u (member bits) "
+ "+ %u (byte holes) + %u (bit holes), diff = %d bits */\n",
+ cconf.indent, tabs,
+ type->size, sum_bytes, sum_bits, sum_holes, sum_bit_holes, size_diff);
+out:
+ printed += fprintf(fp, "%.*s}", indent, tabs);
+
+ if (class->is_packed && !cconf.suppress_packed)
+ printed += fprintf(fp, " __attribute__((__packed__))");
+
+ if (cconf.suffix)
+ printed += fprintf(fp, " %s", cconf.suffix);
+
+ /*
+ * A class that was marked packed by class__infer_packed_attributes
+ * because it has an alignment that is different than its natural
+ * alignment, should not print the __alignment__ here, just the
+ * __packed__ attribute.
+ */
+ if (!cconf.suppress_aligned_attribute && type->alignment != 0 && !class->is_packed)
+ printed += fprintf(fp, " __attribute__((__aligned__(%u)))", type->alignment);
+
+ return printed;
+}
+
+size_t class__fprintf(struct class *class, const struct cu *cu, FILE *fp)
+{
+ return __class__fprintf(class, cu, NULL, fp);
+}
+
+static size_t variable__fprintf(const struct tag *tag, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp)
+{
+ const struct variable *var = tag__variable(tag);
+ const char *name = variable__name(var, cu);
+ size_t printed = 0;
+
+ if (name != NULL) {
+ struct tag *type = cu__type(cu, var->ip.tag.type);
+ if (type != NULL) {
+ const char *varprefix = variable__prefix(var);
+
+ if (varprefix != NULL)
+ printed += fprintf(fp, "%s", varprefix);
+ printed += type__fprintf(type, cu, name, conf, fp);
+ }
+ }
+ return printed;
+}
+
+static size_t namespace__fprintf(const struct tag *tag, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp)
+{
+ struct namespace *space = tag__namespace(tag);
+ struct conf_fprintf cconf = *conf;
+ size_t printed = fprintf(fp, "namespace %s {\n",
+ namespace__name(space, cu));
+ struct tag *pos;
+
+ ++cconf.indent;
+ cconf.no_semicolon = 0;
+
+ namespace__for_each_tag(space, pos) {
+ printed += tag__fprintf(pos, cu, &cconf, fp);
+ printed += fprintf(fp, "\n\n");
+ }
+
+ return printed + fprintf(fp, "}");
+}
+
+size_t tag__fprintf(struct tag *tag, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp)
+{
+ size_t printed = 0;
+ struct conf_fprintf tconf;
+ const struct conf_fprintf *pconf = conf;
+
+ if (conf == NULL) {
+ tconf = conf_fprintf__defaults;
+ pconf = &tconf;
+
+ if (tconf.expand_types)
+ tconf.name_spacing = 55;
+ else if (tag__is_union(tag))
+ tconf.name_spacing = 21;
+ } else if (conf->name_spacing == 0 || conf->type_spacing == 0) {
+ tconf = *conf;
+ pconf = &tconf;
+
+ if (tconf.name_spacing == 0) {
+ if (tconf.expand_types)
+ tconf.name_spacing = 55;
+ else
+ tconf.name_spacing = tag__is_union(tag) ? 21 : 23;
+ }
+ if (tconf.type_spacing == 0)
+ tconf.type_spacing = 26;
+ }
+
+ if (pconf->expand_types)
+ ++tag->recursivity_level;
+
+ if (pconf->show_decl_info) {
+ printed += fprintf(fp, "%.*s", pconf->indent, tabs);
+ printed += fprintf(fp, "/* Used at: %s */\n", cu->name);
+ printed += fprintf(fp, "%.*s", pconf->indent, tabs);
+ printed += tag__fprintf_decl_info(tag, cu, fp);
+ }
+ printed += fprintf(fp, "%.*s", pconf->indent, tabs);
+
+ switch (tag->tag) {
+ case DW_TAG_array_type:
+ printed += array_type__fprintf(tag, cu, "array", pconf, fp);
+ break;
+ case DW_TAG_enumeration_type:
+ printed += enumeration__fprintf(tag, cu, pconf, fp);
+ break;
+ case DW_TAG_typedef:
+ printed += typedef__fprintf(tag, cu, pconf, fp);
+ break;
+ case DW_TAG_class_type:
+ case DW_TAG_interface_type:
+ case DW_TAG_structure_type:
+ printed += __class__fprintf(tag__class(tag), cu, pconf, fp);
+ break;
+ case DW_TAG_subroutine_type:
+ printed += ftype__fprintf(tag__ftype(tag), cu, NULL, false, false, 0, true, pconf, fp);
+ break;
+ case DW_TAG_namespace:
+ printed += namespace__fprintf(tag, cu, pconf, fp);
+ break;
+ case DW_TAG_subprogram:
+ printed += function__fprintf(tag, cu, pconf, fp);
+ break;
+ case DW_TAG_union_type:
+ printed += union__fprintf(tag__type(tag), cu, pconf, fp);
+ break;
+ case DW_TAG_variable:
+ printed += variable__fprintf(tag, cu, pconf, fp);
+ break;
+ case DW_TAG_imported_declaration:
+ printed += imported_declaration__fprintf(tag, cu, fp);
+ break;
+ case DW_TAG_imported_module:
+ printed += imported_module__fprintf(tag, cu, fp);
+ break;
+ default:
+ printed += fprintf(fp, "/* %s: %s tag not supported! */",
+ __func__, dwarf_tag_name(tag->tag));
+ break;
+ }
+
+ if (!pconf->no_semicolon) {
+ fputc(';', fp);
+ ++printed;
+ }
+
+ if (tag__is_function(tag) && !pconf->suppress_comments) {
+ const struct function *func = tag__function(tag);
+
+ if (func->linkage_name)
+ printed += fprintf(fp, " /* linkage=%s */",
+ function__linkage_name(func, cu));
+ }
+
+ if (pconf->expand_types)
+ --tag->recursivity_level;
+
+ return printed;
+}
+
+void cus__print_error_msg(const char *progname, const struct cus *cus,
+ const char *filename, const int err)
+{
+ if (err == -EINVAL || (cus != NULL && list_empty(&cus->cus)))
+ fprintf(stderr, "%s: couldn't load debugging info from %s\n",
+ progname, filename);
+ else
+ fprintf(stderr, "%s: %s\n", progname, strerror(err));
+}
+
+void dwarves__fprintf_init(uint16_t user_cacheline_size)
+{
+ if (user_cacheline_size == 0) {
+ long sys_cacheline_size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
+
+ if (sys_cacheline_size > 0)
+ cacheline_size = sys_cacheline_size;
+ else
+ cacheline_size = 64; /* Fall back to a sane value */
+ } else
+ cacheline_size = user_cacheline_size;
+}
diff --git a/dwarves_reorganize.c b/dwarves_reorganize.c
new file mode 100644
index 0000000..bae5b6e
--- /dev/null
+++ b/dwarves_reorganize.c
@@ -0,0 +1,848 @@
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2006 Mandriva Conectiva S.A.
+ Copyright (C) 2006 Arnaldo Carvalho de Melo <acme@mandriva.com>
+ Copyright (C) 2007 Red Hat Inc.
+ Copyright (C) 2007 Arnaldo Carvalho de Melo <acme@redhat.com>
+*/
+
+#include "list.h"
+#include "dwarves_reorganize.h"
+#include "dwarves.h"
+
+static void class__recalc_holes(struct class *class)
+{
+ class->holes_searched = 0;
+ class__find_holes(class);
+}
+
+void class__subtract_offsets_from(struct class *class,
+ struct class_member *from,
+ const uint16_t size)
+{
+ struct class_member *member;
+
+ class__for_each_member_continue(class, from, member) {
+ member->byte_offset -= size;
+ member->bit_offset -= size * 8;
+ }
+
+ if (class->padding != 0) {
+ struct class_member *last_member =
+ type__last_member(&class->type);
+ const ssize_t new_padding = (class__size(class) -
+ (last_member->byte_offset +
+ last_member->byte_size));
+ if (new_padding > 0)
+ class->padding = new_padding;
+ else
+ class->padding = 0;
+ }
+}
+
+void class__add_offsets_from(struct class *class, struct class_member *from,
+ const uint16_t size)
+{
+ struct class_member *member;
+
+ class__for_each_member_continue(class, from, member) {
+ member->byte_offset += size;
+ member->bit_offset += size * 8;
+ }
+}
+
+/*
+ * XXX: Check this more thoroughly. Right now it is used because I was
+ * to lazy to do class__remove_member properly, adjusting alignments and
+ * holes as we go removing fields. Ditto for class__add_offsets_from.
+ */
+void class__fixup_alignment(struct class *class, const struct cu *cu)
+{
+ struct class_member *pos, *last_member = NULL;
+ size_t power2;
+
+ type__for_each_data_member(&class->type, pos) {
+ if (last_member == NULL && pos->byte_offset != 0) { /* paranoid! */
+ class__subtract_offsets_from(class, pos,
+ (pos->byte_offset -
+ pos->byte_size));
+ pos->byte_offset = 0;
+ pos->bit_offset = 0;
+ } else if (last_member != NULL &&
+ last_member->hole >= cu->addr_size) {
+ size_t dec = (last_member->hole / cu->addr_size) *
+ cu->addr_size;
+
+ last_member->hole -= dec;
+ if (last_member->hole == 0)
+ --class->nr_holes;
+ pos->byte_offset -= dec;
+ pos->bit_offset -= dec * 8;
+ class->type.size -= dec;
+ class__subtract_offsets_from(class, pos, dec);
+ } else for (power2 = cu->addr_size; power2 >= 2; power2 /= 2) {
+ const size_t remainder = pos->byte_offset % power2;
+
+ if (pos->byte_size == power2) {
+ if (remainder == 0) /* perfectly aligned */
+ break;
+ if (last_member->hole >= remainder) {
+ last_member->hole -= remainder;
+ if (last_member->hole == 0)
+ --class->nr_holes;
+ pos->byte_offset -= remainder;
+ pos->bit_offset -= remainder * 8;
+ class__subtract_offsets_from(class, pos, remainder);
+ } else {
+ const size_t inc = power2 - remainder;
+
+ if (last_member->hole == 0)
+ ++class->nr_holes;
+ last_member->hole += inc;
+ pos->byte_offset += inc;
+ pos->bit_offset += inc * 8;
+ class->type.size += inc;
+ class__add_offsets_from(class, pos, inc);
+ }
+ }
+ }
+
+ last_member = pos;
+ }
+
+ if (last_member != NULL) {
+ struct class_member *m =
+ type__find_first_biggest_size_base_type_member(&class->type, cu);
+ size_t unpadded_size = last_member->byte_offset + last_member->byte_size;
+ size_t m_size = m->byte_size, remainder;
+
+ /* google for struct zone_padding in the linux kernel for an example */
+ if (m_size == 0)
+ return;
+
+ remainder = unpadded_size % m_size;
+ if (remainder != 0) {
+ class->padding = m_size - remainder;
+ class->type.size = unpadded_size + class->padding;
+ }
+ }
+}
+
+static struct class_member *
+ class__find_next_hole_of_size(struct class *class,
+ struct class_member *from, size_t size)
+{
+ struct class_member *bitfield_head = NULL;
+ struct class_member *member;
+
+ class__for_each_member_continue(class, from, member) {
+ if (member->bitfield_size != 0) {
+ if (bitfield_head == NULL)
+ bitfield_head = member;
+ } else
+ bitfield_head = NULL;
+ if (member->hole != 0) {
+ if (member->byte_size != 0 && member->byte_size <= size)
+ return bitfield_head ? : member;
+ }
+ }
+
+ return NULL;
+}
+
+static struct class_member *
+ class__find_last_member_of_size(struct class *class,
+ struct class_member *to, size_t size)
+{
+ struct class_member *member;
+
+ class__for_each_member_reverse(class, member) {
+ if (member->tag.tag != DW_TAG_member)
+ continue;
+
+ if (member == to)
+ break;
+ /*
+ * Check if this is the first member of a bitfield. It either
+ * has another member before it that is not part of the current
+ * bitfield or it is the first member of the struct.
+ */
+ if (member->bitfield_size != 0 && member->byte_offset != 0) {
+ struct class_member *prev =
+ list_entry(member->tag.node.prev,
+ struct class_member,
+ tag.node);
+ if (prev->bitfield_size != 0)
+ continue;
+
+ }
+
+ if (member->byte_size != 0 && member->byte_size <= size)
+ return member;
+ }
+
+ return NULL;
+}
+
+static bool class__move_member(struct class *class, struct class_member *dest,
+ struct class_member *from, const struct cu *cu,
+ int from_padding, const int verbose, FILE *fp)
+{
+ const size_t from_size = from->byte_size;
+ const size_t dest_size = dest->byte_size;
+
+#ifndef BITFIELD_REORG_ALGORITHMS_ENABLED
+ /*
+ * For now refuse to move a bitfield, we need to first fixup some BRAIN FARTs
+ */
+ if (from->bitfield_size != 0)
+ return false;
+#endif
+ const bool from_was_last = from->tag.node.next == class__tags(class);
+ struct class_member *tail_from = from;
+ struct class_member *from_prev = list_entry(from->tag.node.prev,
+ struct class_member,
+ tag.node);
+ uint16_t orig_tail_from_hole = tail_from->hole;
+ const uint16_t orig_from_offset = from->byte_offset;
+ /*
+ * Align 'from' after 'dest':
+ */
+ const uint16_t offset = dest->hole % (from_size > cu->addr_size ?
+ cu->addr_size : from_size);
+ /*
+ * Set new 'from' offset, after 'dest->byte_offset', aligned
+ */
+ const uint16_t new_from_offset = dest->byte_offset + dest_size + offset;
+
+ if (verbose)
+ fputs("/* Moving", fp);
+
+ if (from->bitfield_size != 0) {
+ struct class_member *pos, *tmp;
+ LIST_HEAD(from_list);
+
+ if (verbose)
+ fprintf(fp, " bitfield('%s' ... ",
+ class_member__name(from, cu));
+ class__for_each_member_safe_from(class, from, pos, tmp) {
+ /*
+ * Have we reached the end of the bitfield?
+ */
+ if (pos->byte_offset != orig_from_offset)
+ break;
+ tail_from = pos;
+ orig_tail_from_hole = tail_from->hole;
+ pos->byte_offset = new_from_offset;
+ pos->bit_offset = new_from_offset * 8 + pos->bitfield_offset;
+ list_move_tail(&pos->tag.node, &from_list);
+ }
+ list_splice(&from_list, &dest->tag.node);
+ if (verbose)
+ fprintf(fp, "'%s')",
+ class_member__name(tail_from, cu));
+ } else {
+ if (verbose)
+ fprintf(fp, " '%s'", class_member__name(from, cu));
+ /*
+ * Remove 'from' from the list
+ */
+ list_del(&from->tag.node);
+ /*
+ * Add 'from' after 'dest':
+ */
+ __list_add(&from->tag.node, &dest->tag.node,
+ dest->tag.node.next);
+ from->byte_offset = new_from_offset;
+ from->bit_offset = new_from_offset * 8 + from->bitfield_offset;
+ }
+
+ if (verbose)
+ fprintf(fp, " from after '%s' to after '%s' */\n",
+ class_member__name(from_prev, cu),
+ class_member__name(dest, cu));
+
+ if (from_padding) {
+ /*
+ * Check if we're eliminating the need for padding:
+ */
+ if (orig_from_offset % cu->addr_size == 0) {
+ /*
+ * Good, no need for padding anymore:
+ */
+ class->type.size -= from_size + class->padding;
+ } else {
+ /*
+ * No, so just add from_size to the padding:
+ */
+ if (verbose)
+ fprintf(fp, "/* adding %zd bytes from %s to "
+ "the padding */\n",
+ from_size, class_member__name(from, cu));
+ }
+ } else if (from_was_last) {
+ class->type.size -= from_size + class->padding;
+ } else {
+ /*
+ * See if we are adding a new hole that is bigger than
+ * sizeof(long), this may have problems with explicit alignment
+ * made by the programmer, perhaps we need A switch that allows
+ * us to avoid realignment, just using existing holes but
+ * keeping the existing alignment, anyway the programmer has to
+ * check the resulting rerganization before using it, and for
+ * automatic stuff such as the one that will be used for struct
+ * "views" in tools such as ctracer we are more interested in
+ * packing the subset as tightly as possible.
+ */
+ if (orig_tail_from_hole + from_size >= cu->addr_size) {
+ class->type.size -= cu->addr_size;
+ class__subtract_offsets_from(class, from_prev,
+ cu->addr_size);
+ }
+ }
+
+ class__recalc_holes(class);
+
+ if (verbose > 1) {
+ class__fprintf(class, cu, fp);
+ fputc('\n', fp);
+ }
+
+ return true;
+}
+
+#ifdef BITFIELD_REORG_ALGORITHMS_ENABLED
+static struct class_member *
+ class__find_next_bit_hole_of_size(struct class *class,
+ struct class_member *from,
+ size_t size)
+{
+ struct class_member *member;
+
+ class__for_each_member_continue(class, from, member) {
+ if (member->tag.tag != DW_TAG_member)
+ continue;
+ if (member->bit_hole != 0 &&
+ member->bitfield_size <= size)
+ return member;
+ }
+#if 0
+ /*
+ * FIXME: Handle the case where the bit padding is on the same bitfield
+ * that we're looking, i.e. we can't combine a bitfield with itclass,
+ * perhaps we should tag bitfields with a sequential, clearly marking
+ * each of the bitfields in advance, so that all the algoriths that
+ * have to deal with bitfields, moving them around, demoting, etc, can
+ * be simplified.
+ */
+ /*
+ * Now look if the last member is a one member bitfield,
+ * i.e. if we have bit_padding
+ */
+ if (class->bit_padding != 0)
+ return type__last_member(&class->type);
+#endif
+ return NULL;
+}
+
+static void class__move_bit_member(struct class *class, const struct cu *cu,
+ struct class_member *dest,
+ struct class_member *from,
+ const int verbose, FILE *fp)
+{
+ struct class_member *from_prev = list_entry(from->tag.node.prev,
+ struct class_member,
+ tag.node);
+
+ if (verbose)
+ fprintf(fp, "/* Moving '%s:%u' from after '%s' to "
+ "after '%s:%u' */\n",
+ class_member__name(from, cu), from->bitfield_size,
+ class_member__name(from_prev, cu),
+ class_member__name(dest, cu), dest->bitfield_size);
+ /*
+ * Remove 'from' from the list
+ */
+ list_del(&from->tag.node);
+ /*
+ * Add from after dest:
+ */
+ __list_add(&from->tag.node,
+ &dest->tag.node,
+ dest->tag.node.next);
+
+ /* Check if this was the last entry in the bitfield */
+ if (from_prev->bitfield_size == 0) {
+ size_t from_size = from->byte_size;
+ /*
+ * Are we shrinking the struct?
+ */
+ if (from_size + from->hole >= cu->addr_size) {
+ class->type.size -= from_size + from->hole;
+ class__subtract_offsets_from(class, from_prev,
+ from_size + from->hole);
+ }
+ }
+ /*
+ * Tricky, what are the rules for bitfield layouts on this arch?
+ * Assume its IA32
+ */
+ from->bitfield_offset = dest->bitfield_offset + dest->bitfield_size;
+ /*
+ * Now both have the same offset:
+ */
+ from->byte_offset = dest->byte_offset;
+ from->bit_offset = dest->byte_offset * 8 + from->bitfield_offset;
+
+ class__recalc_holes(class);
+
+ if (verbose > 1) {
+ class__fprintf(class, cu, fp);
+ fputc('\n', fp);
+ }
+}
+
+static void class__demote_bitfield_members(struct class *class,
+ struct class_member *from,
+ struct class_member *to,
+ const struct base_type *old_type,
+ const struct base_type *new_type,
+ type_id_t new_type_id)
+{
+ struct class_member *member;
+
+ class__for_each_member_from(class, from, member) {
+ member->byte_size = new_type->bit_size / 8;
+ member->tag.type = new_type_id;
+ if (member == to)
+ break;
+ }
+}
+
+static struct tag *cu__find_base_type_of_size(const struct cu *cu,
+ const size_t size, type_id_t *id)
+{
+ const char *type_name, *type_name_alt = NULL;
+
+ switch (size) {
+ case sizeof(unsigned char):
+ type_name = "unsigned char"; break;
+ case sizeof(unsigned short int):
+ type_name = "short unsigned int";
+ type_name_alt = "unsigned short"; break;
+ case sizeof(unsigned int):
+ type_name = "unsigned int";
+ type_name_alt = "unsigned"; break;
+ case sizeof(unsigned long long):
+ if (cu->addr_size == 8) {
+ type_name = "long unsigned int";
+ type_name_alt = "unsigned long";
+ } else {
+ type_name = "long long unsigned int";
+ type_name_alt = "unsigned long long";
+ }
+ break;
+ default:
+ return NULL;
+ }
+
+ struct tag *ret = cu__find_base_type_by_name(cu, type_name, id);
+ return ret ?: cu__find_base_type_by_name(cu, type_name_alt, id);
+}
+
+static int class__demote_bitfields(struct class *class, const struct cu *cu,
+ const int verbose, FILE *fp)
+{
+ struct class_member *member;
+ struct class_member *bitfield_head = NULL;
+ const struct tag *old_type_tag, *new_type_tag;
+ size_t current_bitfield_size = 0, size, bytes_needed;
+ int some_was_demoted = 0;
+
+ type__for_each_data_member(&class->type, member) {
+ /*
+ * Check if we are moving away from a bitfield
+ */
+ if (member->bitfield_size == 0) {
+ current_bitfield_size = 0;
+ bitfield_head = NULL;
+ } else {
+ if (bitfield_head == NULL) {
+ bitfield_head = member;
+ current_bitfield_size = member->bitfield_size;
+ } else if (bitfield_head->byte_offset != member->byte_offset) {
+ /*
+ * We moved from one bitfield to another, for
+ * now don't handle this case, just move on to
+ * the next bitfield, we may well move it to
+ * another place and then the first bitfield will
+ * be isolated and will be handled in the next
+ * pass.
+ */
+ bitfield_head = member;
+ current_bitfield_size = member->bitfield_size;
+ } else
+ current_bitfield_size += member->bitfield_size;
+ }
+
+ /*
+ * Have we got to the end of a bitfield with holes?
+ */
+ if (member->bit_hole == 0)
+ continue;
+
+ size = member->byte_size;
+ bytes_needed = (current_bitfield_size + 7) / 8;
+ bytes_needed = roundup_pow_of_two(bytes_needed);
+ if (bytes_needed == size)
+ continue;
+
+ type_id_t new_type_id;
+ old_type_tag = cu__type(cu, member->tag.type);
+ new_type_tag = cu__find_base_type_of_size(cu, bytes_needed,
+ &new_type_id);
+
+ if (new_type_tag == NULL) {
+ fprintf(fp, "/* BRAIN FART ALERT! couldn't find a "
+ "%zd bytes base type */\n\n", bytes_needed);
+ continue;
+ }
+ if (verbose) {
+ char old_bf[64], new_bf[64];
+ fprintf(fp, "/* Demoting bitfield ('%s' ... '%s') "
+ "from '%s' to '%s' */\n",
+ class_member__name(bitfield_head, cu),
+ class_member__name(member, cu),
+ base_type__name(tag__base_type(old_type_tag),
+ cu, old_bf, sizeof(old_bf)),
+ base_type__name(tag__base_type(new_type_tag),
+ cu, new_bf, sizeof(new_bf)));
+ }
+
+ class__demote_bitfield_members(class,
+ bitfield_head, member,
+ tag__base_type(old_type_tag),
+ tag__base_type(new_type_tag),
+ new_type_id);
+ class__recalc_holes(class);
+ some_was_demoted = 1;
+
+ if (verbose > 1) {
+ class__fprintf(class, cu, fp);
+ fputc('\n', fp);
+ }
+ }
+ /*
+ * Now look if we have bit padding, i.e. if the the last member
+ * is a bitfield and its the sole member in this bitfield, i.e.
+ * if it wasn't already demoted as part of a bitfield of more than
+ * one member:
+ */
+ member = type__last_member(&class->type);
+ if (class->bit_padding != 0 && bitfield_head == member) {
+ size = member->byte_size;
+ bytes_needed = (member->bitfield_size + 7) / 8;
+ if (bytes_needed < size) {
+ old_type_tag = cu__type(cu, member->tag.type);
+ type_id_t new_type_id;
+ new_type_tag =
+ cu__find_base_type_of_size(cu, bytes_needed,
+ &new_type_id);
+
+ tag__assert_search_result(old_type_tag);
+ tag__assert_search_result(new_type_tag);
+
+ if (verbose) {
+ char old_bf[64], new_bf[64];
+ fprintf(fp, "/* Demoting bitfield ('%s') "
+ "from '%s' to '%s' */\n",
+ class_member__name(member, cu),
+ base_type__name(tag__base_type(old_type_tag),
+ cu, old_bf, sizeof(old_bf)),
+ base_type__name(tag__base_type(new_type_tag),
+ cu, new_bf, sizeof(new_bf)));
+ }
+ class__demote_bitfield_members(class,
+ member, member,
+ tag__base_type(old_type_tag),
+ tag__base_type(new_type_tag),
+ new_type_id);
+ class__recalc_holes(class);
+ some_was_demoted = 1;
+
+ if (verbose > 1) {
+ class__fprintf(class, cu, fp);
+ fputc('\n', fp);
+ }
+ }
+ }
+
+ return some_was_demoted;
+}
+
+static void class__reorganize_bitfields(struct class *class,
+ const struct cu *cu,
+ const int verbose, FILE *fp)
+{
+ struct class_member *member, *brother;
+restart:
+ type__for_each_data_member(&class->type, member) {
+ /* See if we have a hole after this member */
+ if (member->bit_hole != 0) {
+ /*
+ * OK, try to find a member that has a bit hole after
+ * it and that has a size that fits the current hole:
+ */
+ brother =
+ class__find_next_bit_hole_of_size(class, member,
+ member->bit_hole);
+ if (brother != NULL) {
+ class__move_bit_member(class, cu,
+ member, brother,
+ verbose, fp);
+ goto restart;
+ }
+ }
+ }
+}
+
+static void class__fixup_bitfield_types(struct class *class,
+ struct class_member *from,
+ struct class_member *to_before,
+ type_id_t type)
+{
+ struct class_member *member;
+
+ class__for_each_member_from(class, from, member) {
+ if (member == to_before)
+ break;
+ member->tag.type = type;
+ }
+}
+
+/*
+ * Think about this pahole output a bit:
+ *
+ * [filo examples]$ pahole swiss_cheese cheese
+ * / * <11b> /home/acme/git/pahole/examples/swiss_cheese.c:3 * /
+ * struct cheese {
+ * <SNIP>
+ * int bitfield1:1; / * 64 4 * /
+ * int bitfield2:1; / * 64 4 * /
+ *
+ * / * XXX 14 bits hole, try to pack * /
+ * / * Bitfield WARNING: DWARF size=4, real size=2 * /
+ *
+ * short int d; / * 66 2 * /
+ * <SNIP>
+ *
+ * The compiler (gcc 4.1.1 20070105 (Red Hat 4.1.1-51) in the above example),
+ * Decided to combine what was declared as an int (4 bytes) bitfield but doesn't
+ * uses even one byte with the next field, that is a short int (2 bytes),
+ * without demoting the type of the bitfield to short int (2 bytes), so in terms
+ * of alignment the real size is 2, not 4, to make things easier for the rest of
+ * the reorganizing routines we just do the demotion ourselves, fixing up the
+ * sizes.
+*/
+static void class__fixup_member_types(struct class *class, const struct cu *cu,
+ const uint8_t verbose, FILE *fp)
+{
+ struct class_member *pos, *bitfield_head = NULL;
+ uint8_t fixup_was_done = 0;
+
+ type__for_each_data_member(&class->type, pos) {
+ /*
+ * Is this bitfield member?
+ */
+ if (pos->bitfield_size != 0) {
+ /*
+ * The first entry in a bitfield?
+ */
+ if (bitfield_head == NULL)
+ bitfield_head = pos;
+ continue;
+ }
+ /*
+ * OK, not a bitfield member, but have we just passed
+ * by a bitfield?
+ */
+ if (bitfield_head != NULL) {
+ const uint16_t real_size = (pos->byte_offset -
+ bitfield_head->byte_offset);
+ const size_t size = bitfield_head->byte_size;
+ /*
+ * Another case:
+struct irq_cfg {
+ struct irq_pin_list * irq_2_pin; / * 0 8 * /
+ cpumask_var_t domain; / * 8 16 * /
+ cpumask_var_t old_domain; / * 24 16 * /
+ u8 vector; / * 40 1 * /
+ u8 move_in_progress:1; / * 41: 7 1 * /
+ u8 remapped:1; / * 41: 6 1 * /
+
+ / * XXX 6 bits hole, try to pack * /
+ / * XXX 6 bytes hole, try to pack * /
+
+ union {
+ struct irq_2_iommu irq_2_iommu; / * 16 * /
+ struct irq_2_irte irq_2_irte; / * 4 * /
+ }; / * 48 16 * /
+ / * --- cacheline 1 boundary (64 bytes) --- * /
+
+ * So just fix it up if the byte_size of the bitfield is
+ * greater than what it really uses.
+ */
+ if (real_size < size) {
+ type_id_t new_type_id;
+ struct tag *new_type_tag =
+ cu__find_base_type_of_size(cu,
+ real_size,
+ &new_type_id);
+ if (new_type_tag == NULL) {
+ fprintf(stderr, "%s: couldn't find"
+ " a base_type of %d bytes!\n",
+ __func__, real_size);
+ continue;
+ }
+ class__fixup_bitfield_types(class,
+ bitfield_head, pos,
+ new_type_id);
+ fixup_was_done = 1;
+ }
+ }
+ bitfield_head = NULL;
+ }
+ if (fixup_was_done) {
+ class__recalc_holes(class);
+ }
+ if (verbose && fixup_was_done) {
+ fprintf(fp, "/* bitfield types were fixed */\n");
+ if (verbose > 1) {
+ class__fprintf(class, cu, fp);
+ fputc('\n', fp);
+ }
+ }
+}
+#endif // BITFIELD_REORG_ALGORITHMS_ENABLED
+
+void class__reorganize(struct class *class, const struct cu *cu,
+ const int verbose, FILE *fp)
+{
+ struct class_member *member, *brother, *last_member;
+ size_t alignment_size;
+
+ class__find_holes(class);
+#ifdef BITFIELD_REORG_ALGORITHMS_ENABLED
+ class__fixup_member_types(class, cu, verbose, fp);
+ while (class__demote_bitfields(class, cu, verbose, fp))
+ class__reorganize_bitfields(class, cu, verbose, fp);
+#endif
+ /* Now try to combine holes */
+restart:
+ alignment_size = 0;
+ /*
+ * It can be NULL if this class doesn't have any data members,
+ * just inheritance entries
+ */
+ last_member = type__last_member(&class->type);
+ if (last_member == NULL)
+ return;
+
+ type__for_each_data_member(&class->type, member) {
+ const size_t aligned_size = member->byte_size + member->hole;
+ if (aligned_size <= cu->addr_size &&
+ aligned_size > alignment_size)
+ alignment_size = aligned_size;
+ }
+
+ if (alignment_size != 0) {
+ size_t modulo;
+ uint16_t new_padding;
+
+ if (alignment_size > 1)
+ alignment_size = roundup(alignment_size, 2);
+ modulo = (last_member->byte_offset +
+ last_member->byte_size) % alignment_size;
+ if (modulo != 0)
+ new_padding = cu->addr_size - modulo;
+ else
+ new_padding = 0;
+
+ if (new_padding != class->padding) {
+ class->padding = new_padding;
+ class->type.size = (last_member->byte_offset +
+ last_member->byte_size + new_padding);
+ }
+ }
+
+ type__for_each_data_member(&class->type, member) {
+ /* See if we have a hole after this member */
+ if (member->hole != 0) {
+ /*
+ * OK, try to find a member that has a hole after it
+ * and that has a size that fits the current hole:
+ */
+ brother = class__find_next_hole_of_size(class, member,
+ member->hole);
+ if (brother != NULL) {
+ struct class_member *brother_prev =
+ list_entry(brother->tag.node.prev,
+ struct class_member,
+ tag.node);
+ /*
+ * If it the next member, avoid moving it closer,
+ * it could be a explicit alignment rule, like
+ * ____cacheline_aligned_in_smp in the Linux
+ * kernel.
+ */
+ if (brother_prev != member) {
+ if (class__move_member(class, member, brother, cu, 0, verbose, fp))
+ goto restart;
+ }
+ }
+ /*
+ * OK, but is there padding? If so the last member
+ * has a hole, if we are not at the last member and
+ * it has a size that is smaller than the current hole
+ * we can move it after the current member, reducing
+ * the padding or eliminating it altogether.
+ */
+ if (class->padding > 0 &&
+ member != last_member &&
+ last_member->byte_size != 0 &&
+ last_member->byte_size <= member->hole) {
+ if (class__move_member(class, member, last_member, cu, 1, verbose, fp))
+ goto restart;
+ }
+ }
+ }
+
+ /* Now try to move members at the tail to after holes */
+ if (class->nr_holes == 0)
+ return;
+
+ type__for_each_data_member(&class->type, member) {
+ /* See if we have a hole after this member */
+ if (member->hole != 0) {
+ brother = class__find_last_member_of_size(class, member,
+ member->hole);
+ if (brother != NULL) {
+ struct class_member *brother_prev =
+ list_entry(brother->tag.node.prev,
+ struct class_member,
+ tag.node);
+ /*
+ * If it the next member, avoid moving it closer,
+ * it could be a explicit alignment rule, like
+ * ____cacheline_aligned_in_smp in the Linux
+ * kernel.
+ */
+ if (brother_prev != member) {
+ if (class__move_member(class, member, brother, cu, 0, verbose, fp))
+ goto restart;
+ }
+ }
+ }
+ }
+}
diff --git a/dwarves_reorganize.h b/dwarves_reorganize.h
new file mode 100644
index 0000000..5fccf6d
--- /dev/null
+++ b/dwarves_reorganize.h
@@ -0,0 +1,30 @@
+#ifndef _DWARVES_REORGANIZE_H_
+#define _DWARVES_REORGANIZE_H_ 1
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2006 Mandriva Conectiva S.A.
+ Copyright (C) 2006 Arnaldo Carvalho de Melo <acme@mandriva.com>
+ Copyright (C) 2007 Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
+*/
+
+
+#include <stdint.h>
+#include <stdio.h>
+
+struct class;
+struct cu;
+struct class_member;
+
+void class__subtract_offsets_from(struct class *cls, struct class_member *from,
+ const uint16_t size);
+
+void class__add_offsets_from(struct class *cls, struct class_member *from,
+ const uint16_t size);
+
+void class__fixup_alignment(struct class *cls, const struct cu *cu);
+
+void class__reorganize(struct class *cls, const struct cu *cu,
+ const int verbose, FILE *fp);
+
+#endif /* _DWARVES_REORGANIZE_H_ */
diff --git a/elf_symtab.c b/elf_symtab.c
new file mode 100644
index 0000000..741990e
--- /dev/null
+++ b/elf_symtab.c
@@ -0,0 +1,68 @@
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2009 Red Hat Inc.
+ Copyright (C) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
+*/
+
+#include <malloc.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "dutil.h"
+#include "elf_symtab.h"
+
+#define HASHSYMS__BITS 8
+#define HASHSYMS__SIZE (1UL << HASHSYMS__BITS)
+
+struct elf_symtab *elf_symtab__new(const char *name, Elf *elf, GElf_Ehdr *ehdr)
+{
+ if (name == NULL)
+ name = ".symtab";
+
+ GElf_Shdr shdr;
+ Elf_Scn *sec = elf_section_by_name(elf, ehdr, &shdr, name, NULL);
+
+ if (sec == NULL)
+ return NULL;
+
+ if (gelf_getshdr(sec, &shdr) == NULL)
+ return NULL;
+
+ struct elf_symtab *symtab = malloc(sizeof(*symtab));
+ if (symtab == NULL)
+ return NULL;
+
+ symtab->name = strdup(name);
+ if (symtab->name == NULL)
+ goto out_delete;
+
+ symtab->syms = elf_getdata(sec, NULL);
+ if (symtab->syms == NULL)
+ goto out_free_name;
+
+ sec = elf_getscn(elf, shdr.sh_link);
+ if (sec == NULL)
+ goto out_free_name;
+
+ symtab->symstrs = elf_getdata(sec, NULL);
+ if (symtab->symstrs == NULL)
+ goto out_free_name;
+
+ symtab->nr_syms = shdr.sh_size / shdr.sh_entsize;
+
+ return symtab;
+out_free_name:
+ free(symtab->name);
+out_delete:
+ free(symtab);
+ return NULL;
+}
+
+void elf_symtab__delete(struct elf_symtab *symtab)
+{
+ if (symtab == NULL)
+ return;
+ free(symtab->name);
+ free(symtab);
+}
diff --git a/elf_symtab.h b/elf_symtab.h
new file mode 100644
index 0000000..359add6
--- /dev/null
+++ b/elf_symtab.h
@@ -0,0 +1,92 @@
+#ifndef _ELF_SYMTAB_H_
+#define _ELF_SYMTAB_H_ 1
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2009 Red Hat Inc.
+ Copyright (C) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
+*/
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <gelf.h>
+#include <elf.h>
+
+struct elf_symtab {
+ uint32_t nr_syms;
+ Elf_Data *syms;
+ Elf_Data *symstrs;
+ char *name;
+};
+
+struct elf_symtab *elf_symtab__new(const char *name, Elf *elf, GElf_Ehdr *ehdr);
+void elf_symtab__delete(struct elf_symtab *symtab);
+
+static inline uint32_t elf_symtab__nr_symbols(const struct elf_symtab *symtab)
+{
+ return symtab->nr_syms;
+}
+
+static inline const char *elf_sym__name(const GElf_Sym *sym,
+ const struct elf_symtab *symtab)
+{
+ return symtab->symstrs->d_buf + sym->st_name;
+}
+
+static inline uint8_t elf_sym__type(const GElf_Sym *sym)
+{
+ return GELF_ST_TYPE(sym->st_info);
+}
+
+static inline uint16_t elf_sym__section(const GElf_Sym *sym)
+{
+ return sym->st_shndx;
+}
+
+static inline uint8_t elf_sym__bind(const GElf_Sym *sym)
+{
+ return GELF_ST_BIND(sym->st_info);
+}
+
+static inline uint8_t elf_sym__visibility(const GElf_Sym *sym)
+{