[PATCH 1/4] dwarf.h: Add DWARF v6 langauge attributes and DW_LNAME constants

2025-03-11 Thread Mark Wielaard
https://dwarfstd.org/languages-v6.html defines the DWARF v6 language
attributes, DW_AT_language_name, DW_AT_language_version and DW_LNAME
constants as usable by pre-DWARF v6 producers and consumers.

Also add new DW_LANG_V and DW_LANG_Algol68 (DWARFv5) language constants.

* config/known-dwarf.awk: Handle DW_LNAME.
* libdw/dwarf.h: Add DW_AT_language_name and DW_AT_language_version
plus all currently defined DW_LNAME constants. Add DW_LANG_V and
DW_LANG_Algol68.
* libdw/dwarf_default_lower_bound.c: Add DW_LANG_V and
DW_LANG_Algol68.

Signed-off-by: Mark Wielaard 
---
 config/known-dwarf.awk|  2 ++
 libdw/dwarf.h | 59 ++-
 libdw/dwarf_default_lower_bound.c |  2 ++
 3 files changed, 62 insertions(+), 1 deletion(-)

diff --git a/config/known-dwarf.awk b/config/known-dwarf.awk
index bc9b02dbeeef..65a9bfb61b37 100755
--- a/config/known-dwarf.awk
+++ b/config/known-dwarf.awk
@@ -1,6 +1,7 @@
 #!/bin/gawk -f
 
 ## Copyright (C) 2012, 2015 Red Hat, Inc.
+## Copyright (C) 2025 Mark J. Wielaard 
 ##
 ## This file is part of elfutils.
 ##
