dwarf_language returns a DW_LNAME constant for a CU Die. If the CU Die
has a DW_AT_language_name attribute dwarf_language will return it and
the DW_AT_language_version attribute value. Otherwise dwarf_language
will lookup the (old style) DW_AT_language attribute and translate the
DW_LANG constant into a DW_LNAME constant and version using a new
static function srclang_to_language.

The dwarf_language_lower_bound function works just like the
dwarf_default_lower_bound function, but takes a DW_LNAME constant
instead of a DW_LANG constant.

Adds a new test to make sure dwarf_language_lower_bound handles all
known DW_LNAME constants.

    * NEWS: Add new functions.
    * libdw/libdw.map (ELFUTILS_0.193): New section with new functions.
    * libdw/libdw.h (dwarf_srclang): Add comment explaining this
    returns DW_LANG constants.
    (dwarf_language): New function.
    (dwarf_default_lower_bound): Add comment explaining this works on
    DW_LANG constants.
    (dwarf_language_lower_bound): New function.
    * libdw/libdwP.h: INTDECL dwarf_language and
    dwarf_language_lower_bound.
    * libdw/dwarf_srclang.c (srclang_to_language): New function.
    (dwarf_language): Likewise.
    * libdw/dwarf_default_lower_bound.c (dwarf_language_lower_bound):
    New function.
    * libdw/dwarf_getfuncs.c (dwarf_getfuncs):  Use dwarf_language and
    dwarf_language_lower_bound.
    * libdw/dwarf_aggregate_size.c (array_size): Likewise.
    * tests/dwarf_language_lower_bound.c: New test.
    * tests/Makefile.am (check_PROGRAMS): Add dwarf_language_lower_bound.
    (TESTS): Likewise.
    (dwarf_language_lower_bound_LDADD): New variable.

Signed-off-by: Mark Wielaard <m...@klomp.org>
---
 NEWS                               |   2 +
 libdw/dwarf_aggregate_size.c       |   8 +-
 libdw/dwarf_default_lower_bound.c  |  68 ++++++-
 libdw/dwarf_getfuncs.c             |  12 +-
 libdw/dwarf_srclang.c              | 311 +++++++++++++++++++++++++++++
 libdw/libdw.h                      |  26 ++-
 libdw/libdw.map                    |   6 +
 libdw/libdwP.h                     |   2 +
 tests/Makefile.am                  |   5 +-
 tests/dwarf_language_lower_bound.c |  72 +++++++
 10 files changed, 493 insertions(+), 19 deletions(-)
 create mode 100644 tests/dwarf_language_lower_bound.c

diff --git a/NEWS b/NEWS
index 4cb5b2260fec..664c125b6eaa 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,8 @@ Version 0.193 (one after 0.192)
 
 debuginfod: Add CORS (webapp access) support to webapi.
 
+libdw: Add dwarf_language and dwarf_language_lower_bound functions.
+
 Version 0.192 "New rules, faster tools"
 
 CONDUCT: A new code of conduct has been adopted.  See the
