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]>
Two remaining issues:
- Maybe we want a dedicated DWP type? Currently DWARF_T_DWO covers
both single DWO and DWP/multi-DWO types. But this might not be a
detail "real" users care about (only possibly useful for eu-dwp).
- Currently seeing a .debug_dwp section always assumes it is a dwo/dwp
file, but it could be a plain DWARF file with a DWP reference. Need
to actually inspect the .debug_dwp section to see which it is.
---
libdw/dwarf_begin.c | 12 +++-
libdw/dwarf_begin_elf.c | 147 +++++++++++++++++++++++++++++++---------
libdw/libdw.h | 38 ++++++++++-
libdw/libdw.map | 7 ++
libdw/libdwP.h | 14 ++--
tests/Makefile.am | 4 ++
tests/dwarf-type.c | 84 +++++++++++++++++++++++
tests/run-dwarf-type.sh | 53 +++++++++++++++
8 files changed, 312 insertions(+), 47 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 7b58ad2e2218..5586ed66ec19 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
@@ -99,37 +99,37 @@ 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;
+ return DWARF_T_DWO;
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)
@@ -186,11 +186,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);
size_t dbglen = strlen (dwarf_scnnames[cnt]);
@@ -217,7 +217,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;
}
}
@@ -482,17 +482,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;
@@ -504,7 +531,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);
@@ -541,24 +569,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;
+ }
+
+ 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)
@@ -575,7 +637,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;
@@ -644,9 +707,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)
{
@@ -661,4 +724,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..bcd76138d717 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,19 @@ typedef enum
}
Dwarf_Cmd;
+/* DWARF type. Either plain DWARF, split DWARF DWOs 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). */
+ DWARF_T_PLAIN = 64, /* Standard DWARF (.debug_*). */
+ }
+Dwarf_Type;
+
/* Callback results. */
enum
@@ -234,12 +248,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 b45647e6fa4c..404b4c1056a9 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -411,3 +411,10 @@ ELFUTILS_0.194_EXPERIMENTAL {
global:
dwflst_sample_getframes;
} ELFUTILS_0.193_EXPERIMENTAL;
+
+ELFUTILS_0.196 {
+ global:
+ dwarf_begin_elf_type;
+ dwarf_begin_type;
+ dwarf_get_type;
+} ELFUTILS_0.194_EXPERIMENTAL;
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index 628777b069da..8501337f4990 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -154,15 +154,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
@@ -257,7 +248,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
@@ -486,6 +477,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)
@@ -508,6 +501,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 d3ecd35bbf42..4493f895ce10 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 \
@@ -211,6 +212,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 \
@@ -584,6 +586,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 \
@@ -893,6 +896,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..9fff663e2a90
--- /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 < 2)
+ {
+ 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.54.0