@@ -24,6 +25,7 @@ set == "" && $1 ~ /DW_([A-Z_]+)_([^ ]+)/ {
   sub(/^DW_/, "", set);
   sub(/_[^[:upper:]_].*$/, "", set);
   if (set ~ /LANG_.+/) set = "LANG";
+  if (set ~ /LNAME_.+/) set = "LNAME";
 }
 
 $1 ~ /DW([_A-Z]+)_([^ ]+)/ {
diff --git a/libdw/dwarf.h b/libdw/dwarf.h
index 573ffd6925b2..66017c1518dd 100644
--- a/libdw/dwarf.h
+++ b/libdw/dwarf.h
@@ -1,6 +1,6 @@
 /* This file defines standard DWARF types, structures, and macros.
Copyright (C) 2000-2011, 2014, 2016, 2017, 2018 Red Hat, Inc.
-   Copyright (C) 2024 Mark J. Wielaard 
+   Copyright (C) 2024, 2025 Mark J. Wielaard 
This file is part of elfutils.
 
This file is free software; you can redistribute it and/or modify
@@ -295,6 +295,10 @@ enum
 DW_AT_defaulted = 0x8b,
 DW_AT_loclists_base = 0x8c,
 
+/* https://dwarfstd.org/languages-v6.html  */
+DW_AT_language_name = 0x90,
+DW_AT_language_version = 0x91,
+
 DW_AT_lo_user = 0x2000,
 
 DW_AT_MIPS_fde = 0x2001,
@@ -771,6 +775,8 @@ enum
 DW_LANG_Ruby = 0x0040,  /* Ruby */
 DW_LANG_Move = 0x0041,  /* Move */
 DW_LANG_Hylo = 0x0042,  /* Hylo */
+DW_LANG_V = 0x0043, /* V Programming Language */
+DW_LANG_Algol68 = 0x0044,   /* Algol68 */
 
 DW_LANG_lo_user = 0x8000,
 DW_LANG_Mips_Assembler = 0x8001, /* Assembler */
@@ -780,6 +786,57 @@ enum
 /* Old (typo) '1' != 'I'.  */
 #define DW_LANG_PL1 DW_LANG_PLI
 
+/* https://dwarfstd.org/languages-v6.html  */
+enum
+  {
+DW_LNAME_Ada = 0x0001,
+DW_LNAME_BLISS = 0x0002,
+DW_LNAME_C = 0x0003,
+DW_LNAME_C_plus_plus = 0x0004,
+DW_LNAME_Cobol = 0x0005,
+DW_LNAME_Crystal = 0x0006,
+DW_LNAME_D = 0x0007,
+DW_LNAME_Dylan = 0x0008,
+DW_LNAME_Fortran = 0x0009,
+DW_LNAME_Go = 0x000a,
+DW_LNAME_Haskell = 0x000b,
+DW_LNAME_Java = 0x000c,
+DW_LNAME_Julia = 0x000d,
+DW_LNAME_Kotlin = 0x000e,
+DW_LNAME_Modula2 = 0x000f,
+DW_LNAME_Modula3 = 0x0010,
+DW_LNAME_ObjC = 0x0011,
+DW_LNAME_ObjC_plus_plus = 0x0012,
+DW_LNAME_OCaml = 0x0013,
+DW_LNAME_OpenCL_C = 0x0014,
+DW_LNAME_Pascal = 0x0015,
+DW_LNAME_PLI = 0x0016,
+DW_LNAME_Python = 0x0017,
+DW_LNAME_RenderScript = 0x0018,
+DW_LNAME_Rust = 0x0019,
+DW_LNAME_Swift = 0x001a,
+DW_LNAME_UPC = 0x001b,
+DW_LNAME_Zig = 0x001c,
+DW_LNAME_Assembly = 0x001d,
+DW_LNAME_C_sharp = 0x001e,
+DW_LNAME_Mojo = 0x001f,
+DW_LNAME_GLSL = 0x0020,
+DW_LNAME_GLSL_ES = 0x0021,
+DW_LNAME_HLSL = 0x0022,
+DW_LNAME_OpenCL_CPP = 0x0023,
+DW_LNAME_CPP_for_OpenCL = 0x0024,
+DW_LNAME_SYCL = 0x0025,
+DW_LNAME_Ruby = 0x0026,
+DW_LNAME_Move = 0x0027,
+DW_LNAME_Hylo = 0x0028,
+DW_LNAME_HIP = 0x0029,
+DW_LNAME_Odin = 0x002a,
+DW_LNAME_P4 = 0x002b,
+DW_LNAME_Metal = 0x002c,
+DW_LNAME_V = 0x002d,
+DW_LNAME_Algol68 = 0x002e
+  };
+
 /* DWARF identifier case encodings.  */
 enum
   {
diff --git a/libdw/dwarf_default_lower_bound.c 
b/libdw/dwarf_default_lower_bound.c
index 50639e32428e..f04ae46de0c5 100644
--- a/libdw/dwarf_default_lower_bound.c
+++ b/libdw/dwarf_default_lower_bound.c
@@ -88,6 +88,7 @@ dwarf_default_lower_bound (int lang, Dwarf_Sword *result)
 case DW_LANG_Ruby:
 case DW_LANG_Move:
 case DW_LANG_Hylo:
+case DW_LANG_V:
   *result = 0;
   return 0;
 
@@ -109,6 +110,7 @@ dwarf_default_lower_bound (int lang, Dwarf_Sword *result)
 case DW_LANG_Ada2005:
 case DW_LANG_Ada2012:
 case DW_LANG_Fortran23:
+case DW_LANG_Algol68:
   *result = 1;
   return 0;
 
-- 
2.48.1



[PATCH 4/4] readelf: Add support for printing DW_AT_language_name DW_LNAMEs

2025-03-11 Thread Mark Wielaard
Add a testfile using GCC 15 (experimental).

 * libdw/dwarf.h: Add DW_LNAME_lo_user and DW_LNAME_hi_user.
 * src/readelf.c (dwarf_lname_string): New function.
 (dwarf_lname_name): Likewise.
 (attr_callback): Handle DW_AT_language_name by calling
 dwarf_lname_name.

 * run-readelf-lnames.sh: New test.
 * testfile-lnames.bz2: New testfile.
 * tests/Makefile.am (TESTS): Add run-readelf-lnames.sh.
 (EXTRA_DIST): Add run-readelf-lnames.sh and
 testfile-lnames.bz2.

Signed-off-by: Mark Wielaard 
---
 libdw/dwarf.h   |   5 +-
 src/readelf.c   |  26 ++
 tests/Makefile.am   |   2 +
 tests/run-readelf-lnames.sh |  92 
 tests/testfile-lnames.bz2   | Bin 0 -> 2717 bytes
 5 files changed, 124 insertions(+), 1 deletion(-)
 create mode 100755 tests/run-readelf-lnames.sh
 create mode 100755 tests/testfile-lnames.bz2

diff --git a/libdw/dwarf.h b/libdw/dwarf.h
index 66017c1518dd..5c8563128644 100644
--- a/libdw/dwarf.h
+++ b/libdw/dwarf.h
@@ -834,7 +834,10 @@ enum
 DW_LNAME_P4 = 0x002b,
 DW_LNAME_Metal = 0x002c,
 DW_LNAME_V = 0x002d,
-DW_LNAME_Algol68 = 0x002e
+DW_LNAME_Algol68 = 0x002e,
+
+DW_LNAME_lo_user = 0x8000,
+DW_LNAME_hi_user = 0x
   };
 
 /* DWARF identifier case encodings.  */
diff --git a/src/readelf.c b/src/readelf.c
index 12d85472cef1..e5bc16a5bc43 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -4371,6 +4371,20 @@ dwarf_lang_string (unsigned int lang)
 }
 
 