diff --git a/libdw/dwarf_aggregate_size.c b/libdw/dwarf_aggregate_size.c
index 8216ae42e424..6579212abe96 100644
--- a/libdw/dwarf_aggregate_size.c
+++ b/libdw/dwarf_aggregate_size.c
@@ -1,5 +1,6 @@
 /* Compute size of an aggregate type from DWARF.
    Copyright (C) 2010, 2014, 2016 Red Hat, Inc.
+   Copyright (C) 2025, Mark J. Wielaard <m...@klomp.org>
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -131,10 +132,11 @@ array_size (Dwarf_Die *die, Dwarf_Word *size,
                }
              else
                {
+                 Dwarf_Word lang;
                  Dwarf_Die cu = CUDIE (die->cu);
-                 int lang = INTUSE(dwarf_srclang) (&cu);
-                 if (lang == -1
-                     || INTUSE(dwarf_default_lower_bound) (lang, &lower) != 0)
+                 int res = INTUSE(dwarf_language) (&cu, &lang, NULL);
+                 if (res < 0
+                     || INTUSE(dwarf_language_lower_bound) (lang, &lower) != 0)
                    return -1;
                }
              if (unlikely (lower > upper))
diff --git a/libdw/dwarf_default_lower_bound.c 
b/libdw/dwarf_default_lower_bound.c
index f04ae46de0c5..7cc808b22488 100644
--- a/libdw/dwarf_default_lower_bound.c
+++ b/libdw/dwarf_default_lower_bound.c
@@ -1,6 +1,6 @@
 /* Get the default subrange lower bound for a given language.
    Copyright (C) 2016 Red Hat, Inc.
-   Copyright (C) 2024 Mark J. Wielaard <m...@klomp.org>
+   Copyright (C) 2024, 2025 Mark J. Wielaard <m...@klomp.org>
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -125,3 +125,69 @@ dwarf_default_lower_bound (int lang, Dwarf_Sword *result)
     }
 }
 INTDEF (dwarf_default_lower_bound)
+
+/* Determine default lower bound from language, as per the DWARF6
+   https://dwarfstd.org/languages-v6.html table.  */
+int
+dwarf_language_lower_bound (Dwarf_Word lang, Dwarf_Sword *result)
+{
+  switch (lang)
+    {
+    case DW_LNAME_BLISS:
+    case DW_LNAME_C:
+    case DW_LNAME_C_plus_plus:
+    case DW_LNAME_Crystal:
+    case DW_LNAME_D:
+    case DW_LNAME_Dylan:
+    case DW_LNAME_Go:
+    case DW_LNAME_Haskell:
+    case DW_LNAME_Java:
+    case DW_LNAME_Kotlin:
+    case DW_LNAME_ObjC:
+    case DW_LNAME_ObjC_plus_plus:
+    case DW_LNAME_OCaml:
+    case DW_LNAME_OpenCL_C:
+    case DW_LNAME_Python:
+    case DW_LNAME_RenderScript:
+    case DW_LNAME_Rust:
+    case DW_LNAME_Swift:
+    case DW_LNAME_UPC:
+    case DW_LNAME_Zig:
+    case DW_LNAME_Assembly:
+    case DW_LNAME_C_sharp:
+    case DW_LNAME_Mojo:
+    case DW_LNAME_GLSL:
+    case DW_LNAME_GLSL_ES:
+    case DW_LNAME_HLSL:
+    case DW_LNAME_OpenCL_CPP:
+    case DW_LNAME_CPP_for_OpenCL:
+    case DW_LNAME_SYCL:
+    case DW_LNAME_Ruby:
+    case DW_LNAME_Move:
+    case DW_LNAME_Hylo:
+    case DW_LNAME_HIP:
+    case DW_LNAME_Odin:
+    case DW_LNAME_P4:
+    case DW_LNAME_Metal:
+    case DW_LNAME_V:
+      *result = 0;
+      return 0;
+
+    case DW_LNAME_Ada:
+    case DW_LNAME_Cobol:
+    case DW_LNAME_Fortran:
+    case DW_LNAME_Julia:
+    case DW_LNAME_Modula2:
+    case DW_LNAME_Modula3:
+    case DW_LNAME_Pascal:
+    case DW_LNAME_PLI:
+    case DW_LNAME_Algol68:
+      *result = 1;
+      return 0;
+
+    default:
+      __libdw_seterrno (DWARF_E_UNKNOWN_LANGUAGE);
+      return -1;
+    }
+}
+INTDEF (dwarf_language_lower_bound)
diff --git a/libdw/dwarf_getfuncs.c b/libdw/dwarf_getfuncs.c
index f6968f0081d4..bfc5f5727bbd 100644
--- a/libdw/dwarf_getfuncs.c
+++ b/libdw/dwarf_getfuncs.c
@@ -1,6 +1,6 @@
 /* Get function information.
    Copyright (C) 2005, 2013, 2015 Red Hat, Inc.
-   Copyright (C) 2024 Mark J. Wielaard <m...@klomp.org>
+   Copyright (C) 2024, 2025 Mark J. Wielaard <m...@klomp.org>
    This file is part of elfutils.
    Written by Ulrich Drepper <drep...@redhat.com>, 2005.
 
@@ -101,13 +101,9 @@ dwarf_getfuncs (Dwarf_Die *cudie, int (*callback) 
(Dwarf_Die *, void *),
                || INTUSE(dwarf_tag) (cudie) != DW_TAG_compile_unit))
     return -1;
 
-  int lang = INTUSE(dwarf_srclang) (cudie);
-  bool c_cu = (lang == DW_LANG_C89
-              || lang == DW_LANG_C
-              || lang == DW_LANG_C99
-              || lang == DW_LANG_C11
-              || lang == DW_LANG_C17
-              || lang == DW_LANG_C23);
+  Dwarf_Word lang;
+  bool c_cu = (INTUSE(dwarf_language) (cudie, &lang, NULL) == 0
+              && lang == DW_LNAME_C);
 
   struct visitor_info v = { callback, arg, (void *) offset, NULL, c_cu };
   struct Dwarf_Die_Chain chain = { .die = CUDIE (cudie->cu),
diff --git a/libdw/dwarf_srclang.c b/libdw/dwarf_srclang.c
index 77bd58c2f703..aede449c69b6 100644
--- a/libdw/dwarf_srclang.c
+++ b/libdw/dwarf_srclang.c
@@ -1,5 +1,6 @@
 /* Return source language attribute of DIE.
    Copyright (C) 2003-2010 Red Hat, Inc.
+   Copyright (C) 2025 Mark J. Wielaard <m...@klomp.org>
    This file is part of elfutils.
    Written by Ulrich Drepper <drep...@redhat.com>, 2003.
 
@@ -35,6 +36,275 @@
 #include "libdwP.h"
 
 
+static int srclang_to_language (Dwarf_Word srclang,
+                               Dwarf_Word *lname,
+                               Dwarf_Word *lversion)
+{
+  switch (srclang)
+    {
+    case DW_LANG_C89:
+      *lname = DW_LNAME_C;
+      *lversion = 198912;
+      return 0;
+    case DW_LANG_C:
+      *lname = DW_LNAME_C;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_Ada83:
+      *lname = DW_LNAME_Ada;
+      *lversion = 1983;
+      return 0;
+    case DW_LANG_C_plus_plus:
+      *lname = DW_LNAME_C_plus_plus;
+      *lversion = 199711;
+      return 0;
+    case DW_LANG_Cobol74:
+      *lname = DW_LNAME_Cobol;
+      *lversion = 1974;
+      return 0;
+    case DW_LANG_Cobol85:
+      *lname = DW_LNAME_Cobol;
+      *lversion = 1985;
+      return 0;
+    case DW_LANG_Fortran77:
+      *lname = DW_LNAME_Fortran;
+      *lversion = 1977;
+      return 0;
+    case DW_LANG_Fortran90:
+      *lname = DW_LNAME_Fortran;
+      *lversion = 1990;
+      return 0;
+    case DW_LANG_Pascal83:
+      *lname = DW_LNAME_Pascal;
+      *lversion = 1983;
+      return 0;
+    case DW_LANG_Modula2:
+      *lname = DW_LNAME_Modula2;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_Java:
+      *lname = DW_LNAME_Java;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_C99:
+      *lname = DW_LNAME_C;
+      *lversion = 199901;
+      return 0;
+    case DW_LANG_Ada95:
+      *lname = DW_LNAME_Ada;
+      *lversion = 1995;
+      return 0;
+    case DW_LANG_Fortran95:
+      *lname = DW_LNAME_Fortran;
+      *lversion = 1995;
+      return 0;
+    case DW_LANG_PLI:
+      *lname = DW_LNAME_PLI;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_ObjC:
+      *lname = DW_LNAME_ObjC;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_ObjC_plus_plus:
+      *lname = DW_LNAME_ObjC_plus_plus;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_UPC:
+      *lname = DW_LNAME_UPC;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_D:
+      *lname = DW_LNAME_D;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_Python:
+      *lname = DW_LNAME_Python;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_OpenCL:
+      *lname = DW_LNAME_OpenCL_C;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_Go:
+      *lname = DW_LNAME_Go;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_Modula3:
+      *lname = DW_LNAME_Modula3;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_Haskell:
+      *lname = DW_LNAME_Haskell;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_C_plus_plus_03:
+      *lname = DW_LNAME_C_plus_plus;
+      *lversion = 199711; /* This is really just c++98. */
+      return 0;
+    case DW_LANG_C_plus_plus_11:
+      *lname = DW_LNAME_C_plus_plus;
+      *lversion = 201103;
+      return 0;
+    case DW_LANG_OCaml:
+      *lname = DW_LNAME_OCaml;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_Rust:
+      *lname = DW_LNAME_Rust;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_C11:
+      *lname = DW_LNAME_C;
+      *lversion = 201112;
+      return 0;
+    case DW_LANG_Swift:
+      *lname = DW_LNAME_Swift;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_Julia:
+      *lname = DW_LNAME_Julia;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_C_plus_plus_14:
+      *lname = DW_LNAME_C_plus_plus;
+      *lversion = 201402;
+      return 0;
+    case DW_LANG_Fortran03:
+      *lname = DW_LNAME_Fortran;
+      *lversion = 2003;
+      return 0;
+    case DW_LANG_Fortran08:
+      *lname = DW_LNAME_Fortran;
+      *lversion = 2008;
+      return 0;
+    case DW_LANG_RenderScript:
+      *lname = DW_LNAME_RenderScript;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_BLISS:
+      *lname = DW_LNAME_BLISS;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_Kotlin:
+      *lname = DW_LNAME_Kotlin;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_Zig:
+      *lname = DW_LNAME_Zig;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_Crystal:
+      *lname = DW_LNAME_Crystal;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_C_plus_plus_17:
+      *lname = DW_LANG_C_plus_plus;
+      *lversion = 201703;
+      return 0;
+    case DW_LANG_C_plus_plus_20:
+      *lname = DW_LANG_C_plus_plus;
+      *lversion = 202002;
+      return 0;
+    case DW_LANG_C17:
+      *lname = DW_LNAME_C;
+      *lversion = 201710;
+      return 0;
+    case DW_LANG_Fortran18:
+      *lname = DW_LNAME_Fortran;
+      *lversion = 2018;
+      return 0;
+    case DW_LANG_Ada2005:
+      *lname = DW_LNAME_Ada;
+      *lversion = 2005;
+      return 0;
+    case DW_LANG_Ada2012:
+      *lname = DW_LNAME_Ada;
+      *lversion = 2012;
+      return 0;
+    case DW_LANG_HIP:
+      *lname = DW_LNAME_HIP;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_Assembly:
+    case DW_LANG_Mips_Assembler:
+      *lname = DW_LNAME_Assembly;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_C_sharp:
+      *lname = DW_LNAME_C_sharp;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_Mojo:
+      *lname = DW_LNAME_Mojo;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_GLSL:
+      *lname = DW_LNAME_GLSL;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_GLSL_ES:
+      *lname = DW_LNAME_GLSL_ES;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_HLSL:
+      *lname = DW_LNAME_HLSL;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_OpenCL_CPP:
+      *lname = DW_LNAME_OpenCL_CPP;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_CPP_for_OpenCL:
+      *lname = DW_LNAME_CPP_for_OpenCL;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_SYCL:
+      *lname = DW_LNAME_SYCL;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_C_plus_plus_23:
+      *lname = DW_LNAME_C_plus_plus;
+      *lversion = 202302;
+      return 0;
+    case DW_LANG_Odin:
+      *lname = DW_LNAME_Odin;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_P4:
+      *lname = DW_LNAME_P4;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_Metal:
+      *lname = DW_LNAME_Metal;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_C23:
+      *lname = DW_LNAME_C;
+      *lversion = 202311;
+      return 0;
+    case DW_LANG_Fortran23:
+      *lname = DW_LNAME_Fortran;
+      *lversion = 2023;
+      return 0;
+    case DW_LANG_Ruby:
+      *lname = DW_LNAME_Ruby;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_Move:
+      *lname = DW_LNAME_Move;
+      *lversion = 0;
+      return 0;
+    case DW_LANG_Hylo:
+      *lname = DW_LNAME_Hylo;
+      *lversion = 0;
+      return 0;
+    default:
+      __libdw_seterrno (DWARF_E_UNKNOWN_LANGUAGE);
+      return -1;
+    }
+}
+
 NEW_VERSION (dwarf_srclang, ELFUTILS_0.143)
 int
 dwarf_srclang (Dwarf_Die *die)
