659 lines
16 KiB
C
659 lines
16 KiB
C
/* Print contents of object file note.
|
|
Copyright (C) 2002, 2007, 2009, 2011, 2015, 2016, 2018 Red Hat, Inc.
|
|
This file is part of elfutils.
|
|
Written by Ulrich Drepper <drepper@redhat.com>, 2002.
|
|
|
|
This file is free software; you can redistribute it and/or modify
|
|
it under the terms of either
|
|
|
|
* the GNU Lesser General Public License as published by the Free
|
|
Software Foundation; either version 3 of the License, or (at
|
|
your option) any later version
|
|
|
|
or
|
|
|
|
* the GNU General Public License as published by the Free
|
|
Software Foundation; either version 2 of the License, or (at
|
|
your option) any later version
|
|
|
|
or both in parallel, as here.
|
|
|
|
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 copies of the GNU General Public License and
|
|
the GNU Lesser General Public License along with this program. If
|
|
not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <libeblP.h>
|
|
|
|
#include "common.h"
|
|
#include "libelfP.h"
|
|
#include "libdwP.h"
|
|
#include "memory-access.h"
|
|
|
|
|
|
void
|
|
ebl_object_note (Ebl *ebl, uint32_t namesz, const char *name, uint32_t type,
|
|
uint32_t descsz, const char *desc)
|
|
{
|
|
if (! ebl->object_note (name, type, descsz, desc))
|
|
{
|
|
/* The machine specific function did not know this type. */
|
|
|
|
if (strcmp ("stapsdt", name) == 0)
|
|
{
|
|
if (type != 3)
|
|
{
|
|
printf (_("unknown SDT version %u\n"), type);
|
|
return;
|
|
}
|
|
|
|
/* Descriptor starts with three addresses, pc, base ref and
|
|
semaphore. Then three zero terminated strings provider,
|
|
name and arguments. */
|
|
|
|
union
|
|
{
|
|
Elf64_Addr a64[3];
|
|
Elf32_Addr a32[3];
|
|
} addrs;
|
|
|
|
size_t addrs_size = gelf_fsize (ebl->elf, ELF_T_ADDR, 3, EV_CURRENT);
|
|
if (descsz < addrs_size + 3)
|
|
{
|
|
invalid_sdt:
|
|
printf (_("invalid SDT probe descriptor\n"));
|
|
return;
|
|
}
|
|
|
|
Elf_Data src =
|
|
{
|
|
.d_type = ELF_T_ADDR, .d_version = EV_CURRENT,
|
|
.d_buf = (void *) desc, .d_size = addrs_size
|
|
};
|
|
|
|
Elf_Data dst =
|
|
{
|
|
.d_type = ELF_T_ADDR, .d_version = EV_CURRENT,
|
|
.d_buf = &addrs, .d_size = addrs_size
|
|
};
|
|
|
|
if (gelf_xlatetom (ebl->elf, &dst, &src,
|
|
elf_getident (ebl->elf, NULL)[EI_DATA]) == NULL)
|
|
{
|
|
printf ("%s\n", elf_errmsg (-1));
|
|
return;
|
|
}
|
|
|
|
const char *provider = desc + addrs_size;
|
|
const char *pname = memchr (provider, '\0', desc + descsz - provider);
|
|
if (pname == NULL)
|
|
goto invalid_sdt;
|
|
|
|
++pname;
|
|
const char *args = memchr (pname, '\0', desc + descsz - pname);
|
|
if (args == NULL ||
|
|
memchr (++args, '\0', desc + descsz - pname) != desc + descsz - 1)
|
|
goto invalid_sdt;
|
|
|
|
GElf_Addr pc;
|
|
GElf_Addr base;
|
|
GElf_Addr sem;
|
|
if (gelf_getclass (ebl->elf) == ELFCLASS32)
|
|
{
|
|
pc = addrs.a32[0];
|
|
base = addrs.a32[1];
|
|
sem = addrs.a32[2];
|
|
}
|
|
else
|
|
{
|
|
pc = addrs.a64[0];
|
|
base = addrs.a64[1];
|
|
sem = addrs.a64[2];
|
|
}
|
|
|
|
printf (_(" PC: "));
|
|
printf ("%#" PRIx64 ",", pc);
|
|
printf (_(" Base: "));
|
|
printf ("%#" PRIx64 ",", base);
|
|
printf (_(" Semaphore: "));
|
|
printf ("%#" PRIx64 "\n", sem);
|
|
printf (_(" Provider: "));
|
|
printf ("%s,", provider);
|
|
printf (_(" Name: "));
|
|
printf ("%s,", pname);
|
|
printf (_(" Args: "));
|
|
printf ("'%s'\n", args);
|
|
return;
|
|
}
|
|
|
|
if (strncmp (name, ELF_NOTE_GNU_BUILD_ATTRIBUTE_PREFIX,
|
|
strlen (ELF_NOTE_GNU_BUILD_ATTRIBUTE_PREFIX)) == 0
|
|
&& (type == NT_GNU_BUILD_ATTRIBUTE_OPEN
|
|
|| type == NT_GNU_BUILD_ATTRIBUTE_FUNC))
|
|
{
|
|
/* There might or might not be a pair of addresses in the desc. */
|
|
if (descsz > 0)
|
|
{
|
|
printf (" Address Range: ");
|
|
|
|
union
|
|
{
|
|
Elf64_Addr a64[2];
|
|
Elf32_Addr a32[2];
|
|
} addrs;
|
|
|
|
size_t addr_size = gelf_fsize (ebl->elf, ELF_T_ADDR,
|
|
2, EV_CURRENT);
|
|
if (descsz != addr_size)
|
|
printf ("<unknown data>\n");
|
|
else
|
|
{
|
|
Elf_Data src =
|
|
{
|
|
.d_type = ELF_T_ADDR, .d_version = EV_CURRENT,
|
|
.d_buf = (void *) desc, .d_size = descsz
|
|
};
|
|
|
|
Elf_Data dst =
|
|
{
|
|
.d_type = ELF_T_ADDR, .d_version = EV_CURRENT,
|
|
.d_buf = &addrs, .d_size = descsz
|
|
};
|
|
|
|
if (gelf_xlatetom (ebl->elf, &dst, &src,
|
|
elf_getident (ebl->elf,
|
|
NULL)[EI_DATA]) == NULL)
|
|
printf ("%s\n", elf_errmsg (-1));
|
|
else
|
|
{
|
|
if (addr_size == 4)
|
|
printf ("%#" PRIx32 " - %#" PRIx32 "\n",
|
|
addrs.a32[0], addrs.a32[1]);
|
|
else
|
|
printf ("%#" PRIx64 " - %#" PRIx64 "\n",
|
|
addrs.a64[0], addrs.a64[1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Most data actually is inside the name.
|
|
https://fedoraproject.org/wiki/Toolchain/Watermark */
|
|
|
|
/* We need at least 2 chars of data to describe the
|
|
attribute and value encodings. */
|
|
const char *data = (name
|
|
+ strlen (ELF_NOTE_GNU_BUILD_ATTRIBUTE_PREFIX));
|
|
if (namesz < 2)
|
|
{
|
|
printf ("<insufficient data>\n");
|
|
return;
|
|
}
|
|
|
|
printf (" ");
|
|
|
|
/* In most cases the value comes right after the encoding bytes. */
|
|
const char *value = &data[2];
|
|
switch (data[1])
|
|
{
|
|
case GNU_BUILD_ATTRIBUTE_VERSION:
|
|
printf ("VERSION: ");
|
|
break;
|
|
case GNU_BUILD_ATTRIBUTE_STACK_PROT:
|
|
printf ("STACK_PROT: ");
|
|
break;
|
|
case GNU_BUILD_ATTRIBUTE_RELRO:
|
|
printf ("RELRO: ");
|
|
break;
|
|
case GNU_BUILD_ATTRIBUTE_STACK_SIZE:
|
|
printf ("STACK_SIZE: ");
|
|
break;
|
|
case GNU_BUILD_ATTRIBUTE_TOOL:
|
|
printf ("TOOL: ");
|
|
break;
|
|
case GNU_BUILD_ATTRIBUTE_ABI:
|
|
printf ("ABI: ");
|
|
break;
|
|
case GNU_BUILD_ATTRIBUTE_PIC:
|
|
printf ("PIC: ");
|
|
break;
|
|
case GNU_BUILD_ATTRIBUTE_SHORT_ENUM:
|
|
printf ("SHORT_ENUM: ");
|
|
break;
|
|
case 32 ... 126:
|
|
printf ("\"%s\": ", &data[1]);
|
|
value += strlen (&data[1]) + 1;
|
|
break;
|
|
default:
|
|
printf ("<unknown>: ");
|
|
break;
|
|
}
|
|
|
|
switch (data[0])
|
|
{
|
|
case GNU_BUILD_ATTRIBUTE_TYPE_NUMERIC:
|
|
{
|
|
/* Any numbers are always in (unsigned) little endian. */
|
|
static const Dwarf dbg
|
|
= { .other_byte_order = MY_ELFDATA != ELFDATA2LSB };
|
|
size_t bytes = namesz - (value - name);
|
|
uint64_t val;
|
|
if (bytes == 1)
|
|
val = *(unsigned char *) value;
|
|
else if (bytes == 2)
|
|
val = read_2ubyte_unaligned (&dbg, value);
|
|
else if (bytes == 4)
|
|
val = read_4ubyte_unaligned (&dbg, value);
|
|
else if (bytes == 8)
|
|
val = read_8ubyte_unaligned (&dbg, value);
|
|
else
|
|
goto unknown;
|
|
printf ("%" PRIx64, val);
|
|
}
|
|
break;
|
|
case GNU_BUILD_ATTRIBUTE_TYPE_STRING:
|
|
printf ("\"%s\"", value);
|
|
break;
|
|
case GNU_BUILD_ATTRIBUTE_TYPE_BOOL_TRUE:
|
|
printf ("TRUE");
|
|
break;
|
|
case GNU_BUILD_ATTRIBUTE_TYPE_BOOL_FALSE:
|
|
printf ("FALSE");
|
|
break;
|
|
default:
|
|
{
|
|
unknown:
|
|
printf ("<unknown>");
|
|
}
|
|
break;
|
|
}
|
|
|
|
printf ("\n");
|
|
|
|
return;
|
|
}
|
|
|
|
/* NT_VERSION doesn't have any info. All data is in the name. */
|
|
if (descsz == 0 && type == NT_VERSION)
|
|
return;
|
|
|
|
/* Everything else should have the "GNU" owner name. */
|
|
if (strcmp ("GNU", name) != 0)
|
|
return;
|
|
|
|
switch (type)
|
|
{
|
|
case NT_GNU_BUILD_ID:
|
|
if (strcmp (name, "GNU") == 0 && descsz > 0)
|
|
{
|
|
printf (_(" Build ID: "));
|
|
uint_fast32_t i;
|
|
for (i = 0; i < descsz - 1; ++i)
|
|
printf ("%02" PRIx8, (uint8_t) desc[i]);
|
|
printf ("%02" PRIx8 "\n", (uint8_t) desc[i]);
|
|
}
|
|
break;
|
|
|
|
case NT_GNU_GOLD_VERSION:
|
|
if (strcmp (name, "GNU") == 0 && descsz > 0)
|
|
/* A non-null terminated version string. */
|
|
printf (_(" Linker version: %.*s\n"),
|
|
(int) descsz, desc);
|
|
break;
|
|
|
|
case NT_GNU_PROPERTY_TYPE_0:
|
|
if (strcmp (name, "GNU") == 0 && descsz > 0)
|
|
{
|
|
/* There are at least 2 words. type and datasz. */
|
|
while (descsz >= 8)
|
|
{
|
|
struct pr_prop
|
|
{
|
|
GElf_Word pr_type;
|
|
GElf_Word pr_datasz;
|
|
} prop;
|
|
|
|
Elf_Data in =
|
|
{
|
|
.d_version = EV_CURRENT,
|
|
.d_type = ELF_T_WORD,
|
|
.d_size = 8,
|
|
.d_buf = (void *) desc
|
|
};
|
|
Elf_Data out =
|
|
{
|
|
.d_version = EV_CURRENT,
|
|
.d_type = ELF_T_WORD,
|
|
.d_size = descsz,
|
|
.d_buf = (void *) &prop
|
|
};
|
|
|
|
if (gelf_xlatetom (ebl->elf, &out, &in,
|
|
elf_getident (ebl->elf,
|
|
NULL)[EI_DATA]) == NULL)
|
|
{
|
|
printf ("%s\n", elf_errmsg (-1));
|
|
return;
|
|
}
|
|
|
|
desc += 8;
|
|
descsz -= 8;
|
|
|
|
if (prop.pr_datasz > descsz)
|
|
{
|
|
printf ("BAD property datasz: %" PRId32 "\n",
|
|
prop.pr_datasz);
|
|
return;
|
|
}
|
|
|
|
int elfclass = gelf_getclass (ebl->elf);
|
|
char *elfident = elf_getident (ebl->elf, NULL);
|
|
GElf_Ehdr ehdr;
|
|
gelf_getehdr (ebl->elf, &ehdr);
|
|
|
|
/* Prefix. */
|
|
printf (" ");
|
|
if (prop.pr_type == GNU_PROPERTY_STACK_SIZE)
|
|
{
|
|
printf ("STACK_SIZE ");
|
|
union
|
|
{
|
|
Elf64_Addr a64;
|
|
Elf32_Addr a32;
|
|
} addr;
|
|
if ((elfclass == ELFCLASS32 && prop.pr_datasz == 4)
|
|
|| (elfclass == ELFCLASS64 && prop.pr_datasz == 8))
|
|
{
|
|
in.d_type = ELF_T_ADDR;
|
|
out.d_type = ELF_T_ADDR;
|
|
in.d_size = prop.pr_datasz;
|
|
out.d_size = prop.pr_datasz;
|
|
in.d_buf = (void *) desc;
|
|
out.d_buf = (elfclass == ELFCLASS32
|
|
? (void *) &addr.a32
|
|
: (void *) &addr.a64);
|
|
|
|
if (gelf_xlatetom (ebl->elf, &out, &in,
|
|
elfident[EI_DATA]) == NULL)
|
|
{
|
|
printf ("%s\n", elf_errmsg (-1));
|
|
return;
|
|
}
|
|
if (elfclass == ELFCLASS32)
|
|
printf ("%#" PRIx32 "\n", addr.a32);
|
|
else
|
|
printf ("%#" PRIx64 "\n", addr.a64);
|
|
}
|
|
else
|
|
printf (" (garbage datasz: %" PRIx32 ")\n",
|
|
prop.pr_datasz);
|
|
}
|
|
else if (prop.pr_type == GNU_PROPERTY_NO_COPY_ON_PROTECTED)
|
|
{
|
|
printf ("NO_COPY_ON_PROTECTION");
|
|
if (prop.pr_datasz == 0)
|
|
printf ("\n");
|
|
else
|
|
printf (" (garbage datasz: %" PRIx32 ")\n",
|
|
prop.pr_datasz);
|
|
}
|
|
else if (prop.pr_type >= GNU_PROPERTY_LOPROC
|
|
&& prop.pr_type <= GNU_PROPERTY_HIPROC
|
|
&& (ehdr.e_machine == EM_386
|
|
|| ehdr.e_machine == EM_X86_64))
|
|
{
|
|
printf ("X86 ");
|
|
if (prop.pr_type == GNU_PROPERTY_X86_FEATURE_1_AND)
|
|
{
|
|
printf ("FEATURE_1_AND: ");
|
|
|
|
if (prop.pr_datasz == 4)
|
|
{
|
|
GElf_Word data;
|
|
in.d_type = ELF_T_WORD;
|
|
out.d_type = ELF_T_WORD;
|
|
in.d_size = 4;
|
|
out.d_size = 4;
|
|
in.d_buf = (void *) desc;
|
|
out.d_buf = (void *) &data;
|
|
|
|
if (gelf_xlatetom (ebl->elf, &out, &in,
|
|
elfident[EI_DATA]) == NULL)
|
|
{
|
|
printf ("%s\n", elf_errmsg (-1));
|
|
return;
|
|
}
|
|
printf ("%08" PRIx32 " ", data);
|
|
|
|
if ((data & GNU_PROPERTY_X86_FEATURE_1_IBT)
|
|
!= 0)
|
|
{
|
|
printf ("IBT");
|
|
data &= ~GNU_PROPERTY_X86_FEATURE_1_IBT;
|
|
if (data != 0)
|
|
printf (" ");
|
|
}
|
|
|
|
if ((data & GNU_PROPERTY_X86_FEATURE_1_SHSTK)
|
|
!= 0)
|
|
{
|
|
printf ("SHSTK");
|
|
data &= ~GNU_PROPERTY_X86_FEATURE_1_SHSTK;
|
|
if (data != 0)
|
|
printf (" ");
|
|
}
|
|
|
|
if (data != 0)
|
|
printf ("UNKNOWN");
|
|
}
|
|
else
|
|
printf ("<bad datasz: %" PRId32 ">",
|
|
prop.pr_datasz);
|
|
|
|
printf ("\n");
|
|
}
|
|
else
|
|
{
|
|
printf ("%#" PRIx32, prop.pr_type);
|
|
if (prop.pr_datasz > 0)
|
|
{
|
|
printf (" data: ");
|
|
size_t i;
|
|
for (i = 0; i < prop.pr_datasz - 1; i++)
|
|
printf ("%02" PRIx8 " ", (uint8_t) desc[i]);
|
|
printf ("%02" PRIx8 "\n", (uint8_t) desc[i]);
|
|
}
|
|
}
|
|
}
|
|
else if (prop.pr_type >= GNU_PROPERTY_LOPROC
|
|
&& prop.pr_type <= GNU_PROPERTY_HIPROC
|
|
&& ehdr.e_machine == EM_AARCH64)
|
|
{
|
|
printf ("AARCH64 ");
|
|
if (prop.pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND)
|
|
{
|
|
printf ("FEATURE_1_AND: ");
|
|
|
|
if (prop.pr_datasz == 4)
|
|
{
|
|
GElf_Word data;
|
|
in.d_type = ELF_T_WORD;
|
|
out.d_type = ELF_T_WORD;
|
|
in.d_size = 4;
|
|
out.d_size = 4;
|
|
in.d_buf = (void *) desc;
|
|
out.d_buf = (void *) &data;
|
|
|
|
if (gelf_xlatetom (ebl->elf, &out, &in,
|
|
elfident[EI_DATA]) == NULL)
|
|
{
|
|
printf ("%s\n", elf_errmsg (-1));
|
|
return;
|
|
}
|
|
printf ("%08" PRIx32 " ", data);
|
|
|
|
if ((data & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
|
|
!= 0)
|
|
{
|
|
printf ("BTI");
|
|
data &= ~GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
|
|
if (data != 0)
|
|
printf (" ");
|
|
}
|
|
|
|
if ((data & GNU_PROPERTY_AARCH64_FEATURE_1_PAC)
|
|
!= 0)
|
|
{
|
|
printf ("PAC");
|
|
data &= ~GNU_PROPERTY_AARCH64_FEATURE_1_PAC;
|
|
if (data != 0)
|
|
printf (" ");
|
|
}
|
|
|
|
if (data != 0)
|
|
printf ("UNKNOWN");
|
|
}
|
|
else
|
|
printf ("<bad datasz: %" PRId32 ">",
|
|
prop.pr_datasz);
|
|
|
|
printf ("\n");
|
|
}
|
|
else
|
|
{
|
|
printf ("%#" PRIx32, prop.pr_type);
|
|
if (prop.pr_datasz > 0)
|
|
{
|
|
printf (" data: ");
|
|
size_t i;
|
|
for (i = 0; i < prop.pr_datasz - 1; i++)
|
|
printf ("%02" PRIx8 " ", (uint8_t) desc[i]);
|
|
printf ("%02" PRIx8 "\n", (uint8_t) desc[i]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (prop.pr_type >= GNU_PROPERTY_LOPROC
|
|
&& prop.pr_type <= GNU_PROPERTY_HIPROC)
|
|
printf ("proc_type %#" PRIx32, prop.pr_type);
|
|
else if (prop.pr_type >= GNU_PROPERTY_LOUSER
|
|
&& prop.pr_type <= GNU_PROPERTY_HIUSER)
|
|
printf ("app_type %#" PRIx32, prop.pr_type);
|
|
else
|
|
printf ("unknown_type %#" PRIx32, prop.pr_type);
|
|
|
|
if (prop.pr_datasz > 0)
|
|
{
|
|
printf (" data: ");
|
|
size_t i;
|
|
for (i = 0; i < prop.pr_datasz - 1; i++)
|
|
printf ("%02" PRIx8 " ", (uint8_t) desc[i]);
|
|
printf ("%02" PRIx8 "\n", (uint8_t) desc[i]);
|
|
}
|
|
}
|
|
|
|
if (elfclass == ELFCLASS32)
|
|
prop.pr_datasz = NOTE_ALIGN4 (prop.pr_datasz);
|
|
else
|
|
prop.pr_datasz = NOTE_ALIGN8 (prop.pr_datasz);
|
|
|
|
desc += prop.pr_datasz;
|
|
if (descsz > prop.pr_datasz)
|
|
descsz -= prop.pr_datasz;
|
|
else
|
|
descsz = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NT_GNU_ABI_TAG:
|
|
if (descsz >= 8 && descsz % 4 == 0)
|
|
{
|
|
Elf_Data in =
|
|
{
|
|
.d_version = EV_CURRENT,
|
|
.d_type = ELF_T_WORD,
|
|
.d_size = descsz,
|
|
.d_buf = (void *) desc
|
|
};
|
|
/* Normally NT_GNU_ABI_TAG is just 4 words (16 bytes). If it
|
|
is much (4*) larger dynamically allocate memory to convert. */
|
|
#define FIXED_TAG_BYTES 16
|
|
uint32_t sbuf[FIXED_TAG_BYTES];
|
|
uint32_t *buf;
|
|
if (unlikely (descsz / 4 > FIXED_TAG_BYTES))
|
|
{
|
|
buf = malloc (descsz);
|
|
if (unlikely (buf == NULL))
|
|
return;
|
|
}
|
|
else
|
|
buf = sbuf;
|
|
Elf_Data out =
|
|
{
|
|
.d_version = EV_CURRENT,
|
|
.d_type = ELF_T_WORD,
|
|
.d_size = descsz,
|
|
.d_buf = buf
|
|
};
|
|
|
|
if (elf32_xlatetom (&out, &in, ebl->data) != NULL)
|
|
{
|
|
const char *os;
|
|
switch (buf[0])
|
|
{
|
|
case ELF_NOTE_OS_LINUX:
|
|
os = "Linux";
|
|
break;
|
|
|
|
case ELF_NOTE_OS_GNU:
|
|
os = "GNU";
|
|
break;
|
|
|
|
case ELF_NOTE_OS_SOLARIS2:
|
|
os = "Solaris";
|
|
break;
|
|
|
|
case ELF_NOTE_OS_FREEBSD:
|
|
os = "FreeBSD";
|
|
break;
|
|
|
|
default:
|
|
os = "???";
|
|
break;
|
|
}
|
|
|
|
printf (_(" OS: %s, ABI: "), os);
|
|
for (size_t cnt = 1; cnt < descsz / 4; ++cnt)
|
|
{
|
|
if (cnt > 1)
|
|
putchar_unlocked ('.');
|
|
printf ("%" PRIu32, buf[cnt]);
|
|
}
|
|
putchar_unlocked ('\n');
|
|
}
|
|
if (descsz / 4 > FIXED_TAG_BYTES)
|
|
free (buf);
|
|
break;
|
|
}
|
|
FALLTHROUGH;
|
|
|
|
default:
|
|
/* Unknown type. */
|
|
break;
|
|
}
|
|
}
|
|
}
|