binutils/readelf: build against msgpack, dump NT_AMDGPU_METADATA note contents

The AMDGPU HSA OS ABI (code object v3 and above) defines the
NT_AMDGPU_METADATA ELF note [1].  The content is a msgpack object
describing, among other things, the kernels present in the code object
and how to call them.

I think it would be useful for readelf to be able to display the content
of those notes.  msgpack is a structured format, a bit like JSON, except
not text-based.  It is therefore possible to dump the contents in
human-readable form without knowledge of the specific layout of the
note.

Add configury to binutils to optionally check for the msgpack C library
[2].  Add There is a new --with{,out}-msgpack configure flag, and the actual
library lookup is done using pkg-config.

If msgpack support is enabled, dumping a NT_AMDGPU_METADATA note looks
like:

    $ readelf --notes amdgpu-code-object
    Displaying notes found in: .note
      Owner                Data size        Description
      AMDGPU               0x0000040d       NT_AMDGPU_METADATA (code object metadata)
        {
          "amdhsa.kernels": [
            {
              ".args": [
                {
                  ".address_space": "global",
                  ".name": "out.coerce",
                  ".offset": 0,
                  ".size": 8,
                  ".value_kind": "global_buffer",
                },
      <snip>

If msgpack support is disabled, dump the contents as hex, as is done
with notes that are not handled in a special way.  This allows one to
decode the contents manually (maybe using a command-line msgpack
decoder) if really needed.

[1] https://llvm.org/docs/AMDGPUUsage.html#code-object-metadata
[2] https://github.com/msgpack/msgpack-c/tree/c_master

binutils/ChangeLog:

	* Makefile.am (readelf_CFLAGS): New.
	(readelf_LDADD): Add MSGPACK_LIBS.
	* Makefile.in: Re-generate.
	* config.in: Re-generate.
	* configure: Re-generate.
	* configure.ac: Add --with-msgpack flag and check for msgpack
	using pkg-config.
	* readelf.c: Include msgpack.h if HAVE_MSGPACK.
	(print_note_contents_hex): New.
	(print_indents): New.
	(dump_msgpack_obj): New.
	(dump_msgpack): New.
	(print_amdgpu_note): New.
	(process_note): Handle NT_AMDGPU_METADATA note contents.
	Use print_note_contents_hex.

Change-Id: Ia60a654e620bc32dfdb1bccd845594e2af328b84
This commit is contained in:
Simon Marchi
2022-03-16 09:01:36 -04:00
committed by Simon Marchi
parent 28cdbb183b
commit 2952f10cd7
7 changed files with 374 additions and 19 deletions
+18
View File
@@ -1,3 +1,21 @@
2022-03-16 Simon Marchi <simon.marchi@efficios.com>
* Makefile.am (readelf_CFLAGS): New.
(readelf_LDADD): Add MSGPACK_LIBS.
* Makefile.in: Re-generate.
* config.in: Re-generate.
* configure: Re-generate.
* configure.ac: Add --with-msgpack flag and check for msgpack
using pkg-config.
* readelf.c: Include msgpack.h if HAVE_MSGPACK.
(print_note_contents_hex): New.
(print_indents): New.
(dump_msgpack_obj): New.
(dump_msgpack): New.
(print_amdgpu_note): New.
(process_note): Handle NT_AMDGPU_METADATA note contents.
Use print_note_contents_hex.
2022-03-16 Simon Marchi <simon.marchi@efficios.com>
* readelf.c (get_amdgpu_elf_note_type): New.
+13 -1
View File
@@ -256,7 +256,7 @@ objcopy_SOURCES = objcopy.c not-strip.c rename.c $(WRITE_DEBUG_SRCS) $(BULIBS)
strings_SOURCES = strings.c $(BULIBS)
readelf_SOURCES = readelf.c version.c unwind-ia64.c dwarf.c demanguse.c $(ELFLIBS)
readelf_LDADD = $(LIBCTF_NOBFD) $(LIBINTL) $(LIBIBERTY) $(ZLIB) $(DEBUGINFOD_LIBS)
readelf_LDADD = $(LIBCTF_NOBFD) $(LIBINTL) $(LIBIBERTY) $(ZLIB) $(DEBUGINFOD_LIBS) $(MSGPACK_LIBS)
elfedit_SOURCES = elfedit.c version.c $(ELFLIBS)
elfedit_LDADD = $(LIBINTL) $(LIBIBERTY)
@@ -381,6 +381,18 @@ endif
$(AM_V_CC)$(COMPILE) $(DEBUGINFOD_CFLAGS) -c -o $@ $(srcdir)/dwarf.c
endif
readelf.@OBJEXT@: readelf.c
if am__fastdepCC
$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo $(MSGPACK_CFLAGS) -c -o $@ $(srcdir)/readelf.c
$(AM_V_at)mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
else
if AMDEP
source='readelf.c' object='$@' libtool=no @AMDEPBACKSLASH@
DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
endif
$(AM_V_CC)$(COMPILE) $(MSGPACK_CFLAGS) -c -o $@ $(srcdir)/readelf.c
endif
sysroff.@OBJEXT@: sysroff.c
if am__fastdepCC
$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `test -f sysroff.c || echo $(srcdir)/`sysroff.c $(NO_WERROR)
+10 -1
View File
@@ -537,6 +537,8 @@ MKDIR_P = @MKDIR_P@
MKINSTALLDIRS = @MKINSTALLDIRS@
MSGFMT = @MSGFMT@
MSGMERGE = @MSGMERGE@
MSGPACK_CFLAGS = @MSGPACK_CFLAGS@
MSGPACK_LIBS = @MSGPACK_LIBS@
NM = @NM@
NMEDIT = @NMEDIT@
NO_WERROR = @NO_WERROR@
@@ -785,7 +787,7 @@ size_SOURCES = size.c $(BULIBS)
objcopy_SOURCES = objcopy.c not-strip.c rename.c $(WRITE_DEBUG_SRCS) $(BULIBS)
strings_SOURCES = strings.c $(BULIBS)
readelf_SOURCES = readelf.c version.c unwind-ia64.c dwarf.c demanguse.c $(ELFLIBS)
readelf_LDADD = $(LIBCTF_NOBFD) $(LIBINTL) $(LIBIBERTY) $(ZLIB) $(DEBUGINFOD_LIBS)
readelf_LDADD = $(LIBCTF_NOBFD) $(LIBINTL) $(LIBIBERTY) $(ZLIB) $(DEBUGINFOD_LIBS) $(MSGPACK_LIBS)
elfedit_SOURCES = elfedit.c version.c $(ELFLIBS)
elfedit_LDADD = $(LIBINTL) $(LIBIBERTY)
strip_new_SOURCES = objcopy.c is-strip.c rename.c $(WRITE_DEBUG_SRCS) $(BULIBS)
@@ -1919,6 +1921,13 @@ dwarf.@OBJEXT@: dwarf.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC)$(COMPILE) $(DEBUGINFOD_CFLAGS) -c -o $@ $(srcdir)/dwarf.c
readelf.@OBJEXT@: readelf.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo $(MSGPACK_CFLAGS) -c -o $@ $(srcdir)/readelf.c
@am__fastdepCC_TRUE@ $(AM_V_at)mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='readelf.c' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC)$(COMPILE) $(MSGPACK_CFLAGS) -c -o $@ $(srcdir)/readelf.c
sysroff.@OBJEXT@: sysroff.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `test -f sysroff.c || echo $(srcdir)/`sysroff.c $(NO_WERROR)
@am__fastdepCC_TRUE@ $(AM_V_at)mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+3
View File
@@ -104,6 +104,9 @@
/* Define to 1 if you have a working `mmap' system call. */
#undef HAVE_MMAP
/* Define to 1 if msgpack is available. */
#undef HAVE_MSGPACK
/* Define to 1 if you have the `sbrk' function. */
#undef HAVE_SBRK
+134 -3
View File
@@ -648,6 +648,8 @@ BUILD_DLLTOOL
BUILD_SRCONV
LTLIBICONV
LIBICONV
MSGPACK_LIBS
MSGPACK_CFLAGS
zlibinc
zlibdir
DEMANGLER_NAME
@@ -830,6 +832,7 @@ enable_build_warnings
enable_nls
enable_maintainer_mode
with_system_zlib
with_msgpack
enable_rpath
with_libiconv_prefix
with_libiconv_type
@@ -849,7 +852,9 @@ PKG_CONFIG_LIBDIR
DEBUGINFOD_CFLAGS
DEBUGINFOD_LIBS
YACC
YFLAGS'
YFLAGS
MSGPACK_CFLAGS
MSGPACK_LIBS'
# Initialize some variables set by options.
@@ -1512,6 +1517,7 @@ Optional Packages:
--with-debuginfod Enable debuginfo lookups with debuginfod
(auto/yes/no)
--with-system-zlib use installed libz
--with-msgpack Enable msgpack support (auto/yes/no)
--with-gnu-ld assume the C compiler uses GNU ld default=no
--with-libiconv-prefix[=DIR] search for libiconv in DIR/include and DIR/lib
--without-libiconv-prefix don't search for libiconv in includedir and libdir
@@ -1541,6 +1547,10 @@ Some influential environment variables:
YFLAGS The list of arguments that will be passed by default to $YACC.
This script will default YFLAGS to the empty string to avoid a
default value of `-d' given by some make applications.
MSGPACK_CFLAGS
C compiler flags for MSGPACK, overriding pkg-config
MSGPACK_LIBS
linker flags for MSGPACK, overriding pkg-config
Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.
@@ -10971,7 +10981,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 10974 "configure"
#line 10984 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -11077,7 +11087,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 11080 "configure"
#line 11090 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -13723,6 +13733,127 @@ $as_echo "#define USE_BINARY_FOPEN 1" >>confdefs.h
;;
esac
# Support for the msgpack C library.
# Check whether --with-msgpack was given.
if test "${with_msgpack+set}" = set; then :
withval=$with_msgpack;
else
with_msgpack=auto
fi
if test "$with_msgpack" != no; then
pkg_failed=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for msgpack" >&5
$as_echo_n "checking for msgpack... " >&6; }
if test -n "$MSGPACK_CFLAGS"; then
pkg_cv_MSGPACK_CFLAGS="$MSGPACK_CFLAGS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"msgpack\""; } >&5
($PKG_CONFIG --exists --print-errors "msgpack") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_MSGPACK_CFLAGS=`$PKG_CONFIG --cflags "msgpack" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test -n "$MSGPACK_LIBS"; then
pkg_cv_MSGPACK_LIBS="$MSGPACK_LIBS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"msgpack\""; } >&5
($PKG_CONFIG --exists --print-errors "msgpack") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_MSGPACK_LIBS=`$PKG_CONFIG --libs "msgpack" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test $pkg_failed = no; then
pkg_save_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS $pkg_cv_MSGPACK_LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
else
pkg_failed=yes
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LDFLAGS=$pkg_save_LDFLAGS
fi
if test $pkg_failed = yes; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
MSGPACK_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "msgpack" 2>&1`
else
MSGPACK_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "msgpack" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$MSGPACK_PKG_ERRORS" >&5
if test "$with_msgpack" = yes; then
as_fn_error $? "--with-msgpack was given, but msgpack is missing or unusable." "$LINENO" 5
fi
elif test $pkg_failed = untried; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if test "$with_msgpack" = yes; then
as_fn_error $? "--with-msgpack was given, but msgpack is missing or unusable." "$LINENO" 5
fi
else
MSGPACK_CFLAGS=$pkg_cv_MSGPACK_CFLAGS
MSGPACK_LIBS=$pkg_cv_MSGPACK_LIBS
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define HAVE_MSGPACK 1" >>confdefs.h
fi
fi
# target-specific stuff:
# Canonicalize the secondary target names.
+17
View File
@@ -275,6 +275,23 @@ AM_ZLIB
BFD_BINARY_FOPEN
# Support for the msgpack C library.
AC_ARG_WITH([msgpack],
AC_HELP_STRING([--with-msgpack], [Enable msgpack support (auto/yes/no)]),
[],
[with_msgpack=auto])
if test "$with_msgpack" != no; then
PKG_CHECK_MODULES(MSGPACK, msgpack, [
AC_DEFINE([HAVE_MSGPACK], [1], [Define to 1 if msgpack is available.])
], [
if test "$with_msgpack" = yes; then
AC_MSG_ERROR([--with-msgpack was given, but msgpack is missing or unusable.])
fi
])
fi
# target-specific stuff:
# Canonicalize the secondary target names.
+179 -14
View File
@@ -46,6 +46,10 @@
#include <zlib.h>
#include <wchar.h>
#if defined HAVE_MSGPACK
#include <msgpack.h>
#endif
#if __GNUC__ >= 2
/* Define BFD64 here, even if our default architecture is 32 bit ELF
as this will allow us to read in and parse 64bit and 32bit ELF files.
@@ -21307,6 +21311,177 @@ print_gnu_build_attribute_name (Elf_Internal_Note * pnote)
return true;
}
/* Print the contents of PNOTE as hex. */
static void
print_note_contents_hex (Elf_Internal_Note *pnote)
{
if (pnote->descsz)
{
unsigned long i;
printf (_(" description data: "));
for (i = 0; i < pnote->descsz; i++)
printf ("%02x ", pnote->descdata[i] & 0xff);
if (!do_wide)
printf ("\n");
}
if (do_wide)
printf ("\n");
}
#if defined HAVE_MSGPACK
static void
print_indents (int n)
{
printf (" ");
for (int i = 0; i < n; i++)
printf (" ");
}
/* Print OBJ in human-readable form. */
static void
dump_msgpack_obj (const msgpack_object *obj, int indent)
{
switch (obj->type)
{
case MSGPACK_OBJECT_NIL:
printf ("(nil)");
break;
case MSGPACK_OBJECT_BOOLEAN:
printf ("%s", obj->via.boolean ? "true" : "false");
break;
case MSGPACK_OBJECT_POSITIVE_INTEGER:
printf ("%" PRIu64, obj->via.u64);
break;
case MSGPACK_OBJECT_NEGATIVE_INTEGER:
printf ("%" PRIi64, obj->via.i64);
break;
case MSGPACK_OBJECT_FLOAT32:
case MSGPACK_OBJECT_FLOAT64:
printf ("%f", obj->via.f64);
break;
case MSGPACK_OBJECT_STR:
printf ("\"%.*s\"", obj->via.str.size, obj->via.str.ptr);
break;
case MSGPACK_OBJECT_ARRAY:
{
const msgpack_object_array *array = &obj->via.array;
printf ("[\n");
++indent;
for (uint32_t i = 0; i < array->size; ++i)
{
const msgpack_object *item = &array->ptr[i];
print_indents (indent);
dump_msgpack_obj (item, indent);
printf (",\n");
}
--indent;
print_indents (indent);
printf ("]");
break;
}
break;
case MSGPACK_OBJECT_MAP:
{
const msgpack_object_map *map = &obj->via.map;
printf ("{\n");
++indent;
for (uint32_t i = 0; i < map->size; ++i)
{
const msgpack_object_kv *kv = &map->ptr[i];
const msgpack_object *key = &kv->key;
const msgpack_object *val = &kv->val;
print_indents (indent);
dump_msgpack_obj (key, indent);
printf (": ");
dump_msgpack_obj (val, indent);
printf (",\n");
}
--indent;
print_indents (indent);
printf ("}");
break;
}
case MSGPACK_OBJECT_BIN:
printf ("(bin)");
break;
case MSGPACK_OBJECT_EXT:
printf ("(ext)");
break;
}
}
static void
dump_msgpack (const msgpack_unpacked *msg)
{
print_indents (0);
dump_msgpack_obj (&msg->data, 0);
printf ("\n");
}
#endif /* defined HAVE_MSGPACK */
static bool
print_amdgpu_note (Elf_Internal_Note *pnote)
{
#if defined HAVE_MSGPACK
/* If msgpack is available, decode and dump the note's content. */
bool ret;
msgpack_unpacked msg;
msgpack_unpack_return msgpack_ret;
assert (pnote->type == NT_AMDGPU_METADATA);
msgpack_unpacked_init (&msg);
msgpack_ret = msgpack_unpack_next (&msg, pnote->descdata, pnote->descsz,
NULL);
switch (msgpack_ret)
{
case MSGPACK_UNPACK_SUCCESS:
dump_msgpack (&msg);
ret = true;
break;
default:
error (_("failed to unpack msgpack contents in NT_AMDGPU_METADATA note"));
ret = false;
break;
}
msgpack_unpacked_destroy (&msg);
return ret;
#else
/* msgpack is not available, dump contents as hex. */
print_note_contents_hex (pnote);
return true;
#endif
}
/* Note that by the ELF standard, the name field is already null byte
terminated, and namesz includes the terminating null byte.
I.E. the value of namesz for the name "FSF" is 4.
@@ -21404,21 +21579,11 @@ process_note (Elf_Internal_Note * pnote,
&& (pnote->type == NT_GNU_BUILD_ATTRIBUTE_OPEN
|| pnote->type == NT_GNU_BUILD_ATTRIBUTE_FUNC))
return print_gnu_build_attribute_description (pnote, filedata);
else if (startswith (pnote->namedata, "AMDGPU")
&& pnote->type == NT_AMDGPU_METADATA)
return print_amdgpu_note (pnote);
if (pnote->descsz)
{
unsigned long i;
printf (_(" description data: "));
for (i = 0; i < pnote->descsz; i++)
printf ("%02x ", pnote->descdata[i] & 0xff);
if (!do_wide)
printf ("\n");
}
if (do_wide)
printf ("\n");
print_note_contents_hex (pnote);
return true;
}