+static const char *
+dwarf_lname_string (unsigned int lname)
+{
+  switch (lname)
+{
+#define DWARF_ONE_KNOWN_DW_LNAME(NAME, CODE) case CODE: return #NAME;
+  DWARF_ALL_KNOWN_DW_LNAME
+#undef DWARF_ONE_KNOWN_DW_LNAME
+default:
+  return NULL;
+}
+}
+
+
 static const char *
 dwarf_inline_string (unsigned int code)
 {
@@ -4678,6 +4692,15 @@ dwarf_lang_name (unsigned int lang)
 }
 
 
+static const char *
+dwarf_lname_name (unsigned int lname)
+{
+  const char *ret = dwarf_lname_string (lname);
+  return string_or_unknown (ret, lname, DW_LNAME_lo_user, DW_LNAME_hi_user,
+   false);
+}
+
+
 static const char *
 dwarf_inline_name (unsigned int code)
 {
@@ -7938,6 +7961,9 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
case DW_AT_language:
  valuestr = dwarf_lang_name (num);
  break;
+   case DW_AT_language_name:
+ valuestr = dwarf_lname_name (num);
+ break;
case DW_AT_encoding:
  valuestr = dwarf_encoding_name (num);
  break;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index bf5a9b228126..f53fd92662e3 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -165,6 +165,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile 
test-nlist \
run-test-flag-nobits.sh run-prelink-addr-test.sh \
run-dwarf-getstring.sh run-rerequest_tag.sh run-typeiter.sh \
run-readelf-d.sh run-readelf-gdb_index.sh run-unstrip-n.sh \
+   run-readelf-lnames.sh \
run-low_high_pc.sh run-macro-test.sh run-elf_cntl_gelf_getshdr.sh \
run-test-archive64.sh run-readelf-vmcoreinfo.sh \
run-readelf-mixed-corenote.sh run-dwfllines.sh \
@@ -451,6 +452,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
 run-typeiter.sh testfile59.bz2 \
 run-readelf-d.sh testlib_dynseg.so.bz2 \
 run-readelf-Dd.sh \
+run-readelf-lnames.sh testfile-lnames.bz2 \
 testfile-s390x-hash-both.bz2 \
 run-readelf-gdb_index.sh testfilegdbindex5.bz2 \
 testfilegdbindex7.bz2 testfilegdbindex9.bz2 \
diff --git a/tests/run-readelf-lnames.sh b/tests/run-readelf-lnames.sh
new file mode 100755
index ..0f5d874d0dee
--- /dev/null
+++ b/tests/run-readelf-lnames.sh
@@ -0,0 +1,92 @@
+#! /bin/sh
+# Copyright (C) 2025 Mark J. Wielaard 
+# 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 .
+
+. $srcdir/test-subr.sh
+
+# Using GCC 15.0.1 20250310 (experimental)
+#
+# echo "void foo () { }" | g++ -g -c -x c++ -std=c++20 - -o cpp.o
+# echo "int main () { }" | gcc -g -c -x c -std=c23 - -o c.o
+# gcc -g  -o testfile-lnames cpp.o c.o
+#
+# Note this version outputs an older DW_AT_language you need to
+# see the DW_AT_language_version to see the real std used.
+

[PATCH 3/4] libdw: Make dwarf_srclang forward compatible with DW_AT_language_name

2025-03-11 Thread Mark Wielaard
For programs which use dwarf_srclang it might be useful to translate a
DW_LNAME (plus version) into a DW_LANG constant if there is no
DW_AT_language constant, but there is a DW_AT_language_name (and
DW_AT_language_version).

* libdw/dwarf_srclang.c (language_to_srclang): New function.
(dwarf_srclang): If there is no DW_AT_language, try
DW_AT_language_name and DW_AT_language_version, use
language_to_srclang.

Signed-off-by: Mark Wielaard 
---
 libdw/dwarf_srclang.c | 221 +-
 1 file changed, 218 insertions(+), 3 deletions(-)

diff --git a/libdw/dwarf_srclang.c b/libdw/dwarf_srclang.c
index aede449c69b6..948f44cb455e 100644
--- a/libdw/dwarf_srclang.c
+++ b/libdw/dwarf_srclang.c
@@ -305,6 +305,201 @@ static int srclang_to_language (Dwarf_Word srclang,
 }
 }
 
