lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20160704142556.cbc7c016de0ee7b52e8c034e@kernel.org>
Date:	Mon, 4 Jul 2016 14:25:56 +0900
From:	Masami Hiramatsu <mhiramat@...nel.org>
To:	Arnaldo Carvalho de Melo <acme@...nel.org>
Cc:	linux-kernel@...r.kernel.org, Namhyung Kim <namhyung@...nel.org>,
	Peter Zijlstra <peterz@...radead.org>,
	Ingo Molnar <mingo@...hat.com>,
	Hemant Kumar <hemant@...ux.vnet.ibm.com>,
	Ananth N Mavinakayanahalli <ananth@...ux.vnet.ibm.com>,
	Brendan Gregg <brendan.d.gregg@...il.com>
Subject: Re: [PATCH perf/core v13 04/15] perf/sdt: ELF support for SDT

On Fri, 1 Jul 2016 15:56:49 -0300
Arnaldo Carvalho de Melo <acme@...nel.org> wrote:

> Em Fri, Jul 01, 2016 at 05:03:46PM +0900, Masami Hiramatsu escreveu:
> > From: Hemant Kumar <hemant@...ux.vnet.ibm.com>
> > 
> > This patch serves the initial support to identify and list SDT events in binaries.
> > When programs containing SDT markers are compiled, gcc with the help of assembler
> > directives identifies them and places them in the section ".note.stapsdt". To find
> > these markers from the binaries, one needs to traverse through this section and
> > parse the relevant details like the name, type and location of the marker. Also,
> > the original location could be skewed due to the effect of prelinking. If that is
> > the case, the locations need to be adjusted.
> > 
> > The functions in this patch open a given ELF, find out the SDT section, parse the
> > relevant details, adjust the location (if necessary) and populate them in a list.
> 
> Breaks the build on older systems:
> 
> 
> [root@...et perf]# dm
> centos:5: FAIL
>   CC       /tmp/build/perf/util/symbol-elf.o
> cc1: warnings being treated as errors
> util/symbol-elf.c: In function 'construct_sdt_notes_list':
> util/symbol-elf.c:1927: warning: implicit declaration of function 'elf_getshdrstrndx'
> util/symbol-elf.c:1927: warning: nested extern declaration of 'elf_getshdrstrndx'
> mv: cannot stat `/tmp/build/perf/util/.symbol-elf.o.tmp': No such file or directory
> make[3]: *** [/tmp/build/perf/util/symbol-elf.o] Error 1
> make[3]: *** Waiting for unfinished jobs....
> make[2]: *** [util] Error 2
> make[1]: *** [/tmp/build/perf/libperf-in.o] Error 2
> make: *** [install-bin] Error 2
> make: Leaving directory `/git/linux/tools/perf'
> centos:6: Ok
> centos:7: Ok
> debian:experimental: Ok
> debian:7: Ok
> debian:8: Ok
> fedora:21: Ok
> fedora:22: Ok
> fedora:23: Ok
> fedora:24: Ok
> opensuse:13.2: Ok
> opensuse:42.1: Ok
> ubuntu:14.04.4: Ok
> ubuntu:15.10: Ok
> ubuntu:16.04: Ok
> ubuntu:12.04.5: Ok
> 
> ---------

OK, that API is newer one. We have to check wheter it is supported by libelf.

> 
> Needs a feature detection test, I will try to contribute that.

Thanks!