@@ -48,3 +318,44 @@ dwarf_srclang (Dwarf_Die *die)
 }
 NEW_INTDEF (dwarf_srclang)
 OLD_VERSION (dwarf_srclang, ELFUTILS_0.122)
+
+int
+dwarf_language (Dwarf_Die *cudie, Dwarf_Word *lname, Dwarf_Word *lversion)
+{
+  Dwarf_Attribute attr;
+  Dwarf_Word val;
+
+  int res = INTUSE(dwarf_formudata) (INTUSE(dwarf_attr_integrate)
+                                    (cudie, DW_AT_language_name, &attr),
+                                    &val);
+  if (res == 0)
+    {
+      *lname = val;
+      if (lversion != NULL)
+       {
+         /* We like to get the version, but given we already have the
+            lang, we will ignore errors here and just return zero as
+            version.  */
+         res = INTUSE(dwarf_formudata) (INTUSE(dwarf_attr_integrate)
+                                        (cudie, DW_AT_language_version,
+                                         &attr), &val);
+         *lversion = (res == 0) ? val : 0;
+       }
+    }
+  else
+    {
+      /* Try the get the old style pre DWARF6 DW_AT_LANG and translate
+        that to the new language name/version style.  */
+      res = INTUSE(dwarf_formudata) (INTUSE(dwarf_attr_integrate)
+                                    (cudie, DW_AT_language, &attr), &val);
+      if (res == 0)
+       {
+         Dwarf_Word version;
+         res = srclang_to_language (val, lname, (lversion == NULL
+                                                 ? &version : lversion));
+       }
+    }
+
+  return res;
+}
+INTDEF (dwarf_language)
diff --git a/libdw/libdw.h b/libdw/libdw.h
index ec4713a6772a..9096f301f6e3 100644
--- a/libdw/libdw.h
+++ b/libdw/libdw.h
@@ -579,8 +579,15 @@ extern int dwarf_bitoffset (Dwarf_Die *die);
 /* Return array order attribute of DIE.  */
 extern int dwarf_arrayorder (Dwarf_Die *die);
 
