Tools that display the internal structure of ELF and DWARF files (like elfdump(1), readelf(1), and nm(1)) frequently need to translate ELF constants to human-readable strings.

Previously, Elftoolchain's tools contained ad-hoc code to perform these translations.  Take elfdump, as an example.  To convert an EM_* value to its corresponding symbolic name, it uses code like:

File elfdump/elfdump.c:228
static const char *
e_machines(unsigned int mach)
{
	static char machdesc[64];

	switch (mach) {
	case EM_NONE:	return "EM_NONE";
	case EM_M32:	return "EM_M32";
	/* ... similar lines of code ... */
	case EM_RISCV:	return "EM_RISCV";
	}
	snprintf(machdesc, sizeof(machdesc),
	    "(unknown machine) -- type 0x%x", mach);
	return (machdesc);
}

elfdump has similar code for converting the other ELF constant values to their corresponding names.  The other tools — nm, readelf, etc. — also have code doing more or less the same thing.

As of revision [r4065] I have started adding new ELFTC(3) APIs that could help ease code maintenance in the future.  These APIs follow the general template below:

/* Returns the symbolic name for a constant. */
const char *elftc_get_machine_name(unsigned int e_machine);
/* Returns any associated additional comment text. */
const char *elftc_get_machine_description(unsigned int e_machine);

Some notes about these APIs:

  1. These functions are intended to be thread-safe by default — when they succeed, the functions return pointers to read-only data, and when they fail they return NULL and set errno (a thread-private value).

  2. Error handling is being left to the caller.  This is because the caller usually has the best context on how to handle the error.  In contrast, APIs that return a preformatted error string in the event of an error (such as, "Unknown value NNN") make it tedious for the caller to tell if the call had actually succeeded.

  3. The implementations of these APIs are generated at build time from the information in the file elfconstants.m4.

There are manual pages for these functions, e.g. elftc_get_machine_name(3).

Revision [r4075] shows how these functions could be used.

Example API usage
const char *em_name;
char em_name_buffer[64];

if ((em_name = elftc_get_machine_name(ed->ehdr.e_machine)) == NULL) {
    (void) snprintf(em_name_buffer, sizeof(em_name_buffer),
		"(unknown machine) -- type 0x%x", ed->ehdr.e_machine);
    em_name = em_name_buffer;
}

printf("  e_machine:  %-18s\n", em_name);