> 
> - Arnaldo
> 
>  
> > A typical note entry in ".note.stapsdt" section is as follows :
> > 
> > 
> >                                  |--nhdr.n_namesz--|
> >                 ------------------------------------
> >                 |      nhdr      |     "stapsdt"   |
> >         -----   |----------------------------------|
> >          |      |  <location>       <base_address> |
> >          |      |  <semaphore>                     |
> > nhdr.n_descsize |  "provider_name"   "note_name"   |
> >          |      |   <args>                         |
> >         -----   |----------------------------------|
> >                 |      nhdr      |     "stapsdt"   |
> >                 |...
> > 
> > The above shows an excerpt from the section ".note.stapsdt".
> > 'nhdr' is a structure which has the note name size (n_namesz), note
> > description size (n_desc_sz) and note type (n_type). So, in order to
> > parse the note note info, we need nhdr to tell us where to start from.
> > As can be seen from <sys/sdt.h>, the name of the SDT notes given is "stapsdt".
> > But this is not the identifier of the note.
> > After that, we go to description of the note to find out its location, the
> > address of the ".stapsdt.base" section and the semaphore address.
> > Then, we find the provider name and the SDT marker name and then follow the
> > arguments.
> > 
> > Signed-off-by: Hemant Kumar <hemant@...ux.vnet.ibm.com>
> > Reviewed-by: Masami Hiramatsu <mhiramat@...nel.org>
> > Acked-by: Namhyung Kim <namhyung@...nel.org>
> > ---
> >  tools/perf/util/symbol-elf.c |  252 ++++++++++++++++++++++++++++++++++++++++++
> >  tools/perf/util/symbol.h     |   22 ++++
> >  2 files changed, 274 insertions(+)
> > 
> > diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
> > index 87a297d..e74ce17 100644
> > --- a/tools/perf/util/symbol-elf.c
> > +++ b/tools/perf/util/symbol-elf.c
> > @@ -1781,6 +1781,258 @@ void kcore_extract__delete(struct kcore_extract *kce)
> >  	unlink(kce->extract_filename);
> >  }
> >  
> > +/**
> > + * populate_sdt_note : Parse raw data and identify SDT note
> > + * @elf: elf of the opened file
> > + * @data: raw data of a section with description offset applied
> > + * @len: note description size
> > + * @type: type of the note
> > + * @sdt_notes: List to add the SDT note
> > + *
> > + * Responsible for parsing the @data in section .note.stapsdt in @elf and
> > + * if its an SDT note, it appends to @sdt_notes list.
> > + */
> > +static int populate_sdt_note(Elf **elf, const char *data, size_t len,
> > +			     struct list_head *sdt_notes)
> > +{
> > +	const char *provider, *name;
> > +	struct sdt_note *tmp = NULL;
> > +	GElf_Ehdr ehdr;
> > +	GElf_Addr base_off = 0;
> > +	GElf_Shdr shdr;
> > +	int ret = -EINVAL;
> > +
> > +	union {
> > +		Elf64_Addr a64[NR_ADDR];
> > +		Elf32_Addr a32[NR_ADDR];
> > +	} buf;
> > +
> > +	Elf_Data dst = {
> > +		.d_buf = &buf, .d_type = ELF_T_ADDR, .d_version = EV_CURRENT,
> > +		.d_size = gelf_fsize((*elf), ELF_T_ADDR, NR_ADDR, EV_CURRENT),
> > +		.d_off = 0, .d_align = 0
> > +	};
> > +	Elf_Data src = {
> > +		.d_buf = (void *) data, .d_type = ELF_T_ADDR,
> > +		.d_version = EV_CURRENT, .d_size = dst.d_size, .d_off = 0,
> > +		.d_align = 0
> > +	};
> > +
> > +	tmp = (struct sdt_note *)calloc(1, sizeof(struct sdt_note));
> > +	if (!tmp) {
> > +		ret = -ENOMEM;
> > +		goto out_err;
> > +	}
> > +
> > +	INIT_LIST_HEAD(&tmp->note_list);
> > +
> > +	if (len < dst.d_size + 3)
> > +		goto out_free_note;
> > +
> > +	/* Translation from file representation to memory representation */
> > +	if (gelf_xlatetom(*elf, &dst, &src,
> > +			  elf_getident(*elf, NULL)[EI_DATA]) == NULL) {
> > +		pr_err("gelf_xlatetom : %s\n", elf_errmsg(-1));
> > +		goto out_free_note;
> > +	}
> > +
> > +	/* Populate the fields of sdt_note */
> > +	provider = data + dst.d_size;
> > +
> > +	name = (const char *)memchr(provider, '\0', data + len - provider);
> > +	if (name++ == NULL)
> > +		goto out_free_note;
> > +
> > +	tmp->provider = strdup(provider);
> > +	if (!tmp->provider) {
> > +		ret = -ENOMEM;
> > +		goto out_free_note;
> > +	}
> > +	tmp->name = strdup(name);
> > +	if (!tmp->name) {
> > +		ret = -ENOMEM;
> > +		goto out_free_prov;
> > +	}
> > +
> > +	if (gelf_getclass(*elf) == ELFCLASS32) {
> > +		memcpy(&tmp->addr, &buf, 3 * sizeof(Elf32_Addr));
> > +		tmp->bit32 = true;
> > +	} else {
> > +		memcpy(&tmp->addr, &buf, 3 * sizeof(Elf64_Addr));
> > +		tmp->bit32 = false;
> > +	}
> > +
> > +	if (!gelf_getehdr(*elf, &ehdr)) {
> > +		pr_debug("%s : cannot get elf header.\n", __func__);
> > +		ret = -EBADF;
> > +		goto out_free_name;
> > +	}
> > +
> > +	/* Adjust the prelink effect :
> > +	 * Find out the .stapsdt.base section.
> > +	 * This scn will help us to handle prelinking (if present).
> > +	 * Compare the retrieved file offset of the base section with the
> > +	 * base address in the description of the SDT note. If its different,
> > +	 * then accordingly, adjust the note location.
> > +	 */
> > +	if (elf_section_by_name(*elf, &ehdr, &shdr, SDT_BASE_SCN, NULL)) {
> > +		base_off = shdr.sh_offset;
> > +		if (base_off) {
> > +			if (tmp->bit32)
> > +				tmp->addr.a32[0] = tmp->addr.a32[0] + base_off -
> > +					tmp->addr.a32[1];
> > +			else
> > +				tmp->addr.a64[0] = tmp->addr.a64[0] + base_off -
> > +					tmp->addr.a64[1];
> > +		}
> > +	}
> > +
> > +	list_add_tail(&tmp->note_list, sdt_notes);
> > +	return 0;
> > +
> > +out_free_name:
> > +	free(tmp->name);
> > +out_free_prov:
> > +	free(tmp->provider);
> > +out_free_note:
> > +	free(tmp);
> > +out_err:
> > +	return ret;
> > +}
> > +
> > +/**
> > + * construct_sdt_notes_list : constructs a list of SDT notes
> > + * @elf : elf to look into
> > + * @sdt_notes : empty list_head
> > + *
> > + * Scans the sections in 'elf' for the section
> > + * .note.stapsdt. It, then calls populate_sdt_note to find
> > + * out the SDT events and populates the 'sdt_notes'.
> > + */
> > +static int construct_sdt_notes_list(Elf *elf, struct list_head *sdt_notes)
> > +{
> > +	GElf_Ehdr ehdr;
> > +	Elf_Scn *scn = NULL;
> > +	Elf_Data *data;
> > +	GElf_Shdr shdr;
> > +	size_t shstrndx, next;
> > +	GElf_Nhdr nhdr;
> > +	size_t name_off, desc_off, offset;
> > +	int ret = 0;
> > +
> > +	if (gelf_getehdr(elf, &ehdr) == NULL) {
> > +		ret = -EBADF;
> > +		goto out_ret;
> > +	}
> > +	if (elf_getshdrstrndx(elf, &shstrndx) != 0) {
> > +		ret = -EBADF;
> > +		goto out_ret;
> > +	}
> > +
> > +	/* Look for the required section */
> > +	scn = elf_section_by_name(elf, &ehdr, &shdr, SDT_NOTE_SCN, NULL);
> > +	if (!scn) {
> > +		ret = -ENOENT;
> > +		goto out_ret;
> > +	}
> > +
> > +	if ((shdr.sh_type != SHT_NOTE) || (shdr.sh_flags & SHF_ALLOC)) {
> > +		ret = -ENOENT;
> > +		goto out_ret;
> > +	}
> > +
> > +	data = elf_getdata(scn, NULL);
> > +
> > +	/* Get the SDT notes */
> > +	for (offset = 0; (next = gelf_getnote(data, offset, &nhdr, &name_off,
> > +					      &desc_off)) > 0; offset = next) {
> > +		if (nhdr.n_namesz == sizeof(SDT_NOTE_NAME) &&
> > +		    !memcmp(data->d_buf + name_off, SDT_NOTE_NAME,
> > +			    sizeof(SDT_NOTE_NAME))) {
> > +			/* Check the type of the note */
> > +			if (nhdr.n_type != SDT_NOTE_TYPE)
> > +				goto out_ret;
> > +
> > +			ret = populate_sdt_note(&elf, ((data->d_buf) + desc_off),
> > +						nhdr.n_descsz, sdt_notes);
> > +			if (ret < 0)
> > +				goto out_ret;
> > +		}
> > +	}
> > +	if (list_empty(sdt_notes))
> > +		ret = -ENOENT;
> > +
> > +out_ret:
> > +	return ret;
> > +}
> > +
> > +/**
> > + * get_sdt_note_list : Wrapper to construct a list of sdt notes
> > + * @head : empty list_head
> > + * @target : file to find SDT notes from
> > + *
> > + * This opens the file, initializes
> > + * the ELF and then calls construct_sdt_notes_list.
> > + */
> > +int get_sdt_note_list(struct list_head *head, const char *target)
> > +{
> > +	Elf *elf;
> > +	int fd, ret;
> > +
> > +	fd = open(target, O_RDONLY);
> > +	if (fd < 0)
> > +		return -EBADF;
> > +
> > +	elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
> > +	if (!elf) {
> > +		ret = -EBADF;
> > +		goto out_close;
> > +	}
> > +	ret = construct_sdt_notes_list(elf, head);
> > +	elf_end(elf);
> > +out_close:
> > +	close(fd);
> > +	return ret;
> > +}
> > +
> > +/**
> > + * cleanup_sdt_note_list : free the sdt notes' list
> > + * @sdt_notes: sdt notes' list
> > + *
> > + * Free up the SDT notes in @sdt_notes.
> > + * Returns the number of SDT notes free'd.
> > + */
> > +int cleanup_sdt_note_list(struct list_head *sdt_notes)
> > +{
> > +	struct sdt_note *tmp, *pos;
> > +	int nr_free = 0;
> > +
> > +	list_for_each_entry_safe(pos, tmp, sdt_notes, note_list) {
> > +		list_del(&pos->note_list);
> > +		free(pos->name);
> > +		free(pos->provider);
> > +		free(pos);
> > +		nr_free++;
> > +	}
> > +	return nr_free;
> > +}
> > +
> > +/**
> > + * sdt_notes__get_count: Counts the number of sdt events
> > + * @start: list_head to sdt_notes list
> > + *
> > + * Returns the number of SDT notes in a list
> > + */
> > +int sdt_notes__get_count(struct list_head *start)
> > +{
> > +	struct sdt_note *sdt_ptr;
> > +	int count = 0;
> > +
> > +	list_for_each_entry(sdt_ptr, start, note_list)
> > +		count++;
> > +	return count;
> > +}
> > +
> >  void symbol__elf_init(void)
> >  {
> >  	elf_version(EV_CURRENT);
> > diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
> > index b10d558..699f7cb 100644
> > --- a/tools/perf/util/symbol.h
> > +++ b/tools/perf/util/symbol.h
> > @@ -342,4 +342,26 @@ void arch__sym_update(struct symbol *s, GElf_Sym *sym);
> >  
> >  int arch__choose_best_symbol(struct symbol *syma, struct symbol *symb);
> >  
> > +/* structure containing an SDT note's info */
> > +struct sdt_note {
> > +	char *name;			/* name of the note*/
> > +	char *provider;			/* provider name */
> > +	bool bit32;			/* whether the location is 32 bits? */
> > +	union {				/* location, base and semaphore addrs */
> > +		Elf64_Addr a64[3];
> > +		Elf32_Addr a32[3];
> > +	} addr;
> > +	struct list_head note_list;	/* SDT notes' list */
> > +};
> > +
> > +int get_sdt_note_list(struct list_head *head, const char *target);
> > +int cleanup_sdt_note_list(struct list_head *sdt_notes);
> > +int sdt_notes__get_count(struct list_head *start);
> > +
> > +#define SDT_BASE_SCN ".stapsdt.base"
> > +#define SDT_NOTE_SCN  ".note.stapsdt"
> > +#define SDT_NOTE_TYPE 3
> > +#define SDT_NOTE_NAME "stapsdt"
> > +#define NR_ADDR 3
> > +
> >  #endif /* __PERF_SYMBOL */


-- 
Masami Hiramatsu <mhiramat@...nel.org>

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