-/* Return source language attribute of DIE.  */
-extern int dwarf_srclang (Dwarf_Die *die);
+/* Return DW_LANG source language of CU DIE.
+   Returns the DW_LANG constant on success, -1 otherwise.  */
+extern int dwarf_srclang (Dwarf_Die *cudie);
+
+/* Provides the DW_LNAME source language and version of the given CU
+   DIE.  LVERSION may be NULL. Returns zero on success.  */
+extern int dwarf_language (Dwarf_Die *cudie,
+                          Dwarf_Word *lname,
+                          Dwarf_Word *lversion) __nonnull_attribute__ (2);
 
 
 /* Get abbreviation at given offset for given DIE.  */
@@ -829,13 +836,20 @@ extern int dwarf_getlocation_attr (Dwarf_Attribute *attr,
    For DW_TAG_array_type it can apply much more complex rules.  */
 extern int dwarf_aggregate_size (Dwarf_Die *die, Dwarf_Word *size);
 
-/* Given a language code, as returned by dwarf_srclan, get the default
-   lower bound for a subrange type without a lower bound attribute.
-   Returns zero on success or -1 on failure when the given language
-   wasn't recognized.  */
+/* Given a DW_LANG language code, as returned by dwarf_srclang, get
+   the default lower bound for a subrange type without a lower bound
+   attribute.  Returns zero on success or -1 on failure when the given
+   language wasn't recognized.  */
 extern int dwarf_default_lower_bound (int lang, Dwarf_Sword *result)
   __nonnull_attribute__ (2);
 
+/* Given a DW_LNAME language code, as returned by dwarf_language, get
+   the default lower bound for a subrange type without a lower bound
+   attribute.  Returns zero on success or -1 on failure when the given
+   language wasn't recognized.  */
+extern int dwarf_language_lower_bound (Dwarf_Word lname, Dwarf_Sword *result)
+  __nonnull_attribute__ (2);
+
 /* Return scope DIEs containing PC address.
    Sets *SCOPES to a malloc'd array of Dwarf_Die structures,
    and returns the number of elements in the array.
diff --git a/libdw/libdw.map b/libdw/libdw.map
index bc53385f61a6..e7baf3c4da2f 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -385,3 +385,9 @@ ELFUTILS_0.192 {
     dwfl_frame_unwound_source;
     dwfl_unwound_source_str;
 } ELFUTILS_0.191;
+
+ELFUTILS_0.193 {
+  global:
+    dwarf_language;
+    dwarf_language_lower_bound;
+} ELFUTILS_0.192;
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index 0cff5c26cb82..7d9c12607c0c 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -496,6 +496,8 @@ INTDECL (dwarf_hasattr)
 INTDECL (dwarf_haschildren)
 INTDECL (dwarf_haspc)
 INTDECL (dwarf_highpc)
+INTDECL (dwarf_language)
+INTDECL (dwarf_language_lower_bound)
 INTDECL (dwarf_lowpc)
 INTDECL (dwarf_nextcu)
 INTDECL (dwarf_next_unit)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 335f192583da..bf5a9b228126 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -56,7 +56,8 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames 
sectiondump \
                  elfstrtab dwfl-proc-attach \
                  elfshphehdr elfstrmerge dwelfgnucompressed elfgetchdr \
                  elfgetzdata elfputzdata zstrptr emptyfile vendorelf \
-                 fillfile dwarf_default_lower_bound dwarf-die-addr-die \
+                 fillfile dwarf_default_lower_bound \
+                 dwarf_language_lower_bound dwarf-die-addr-die \
                  get-units-invalid get-units-split attr-integrate-skel \
                  all-dwarf-ranges unit-info next_cfi \
                  elfcopy addsections xlate_notes elfrdwrnop \
@@ -203,6 +204,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile 
test-nlist \
        run-compress-test.sh \
        run-readelf-zdebug.sh run-readelf-zdebug-rel.sh \
        emptyfile vendorelf fillfile dwarf_default_lower_bound \
+       dwarf_language_lower_bound \
        run-dwarf-die-addr-die.sh \
        run-get-units-invalid.sh run-get-units-split.sh \
        run-attr-integrate-skel.sh \
@@ -864,6 +866,7 @@ emptyfile_LDADD = $(libelf)
 vendorelf_LDADD = $(libelf)
 fillfile_LDADD = $(libelf)
 dwarf_default_lower_bound_LDADD = $(libdw)
+dwarf_language_lower_bound_LDADD = $(libdw)
 dwarf_die_addr_die_LDADD = $(libdw)
 get_units_invalid_LDADD = $(libdw)
 get_units_split_LDADD = $(libdw)
diff --git a/tests/dwarf_language_lower_bound.c 
b/tests/dwarf_language_lower_bound.c
new file mode 100644
index 000000000000..6bb534ac72f3
--- /dev/null
+++ b/tests/dwarf_language_lower_bound.c
@@ -0,0 +1,72 @@
+/* Test all DW_LNAME constants are handled by dwarf_language_lower_bound.
+
+   Copyright (C) 2016 Red Hat, Inc.
+   Copyright (C) 2024, 2025 Mark J. Wielaard <m...@klomp.org>
+   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/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <dwarf.h>
+#include ELFUTILS_HEADER(dw)
+#include "../libdw/known-dwarf.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static void
+test_lang (const char *name, Dwarf_Word lang)
+{
+  Dwarf_Sword low;
+  int res = dwarf_language_lower_bound (lang, &low);
+
+  if (res != 0)
+    {
+      printf ("dwarf_language_lower_bound failed (%d) for %s\n", res, name);
+      exit (-1);
+    }
+
+  /* All currently known lower bounds are either zero or one, but
+     they don't have to.  Update test once one is a different value.  */
+  if (low != 0 && low != 1)
+    {
+      printf ("unexpected lower bound %" PRId64 " for %s\n", low, name);
+      exit (-1);
+    }
+
+  printf ("%s: %" PRId64 "\n", name, low);
+}
+
+int
+main (int argc __attribute__ ((unused)), char *argv[] __attribute__ ((unused)))
+{
+  Dwarf_Sword low;
+  /* Bad language code must fail.  */
+  if (dwarf_language_lower_bound (-1, &low) == 0)
+    {
+      printf ("Bad lang code -1 succeeded (%" PRId64 ")\n", low);
+      exit (-1);
+    }
+
+  /* Test all known language codes.  */
+#define DWARF_ONE_KNOWN_DW_LNAME(NAME, CODE) test_lang (#NAME, CODE);
+  DWARF_ALL_KNOWN_DW_LNAME
+#undef DWARF_ONE_KNOWN_DW_LNAME
+
+  return 0;
+}
-- 
2.48.1

Reply via email to