Hi Mark,
On Fri, Jul 10, 2026 at 6:35 PM Mark Wielaard <[email protected]> wrote:
>
> This introduces three new libdw functions dwarf_begin_type,
> dwarf_begin_elf_type and dwarf_type.
>
> When a file contained multiple types of DWARF, plain DWARF, DWO or
> GNU_LTO, dwarf_begin and dwarf_begin_elf would use the plain DWARF
> when available, otherwise use the DWO sections and finally the GNU_LTO
> sections to construct a Dwarf handle. This meant that if a file
> contained multiple types you would only be able to get one. And you
> couldn't explicitly say you only wanted a Dwarf handle for a specific
> type. Also you had no clear way of determining which type of Dwarf
> handle you got.
>
> The new dwarf_begin_type and dwarf_begin_elf_type allow explicitly
> specifying a specific type of DWARF you want. And the dwarf_type
> function will return the actual type of DWARF you got.
>
> * libdw/libdw.h (Dwarf_Type): New enum.
> (dwarf_begin_type): New function declaration.
> (dwarf_begin_elf_type): Likewise.
> (dwarf_get_type): Likewise.
> * libdw/libdw.map (ELFUTILS_0.196): New section with new
> function symbols based on ELFUTILS_0.194_EXPERIMENTAL.
> * libdw/libdwP.h (enum dwarf_type): Removed.
> (struct Dwarf): Make Dwarf_Type type.
> (INTDECL): dwarf_begin_elf_type, dwarf_begin_type,
> dwarf_get_type.
> * libdw/dwarf_begin.c (dwarf_begin): Rename to and make a
> wrapper around...
> (dwarf_begin_type): ... this new function.
> * libdw/dwarf_begin_elf.c (scn_dwarf_type): Return a
> Dwarf_Type.
> (check_section): Use Dwarf_Type instead of enum dwarf_type.
> (global_read): Add requested_type argument. Split in
> DWARF_T_AUTO case and specific type case.
> (scngrp_read): Likewise.
> (dwarf_begin_elf): Rename to and make a wrapper around..,
> (dwarf_begin_elf_type): ... this new function.
> (dwarf_get_type): New function.
> * tests/Makefile.am (check_PROGRAMS): Add dwarf-type.
> (TESTS): Add run-dwarf-type.sh.
> (EXTRA_DIST): Likewise.
> (dwarf_type_LDADD): New.
> * tests/dwarf-type.c: New test.
> * tests/run-dwarf-type.sh: New test wrapper.
>
> https://sourceware.org/bugzilla/show_bug.cgi?id=28573
>
> Signed-off-by: Mark Wielaard <[email protected]>
> ---
> libdw/dwarf_begin.c | 12 +++-
> libdw/dwarf_begin_elf.c | 156 ++++++++++++++++++++++++++++++----------
> libdw/libdw.h | 39 +++++++++-
> libdw/libdw.map | 5 +-
> libdw/libdwP.h | 14 ++--
> tests/Makefile.am | 4 ++
> tests/dwarf-type.c | 84 ++++++++++++++++++++++
> tests/run-dwarf-type.sh | 53 ++++++++++++++
> 8 files changed, 316 insertions(+), 51 deletions(-)
> create mode 100644 tests/dwarf-type.c
> create mode 100755 tests/run-dwarf-type.sh
>
> diff --git a/libdw/dwarf_begin.c b/libdw/dwarf_begin.c
> index 19d16e5c511c..fbf1b0f05c90 100644
> --- a/libdw/dwarf_begin.c
> +++ b/libdw/dwarf_begin.c
> @@ -1,5 +1,6 @@
> /* Create descriptor from file descriptor for processing file.
> Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc.
> + Copyright (C) 2026 Mark J. Wielaard <[email protected]>
> This file is part of elfutils.
> Written by Ulrich Drepper <[email protected]>, 2002.
>
> @@ -39,7 +40,7 @@
>
>
> Dwarf *
> -dwarf_begin (int fd, Dwarf_Cmd cmd)
> +dwarf_begin_type (int fd, Dwarf_Cmd cmd, Dwarf_Type type)
> {
> Elf *elf;
> Elf_Cmd elfcmd;
> @@ -85,7 +86,7 @@ dwarf_begin (int fd, Dwarf_Cmd cmd)
> else
> {
> /* Do the real work now that we have an ELF descriptor. */
> - result = INTUSE(dwarf_begin_elf) (elf, cmd, NULL);
> + result = INTUSE(dwarf_begin_elf_type) (elf, cmd, type, NULL);
>
> /* If this failed, free the resources. */
> if (result == NULL)
> @@ -96,4 +97,11 @@ dwarf_begin (int fd, Dwarf_Cmd cmd)
>
> return result;
> }
> +INTDEF(dwarf_begin_type)
> +
> +Dwarf *
> +dwarf_begin (int fd, Dwarf_Cmd cmd)
> +{
> + return INTUSE(dwarf_begin_type) (fd, cmd, DWARF_T_AUTO);
> +}
> INTDEF(dwarf_begin)
> diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c
> index ec8fff4fcb52..f6ce7e20e032 100644
> --- a/libdw/dwarf_begin_elf.c
> +++ b/libdw/dwarf_begin_elf.c
> @@ -1,6 +1,6 @@
> /* Create descriptor from ELF descriptor for processing file.
> Copyright (C) 2002-2011, 2014, 2015, 2017, 2018 Red Hat, Inc.
> - Copyright (C) 2023, Mark J. Wielaard <[email protected]>
> + Copyright (C) 2023, 2026 Mark J. Wielaard <[email protected]>
> This file is part of elfutils.
>
> This file is free software; you can redistribute it and/or modify
> @@ -103,37 +103,40 @@ static const enum string_section_index
> scn_to_string_section_idx[IDX_last] =
> [IDX_gnu_debugaltlink] = STR_SCN_IDX_last
> };
>
> -static enum dwarf_type
> +static Dwarf_Type
> scn_dwarf_type (Dwarf *result, size_t shstrndx, Elf_Scn *scn)
> {
> GElf_Shdr shdr_mem;
> GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
> if (shdr == NULL)
> - return TYPE_UNKNOWN;
> + return DWARF_T_AUTO;
>
> const char *scnname = elf_strptr (result->elf, shstrndx,
> shdr->sh_name);
> if (scnname != NULL)
> {
> if (startswith (scnname, ".gnu.debuglto_.debug"))
> - return TYPE_GNU_LTO;
> + return DWARF_T_GNU_LTO;
> else if (strcmp (scnname, ".debug_cu_index") == 0
> || strcmp (scnname, ".debug_tu_index") == 0
> - || strcmp (scnname, ".debug_dwp") == 0
> || strcmp (scnname, ".zdebug_cu_index") == 0
> - || strcmp (scnname, ".zdebug_tu_index") == 0
> - || strcmp (scnname, ".zdebug_dwp") == 0)
> - return TYPE_DWO;
> + || strcmp (scnname, ".zdebug_tu_index") == 0)
> + return DWARF_T_DWO;
> + if (strcmp (scnname, ".debug_dwp") == 0
> + || strcmp (scnname, ".zdebug_dwp") == 0
> + || strcmp (scnname, ".debug_sup") == 0
> + || strcmp (scnname, ".zdebug_sup") == 0)
> + return DWARF_T_AUTO; /* not a "real" debug section. */
> else if (startswith (scnname, ".debug_") || startswith (scnname,
> ".zdebug_"))
> {
> size_t len = strlen (scnname);
> if (strcmp (scnname + len - 4, ".dwo") == 0)
> - return TYPE_DWO;
> + return DWARF_T_DWO;
> else
> - return TYPE_PLAIN;
> + return DWARF_T_PLAIN;
> }
> }
> - return TYPE_UNKNOWN;
> + return DWARF_T_AUTO;
> }
> static Dwarf *
> check_section (Dwarf *result, size_t shstrndx, Elf_Scn *scn, bool inscngrp)
> @@ -190,11 +193,11 @@ check_section (Dwarf *result, size_t shstrndx, Elf_Scn
> *scn, bool inscngrp)
> {
> /* .debug_cu_index and .debug_tu_index don't have a .dwo suffix,
> but they are for DWO. */
> - if (result->type != TYPE_DWO
> + if (result->type != DWARF_T_DWO
> && (cnt == IDX_debug_cu_index || cnt == IDX_debug_tu_index))
> continue;
> bool need_dot_dwo =
> - (result->type == TYPE_DWO
> + (result->type == DWARF_T_DWO
> && cnt != IDX_debug_cu_index
> && cnt != IDX_debug_tu_index);
Should IDX_debug_dwp be included here since it also doesn't have a .dwo
suffix?
> size_t dbglen = strlen (dwarf_scnnames[cnt]);
> @@ -221,7 +224,7 @@ check_section (Dwarf *result, size_t shstrndx, Elf_Scn
> *scn, bool inscngrp)
> && startswith (scnname, ".gnu.debuglto_")
> && strcmp (&scnname[14], dwarf_scnnames[cnt]) == 0)
> {
> - if (result->type == TYPE_GNU_LTO)
> + if (result->type == DWARF_T_GNU_LTO)
> break;
> }
> }
> @@ -486,17 +489,44 @@ valid_p (Dwarf *result)
>
>
> static Dwarf *
> -global_read (Dwarf *result, Elf *elf, size_t shstrndx)
> +global_read (Dwarf *result, Elf *elf, size_t shstrndx,
> + Dwarf_Type requested_type)
> {
> Elf_Scn *scn = NULL;
>
> - /* First check the type (PLAIN, DWO, LTO) we are looking for. We
> - prefer PLAIN if available over DWO, over LTO. */
> - while ((scn = elf_nextscn (elf, scn)) != NULL && result->type !=
> TYPE_PLAIN)
> + if (requested_type == DWARF_T_AUTO)
> {
> - enum dwarf_type type = scn_dwarf_type (result, shstrndx, scn);
> - if (type > result->type)
> - result->type = type;
> + /* First check the type (PLAIN, DWO, LTO) we are looking for. We
> + prefer PLAIN if available over DWO, over LTO. */
> + while ((scn = elf_nextscn (elf, scn)) != NULL
> + && result->type != DWARF_T_PLAIN)
> + {
> + Dwarf_Type type = scn_dwarf_type (result, shstrndx, scn);
> + if (type > result->type)
> + result->type = type;
> + }
> + }
> + else
> + {
> + /* Check there is at least one section of the requested type. */
> + bool found = false;
> + while ((scn = elf_nextscn (elf, scn)) != NULL && !found)
> + {
> + Dwarf_Type type = scn_dwarf_type (result, shstrndx, scn);
> + if (type == requested_type)
> + found = true;
> + }
> +
> + if (!found)
> + {
> + /* Requested type not available. */
> + Dwarf_Sig8_Hash_free (&result->sig8_hash);
> + __libdw_seterrno (DWARF_E_NO_DWARF);
> + free (result);
> + return NULL;
> + }
> +
> + result->type = requested_type;
> }
>
> scn = NULL;
> @@ -508,7 +538,8 @@ global_read (Dwarf *result, Elf *elf, size_t shstrndx)
>
>
> static Dwarf *
> -scngrp_read (Dwarf *result, Elf *elf, size_t shstrndx, Elf_Scn *scngrp)
> +scngrp_read (Dwarf *result, Elf *elf, size_t shstrndx,
> + Dwarf_Type requested_type, Elf_Scn *scngrp)
> {
> GElf_Shdr shdr_mem;
> GElf_Shdr *shdr = gelf_getshdr (scngrp, &shdr_mem);
> @@ -545,24 +576,58 @@ scngrp_read (Dwarf *result, Elf *elf, size_t shstrndx,
> Elf_Scn *scngrp)
> Elf32_Word *scnidx = (Elf32_Word *) data->d_buf;
> size_t cnt;
>
> - /* First check the type (PLAIN, DWO, LTO) we are looking for. We
> - prefer PLAIN if available over DWO, over LTO. */
> - for (cnt = 1; cnt * sizeof (Elf32_Word) <= data->d_size; ++cnt)
> + if (requested_type == DWARF_T_AUTO)
> {
> - Elf_Scn *scn = elf_getscn (elf, scnidx[cnt]);
> - if (scn == NULL)
> + /* First check the type (PLAIN, DWO, LTO) we are looking for. We
> + prefer PLAIN if available over DWO, over LTO. */
> + for (cnt = 1; cnt * sizeof (Elf32_Word) <= data->d_size; ++cnt)
> {
> - /* A section group refers to a non-existing section. Should
> - never happen. */
> + Elf_Scn *scn = elf_getscn (elf, scnidx[cnt]);
> + if (scn == NULL)
> + {
> + /* A section group refers to a non-existing section. Should
> + never happen. */
> + Dwarf_Sig8_Hash_free (&result->sig8_hash);
> + __libdw_seterrno (DWARF_E_INVALID_ELF);
> + free (result);
> + return NULL;
> + }
> +
> + Dwarf_Type type = scn_dwarf_type (result, shstrndx, scn);
> + if (type > result->type)
> + result->type = type;
> + }
> + }
> + else
> + {
> + /* Check there is at least one section of the requested type. */
> + bool found = false;
> + for (cnt = 1; cnt * sizeof (Elf32_Word) <= data->d_size && !found;
> ++cnt)
> + {
> + Elf_Scn *scn = elf_getscn (elf, scnidx[cnt]);
> + if (scn == NULL)
> + {
> + Dwarf_Sig8_Hash_free (&result->sig8_hash);
> + __libdw_seterrno (DWARF_E_INVALID_ELF);
> + free (result);
> + return NULL;
> + }
> +
> + Dwarf_Type type = scn_dwarf_type (result, shstrndx, scn);
> + if (type == requested_type)
> + found = true;
There is an assert around line 636 in dwarf_begin_elf.c that will fail
if a malformed binary has scngrp containing non-existing sections.
The loop in the `if (requested_type == DWARF_T_AUTO)` branch verifies
every scngrp entry corresponds to a real section but the new else branch
loop will stop early if found is set to true. In this case not every
entry is verified.
It might be worth addressing this to prevent another security bug report.
We can still exit the loop early but replace the assert with an early
return.
Aaron
> + }
> +
> + if (!found)
> + {
> + /* Requested type not available. */
> Dwarf_Sig8_Hash_free (&result->sig8_hash);
> - __libdw_seterrno (DWARF_E_INVALID_ELF);
> + __libdw_seterrno (DWARF_E_NO_DWARF);
> free (result);
> return NULL;
> }
>
> - enum dwarf_type type = scn_dwarf_type (result, shstrndx, scn);
> - if (type > result->type)
> - result->type = type;
> + result->type = requested_type;
> }
>
> for (cnt = 1; cnt * sizeof (Elf32_Word) <= data->d_size && result != NULL;
> ++cnt)
> @@ -579,7 +644,8 @@ scngrp_read (Dwarf *result, Elf *elf, size_t shstrndx,
> Elf_Scn *scngrp)
>
>
> Dwarf *
> -dwarf_begin_elf (Elf *elf, Dwarf_Cmd cmd, Elf_Scn *scngrp)
> +dwarf_begin_elf_type (Elf *elf, Dwarf_Cmd cmd, Dwarf_Type type,
> + Elf_Scn *scngrp)
> {
> GElf_Ehdr *ehdr;
> GElf_Ehdr ehdr_mem;
> @@ -648,9 +714,9 @@ dwarf_begin_elf (Elf *elf, Dwarf_Cmd cmd, Elf_Scn *scngrp)
> sections with the name are ignored. The DWARF specification
> does not really say this is allowed. */
> if (scngrp == NULL)
> - return global_read (result, elf, shstrndx);
> + return global_read (result, elf, shstrndx, type);
> else
> - return scngrp_read (result, elf, shstrndx, scngrp);
> + return scngrp_read (result, elf, shstrndx, type, scngrp);
> }
> else if (cmd == DWARF_C_WRITE)
> {
> @@ -665,4 +731,22 @@ dwarf_begin_elf (Elf *elf, Dwarf_Cmd cmd, Elf_Scn
> *scngrp)
> free (result);
> return NULL;
> }
> +INTDEF(dwarf_begin_elf_type)
> +
> +Dwarf *
> +dwarf_begin_elf (Elf *elf, Dwarf_Cmd cmd, Elf_Scn *scngrp)
> +{
> + return dwarf_begin_elf_type (elf, cmd, DWARF_T_AUTO, scngrp);
> +}
> INTDEF(dwarf_begin_elf)
> +
> +Dwarf_Type
> +dwarf_get_type (Dwarf *dwarf)
> +{
> + if (dwarf == NULL)
> + return DWARF_T_AUTO;
> +
> + return dwarf->type;
> +}
> +INTDEF(dwarf_get_type)
> +
> diff --git a/libdw/libdw.h b/libdw/libdw.h
> index 9096f301f6e3..255e34f08d78 100644
> --- a/libdw/libdw.h
> +++ b/libdw/libdw.h
> @@ -1,5 +1,6 @@
> /* Interfaces for libdw.
> Copyright (C) 2002-2010, 2013, 2014, 2016, 2018 Red Hat, Inc.
> + Copyright (C) 2026 Mark J. Wielaard <[email protected]>
> This file is part of elfutils.
>
> This file is free software; you can redistribute it and/or modify
> @@ -43,6 +44,20 @@ typedef enum
> }
> Dwarf_Cmd;
>
> +/* DWARF type. Either plain DWARF, split DWARF DWOs (including DWP
> + files) or GNU LTO. When used with dwarf_begin_type or
> + dwarf_begin_elf_type DWARF_T_AUTO tries to get PLAIN, DWO, GNU_LTO
> + in that order. Enum values have a numeric value indicating their
> + priority in auto mode. */
> +typedef enum
> + {
> + DWARF_T_AUTO = 0, /* Automatic selection (PLAIN > DWO > GNU_LTO). */
> + DWARF_T_GNU_LTO = 16, /* GNU LTO (.gnu.debuglto_.debug_*). */
> + DWARF_T_DWO = 32, /* Split DWARF (.debug_*.dwo and dwp indexes). */
> + DWARF_T_PLAIN = 64, /* Standard DWARF (.debug_*). */
> + }
> +Dwarf_Type;
> +
>
> /* Callback results. */
> enum
> @@ -234,12 +249,32 @@ typedef void (*__noreturn_attribute__ Dwarf_OOM) (void);
> extern "C" {
> #endif
>
> -/* Create a handle for a new debug session. */
> +/* Create a handle for a new debug session.
> + Calls dwarf_begin_type (fildes, cmd, DWARF_T_AUTO). */
> extern Dwarf *dwarf_begin (int fildes, Dwarf_Cmd cmd);
>
> -/* Create a handle for a new debug session for an ELF file. */
> +/* Create a handle for a new debug session given a specific type.
> + When type is DWARF_T_AUTO will try to get PLAIN, DWO, GNU_LTO in
> + that order. Will return NULL if the requested type is not available
> + in the file. */
> +extern Dwarf *dwarf_begin_type (int fildes, Dwarf_Cmd cmd, Dwarf_Type type);
> +
> +/* Create a handle for a new debug session for an ELF file.
> + Calls dwarf_begin_type (elf, cmd, DWARF_T_AUTO, scngrp). */
> extern Dwarf *dwarf_begin_elf (Elf *elf, Dwarf_Cmd cmd, Elf_Scn *scngrp);
>
> +/* Create a handle for a new debug session given a specific type.
> + When type is DWARF_T_AUTO will try to get PLAIN, DWO, GNU_LTO in
> + that order. Will return NULL if the requested type is not available
> + in the file. If scngrp isn't NULL it must be a SHT_GROUP section
> + and only sections in this group will be used. */
> +extern Dwarf *dwarf_begin_elf_type (Elf *elf, Dwarf_Cmd cmd, Dwarf_Type type,
> + Elf_Scn *scngrp);
> +
> +/* Returns the DWARF type of the givn Dwarf descriptor. Return
> + DWARF_T_AUTO (zero) if the descriptor is NULL. */
> +extern Dwarf_Type dwarf_get_type (Dwarf *dwarf);
> +
> /* Retrieve ELF descriptor used for DWARF access. */
> extern Elf *dwarf_getelf (Dwarf *dwarf);
>
> diff --git a/libdw/libdw.map b/libdw/libdw.map
> index f201fd30f2e1..af5726cc7284 100644
> --- a/libdw/libdw.map
> +++ b/libdw/libdw.map
> @@ -416,4 +416,7 @@ ELFUTILS_0.196 {
> global:
> dwelf_dwarf_debug_sup;
> dwelf_dwarf_debug_dwp;
> -} ELFUTILS_0.193;
> \ No newline at end of file
> + dwarf_begin_elf_type;
> + dwarf_begin_type;
> + dwarf_get_type;
> +} ELFUTILS_0.193;
> diff --git a/libdw/libdwP.h b/libdw/libdwP.h
> index 7086dddc6962..92e0bb1c4540 100644
> --- a/libdw/libdwP.h
> +++ b/libdw/libdwP.h
> @@ -156,15 +156,6 @@ enum
>
> #include "dwarf_sig8_hash.h"
>
> -/* The type of Dwarf object, sorted by preference
> - (if there is a higher order type, we pick that one over the others). */
> -enum dwarf_type
> - {
> - TYPE_UNKNOWN = 0,
> - TYPE_GNU_LTO = 16,
> - TYPE_DWO = 32,
> - TYPE_PLAIN = 64,
> - };
>
> /* This is the structure representing the debugging state. */
> struct Dwarf
> @@ -259,7 +250,7 @@ struct Dwarf
> /* Similar for addrx/constx, which will come from .debug_addr section. */
> struct Dwarf_CU *fake_addr_cu;
>
> - enum dwarf_type type;
> + Dwarf_Type type;
>
> /* Supporting lock for internal memory handling. Ensures threads that have
> an entry in the mem_tails array are not disturbed by new threads doing
> @@ -488,6 +479,8 @@ INTDECL (dwarf_attr)
> INTDECL (dwarf_attr_integrate)
> INTDECL (dwarf_begin)
> INTDECL (dwarf_begin_elf)
> +INTDECL (dwarf_begin_elf_type)
> +INTDECL (dwarf_begin_type)
> INTDECL (dwarf_child)
> INTDECL (dwarf_cu_dwp_section_info)
> INTDECL (dwarf_default_lower_bound)
> @@ -510,6 +503,7 @@ INTDECL (dwarf_getaranges)
> INTDECL (dwarf_getlocation_die)
> INTDECL (dwarf_getsrcfiles)
> INTDECL (dwarf_getsrclines)
> +INTDECL (dwarf_get_type)
> INTDECL (dwarf_get_units)
> INTDECL (dwarf_hasattr)
> INTDECL (dwarf_haschildren)
> diff --git a/tests/Makefile.am b/tests/Makefile.am
> index 7c6c776a0410..a1a9d2f0826c 100644
> --- a/tests/Makefile.am
> +++ b/tests/Makefile.am
> @@ -58,6 +58,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx
> scnnames sectiondump \
> elfgetzdata elfputzdata zstrptr emptyfile vendorelf \
> fillfile dwarf_default_lower_bound \
> dwarf_language_lower_bound dwarf-die-addr-die \
> + dwarf-type \
> get-units-invalid get-units-split attr-integrate-skel \
> all-dwarf-ranges unit-info next_cfi \
> elfcopy addsections xlate_notes elfrdwrnop \
> @@ -212,6 +213,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh
> newfile test-nlist \
> emptyfile vendorelf fillfile dwarf_default_lower_bound \
> dwarf_language_lower_bound \
> run-dwarf-die-addr-die.sh \
> + run-dwarf-type.sh \
> run-get-units-invalid.sh run-get-units-split.sh \
> run-attr-integrate-skel.sh \
> run-all-dwarf-ranges.sh run-unit-info.sh \
> @@ -586,6 +588,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
> testfile-bpf-reloc.expect.bz2 testfile-bpf-reloc.o.bz2 \
> testfile-m68k-core.bz2 testfile-m68k.bz2 testfile-m68k-s.bz2 \
> run-dwarf-die-addr-die.sh \
> + run-dwarf-type.sh \
> run-get-units-invalid.sh run-get-units-split.sh \
> testfile-hello4.dwo.bz2 testfile-hello5.dwo.bz2 \
> testfile-splitdwarf-4.bz2 testfile-splitdwarf-5.bz2 \
> @@ -898,6 +901,7 @@ fillfile_LDADD = $(libelf)
> dwarf_default_lower_bound_LDADD = $(libdw)
> dwarf_language_lower_bound_LDADD = $(libdw)
> dwarf_die_addr_die_LDADD = $(libdw)
> +dwarf_type_LDADD = $(libdw)
> get_units_invalid_LDADD = $(libdw)
> get_units_split_LDADD = $(libdw)
> attr_integrate_skel_LDADD = $(libdw)
> diff --git a/tests/dwarf-type.c b/tests/dwarf-type.c
> new file mode 100644
> index 000000000000..8663a5af98a1
> --- /dev/null
> +++ b/tests/dwarf-type.c
> @@ -0,0 +1,84 @@
> +/* Test dwarf_begin_type and dwarf_get_type functionality.
> + Copyright (C) 2024 Red Hat, Inc.
> + This file is part of elfutils.
> +
> + This file is free software; you can redistribute it and/or modify
> + it under the terms of the GNU General Public License as published by
> + the Free Software Foundation; either version 3 of the License, or
> + (at your option) any later version.
> +
> + elfutils is distributed in the hope that it will be useful, but
> + WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> +
> +#include <config.h>
> +#include <dwarf.h>
> +#include <libdw.h>
> +#include <fcntl.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +
> +static const char *
> +type_name (Dwarf_Type type)
> +{
> + switch (type)
> + {
> + case DWARF_T_AUTO: return "AUTO";
> + case DWARF_T_PLAIN: return "PLAIN";
> + case DWARF_T_DWO: return "DWO";
> + case DWARF_T_GNU_LTO: return "GNU_LTO";
> + default: return "UNKNOWN";
> + }
> +}
> +
> +Dwarf_Type
> +name_type (const char *name)
> +{
> + if (strcmp (name, "AUTO") == 0)
> + return DWARF_T_AUTO;
> + else if (strcmp (name, "PLAIN") == 0)
> + return DWARF_T_PLAIN;
> + else if (strcmp (name, "DWO") == 0)
> + return DWARF_T_DWO;
> + else if (strcmp (name, "GNU_LTO") == 0)
> + return DWARF_T_GNU_LTO;
> + else
> + {
> + printf ("Unknown type name: %s\n", name);
> + exit (1);
> + }
> +}
> +
> +int
> +main (int argc, char *argv[])
> +{
> + if (argc < 3)
> + {
> + fprintf (stderr, "Need <type> and <testfile>\n");
> + return 1;
> + }
> +
> + const char *tname = argv[1];
> + const char *file = argv[2];
> + Dwarf_Type type = name_type (tname);
> +
> + int fd = open (file, O_RDONLY);
> + if (fd < 0)
> + {
> + printf ("Cannot open %s\n", file);
> + return 1;
> + }
> +
> + Dwarf *dbg = dwarf_begin_type (fd, DWARF_C_READ, type);
> + Dwarf_Type dwarf_type = dwarf_get_type (dbg);
> + printf ("%s: %s -> %s\n", file, type_name (type), type_name (dwarf_type));
> + dwarf_end (dbg);
> + close (fd);
> + return 0;
> +}
> diff --git a/tests/run-dwarf-type.sh b/tests/run-dwarf-type.sh
> new file mode 100755
> index 000000000000..954d78519a4c
> --- /dev/null
> +++ b/tests/run-dwarf-type.sh
> @@ -0,0 +1,53 @@
> +#!/bin/sh
> +# Copyright (C) 2026 Mark J. Wielaard <[email protected]>
> +# This file is part of elfutils.
> +#
> +# This file is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# elfutils is distributed in the hope that it will be useful, but
> +# WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program. If not, see <http://www.gnu.org/licenses/>.
> +
> +. $srcdir/test-subr.sh
> +
> +# Test dwarf_begin_type and dwarf_get_type with fat LTO file
> +# (file with both PLAIN and GNU_LTO sections)
> +testfiles testfile-dwarf5-fat-lto.o
> +
> +testrun_compare ${abs_builddir}/dwarf-type AUTO testfile-dwarf5-fat-lto.o
> <<\EOF
> +testfile-dwarf5-fat-lto.o: AUTO -> PLAIN
> +EOF
> +testrun_compare ${abs_builddir}/dwarf-type PLAIN testfile-dwarf5-fat-lto.o
> <<\EOF
> +testfile-dwarf5-fat-lto.o: PLAIN -> PLAIN
> +EOF
> +testrun_compare ${abs_builddir}/dwarf-type DWO testfile-dwarf5-fat-lto.o
> <<\EOF
> +testfile-dwarf5-fat-lto.o: DWO -> AUTO
> +EOF
> +testrun_compare ${abs_builddir}/dwarf-type GNU_LTO testfile-dwarf5-fat-lto.o
> <<\EOF
> +testfile-dwarf5-fat-lto.o: GNU_LTO -> GNU_LTO
> +EOF
> +
> +# Test dwarf_begin_type and dwarf_get_type with DWO (only) file
> +testfiles testfile-hello5.dwo
> +
> +testrun_compare ${abs_builddir}/dwarf-type AUTO testfile-hello5.dwo <<\EOF
> +testfile-hello5.dwo: AUTO -> DWO
> +EOF
> +testrun_compare ${abs_builddir}/dwarf-type PLAIN testfile-hello5.dwo <<\EOF
> +testfile-hello5.dwo: PLAIN -> AUTO
> +EOF
> +testrun_compare ${abs_builddir}/dwarf-type DWO testfile-hello5.dwo <<\EOF
> +testfile-hello5.dwo: DWO -> DWO
> +EOF
> +testrun_compare ${abs_builddir}/dwarf-type GNU_LTO testfile-hello5.dwo <<\EOF
> +testfile-hello5.dwo: GNU_LTO -> AUTO
> +EOF
> +
> +exit 0
> --
> 2.55.0
>