+static int
+language_to_srclang (Dwarf_Word lname, Dwarf_Word lversion, Dwarf_Word *value)
+{
+  switch (lname)
+{
+case DW_LNAME_Ada:
+  if (lversion <= 1983)
+   *value = DW_LANG_Ada83;
+  else if (lversion <= 1995)
+   *value = DW_LANG_Ada95;
+  else if (lversion <= 2005)
+   *value = DW_LANG_Ada2005;
+  else
+   *value = DW_LANG_Ada2012;
+  return 0;
+case DW_LNAME_BLISS:
+  *value = DW_LANG_BLISS;
+  return 0;
+case DW_LNAME_C:
+  if (lversion == 0)
+   *value = DW_LANG_C;
+  else if (lversion <= 198912)
+   *value = DW_LANG_C89;
+  else if (lversion <= 199901)
+   *value = DW_LANG_C99;
+  else if (lversion <= 201112)
+   *value = DW_LANG_C11;
+  else if (lversion <= 201710)
+   *value = DW_LANG_C17;
+  else
+   *value = DW_LANG_C23;
+  return 0;
+case DW_LNAME_C_plus_plus:
+  if (lversion <= 199711)
+   *value = DW_LANG_C_plus_plus;
+  else if (lversion <= 201103)
+   *value = DW_LANG_C_plus_plus_11;
+  else if (lversion <= 201402)
+   *value = DW_LANG_C_plus_plus_14;
+  else if (lversion <= 201703)
+   *value = DW_LANG_C_plus_plus_17;
+  else if (lversion <= 202002)
+   *value = DW_LANG_C_plus_plus_20;
+  else
+   *value = DW_LANG_C_plus_plus_23;
+  return 0;
+case DW_LNAME_Cobol:
+  if (lversion <= 1974)
+   *value = DW_LANG_Cobol74;
+  else
+   *value = DW_LANG_Cobol85;
+  return 0;
+case DW_LNAME_Crystal:
+  *value = DW_LANG_Crystal;
+  return 0;
+case DW_LNAME_D:
+  *value = DW_LANG_D;
+  return 0;
+case DW_LNAME_Dylan:
+  *value = DW_LANG_Dylan;
+  return 0;
+case DW_LNAME_Fortran:
+  if (lversion <= 1977)
+   *value = DW_LANG_Fortran77;
+  else if (lversion <= 1990)
+   *value = DW_LANG_Fortran90;
+  else if (lversion <= 1995)
+   *value = DW_LANG_Fortran95;
+  else if (lversion <= 2003)
+   *value = DW_LANG_Fortran03;
+  else if (lversion <= 2008)
+   *value = DW_LANG_Fortran08;
+  else if (lversion <= 2018)
+   *value = DW_LANG_Fortran18;
+  else
+   *value = DW_LANG_Fortran23;
+  return 0;
+case DW_LNAME_Go:
+  *value = DW_LANG_Go;
+  return 0;
+case DW_LNAME_Haskell:
+  *value = DW_LANG_Haskell;
+  return 0;
+case DW_LNAME_Java:
+  *value = DW_LANG_Java;
+  return 0;
+case DW_LNAME_Julia:
+  *value = DW_LANG_Julia;
+  return 0;
+case DW_LNAME_Kotlin:
+  *value = DW_LANG_Kotlin;
+  return 0;
+case DW_LNAME_Modula2:
+  *value = DW_LANG_Modula2;
+  return 0;
+case DW_LNAME_Modula3:
+  *value = DW_LANG_Modula3;
+  return 0;
+case DW_LNAME_ObjC:
+  *value = DW_LANG_ObjC;
+  return 0;
+case DW_LNAME_ObjC_plus_plus:
+  *value = DW_LANG_ObjC_plus_plus;
+  return 0;
+case DW_LNAME_OCaml:
+  *value = DW_LANG_OCaml;
+  return 0;
+case DW_LNAME_OpenCL_C:
+  *value = DW_LANG_OpenCL;
+  return 0;
+case DW_LNAME_Pascal:
+  *value = DW_LANG_Pascal83;
+  return 0;
+case DW_LNAME_PLI:
+  *value = DW_LANG_PLI;
+  return 0;
+case DW_LNAME_Python:
+  *value = DW_LANG_Python;
+  return 0;
+case DW_LNAME_RenderScript:
+  *value = DW_LANG_RenderScript;
+  return 0;
+case DW_LNAME_Rust:
+  *value = DW_LANG_Rust;
+  return 0;
+case DW_LNAME_Swift:
+  *value = DW_LANG_Swift;
+  return 0;
+case DW_LNAME_UPC:
+  *value = DW_LANG_UPC;
+  return 0;
+case DW_LNAME_Zig:
+  *value = DW_LANG_Zig;
+  return 0;
+case DW_LNAME_Assembly:
+  /* DW_LANG_Assembler is not as good for compatibility.  */
+  *value = DW_LANG_Mips_Assembler;
+  return 0;
+case DW_LNAME_C_sharp:
+  *value = DW_LANG_C_sharp;
+  return 0;
+case DW_LNAME_Mojo:
+  *value = DW_LANG_Move;
+  return 0;
+case DW_LNAME_GLSL:
+  *value = DW_LANG_GLSL;
+  return 0;
+case DW_LNAME_GLSL_ES:
+  *value = DW_LANG_GLSL_ES;
+  return 0;
+case DW_LNAME_HLSL:
+  *v