[04/19] libctf: low-level list manipulation and helper utilities

Message ID 20190430225706.159422-5-nick.alcock@oracle.com
State New
Headers show
Series
  • libctf, and CTF support for objdump and readelf
Related show

Commit Message

Nick Alcock April 30, 2019, 10:56 p.m.
These utilities are a bit of a ragbag of small things needed by more
than one TU: list manipulation, ELF32->64 translators, routines to look
up strings in string tables, dynamically-allocated string appenders, and
routines to set the specialized errno values previously committed in
<ctf-api.h>.

Notes for reviewers:

The distinction betweern this file and ctf-subr.c is lost in the mists
of time: perhaps it was originally that ctf-subr.c contained nothing but
wrappers.  I am quite amenable to combining the two, and also to
splitting the errno-setting stuff out into ctf-error.c with the error
fetchers.

libctf/
	* ctf-util.c: New file.
	* ctf-impl.h: Add definitions.
---
 libctf/ctf-impl.h |  32 +++++++++
 libctf/ctf-util.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 208 insertions(+)
 create mode 100644 libctf/ctf-util.c

-- 
2.21.0.237.gd0cfaa883d

Comments

Nick Clifton May 2, 2019, 4:04 p.m. | #1
Hi Nick,

> The distinction betweern this file and ctf-subr.c is lost in the mists

> of time: perhaps it was originally that ctf-subr.c contained nothing but

> wrappers.  I am quite amenable to combining the two, and also to

> splitting the errno-setting stuff out into ctf-error.c with the error

> fetchers.


Meh - whatever you want.  The distribution of the code within the library
is really just up to you.


> +#include <gelf.h>


This header is from the elfutils project right ?  Given that, and the fact
that you are using the types from this header, why are you submitting this
code to the binutils project ?  (Or maybe you are submitting it to both
projects - I have not checked).

In particular the BFD library has its own ELF reading and writing functions 
and its own headers defining the layout of ELF structures.  Unfortunately 
these headers do tend to conflict with the headers from the elfutils project, 
whoch makes combining them problematical.


> +/* Simple doubly-linked list append routine.  This implementation assumes that

> +   each list element contains an embedded ctf_list_t as the first member.

> +   An additional ctf_list_t is used to store the head (l_next) and tail

> +   (l_prev) pointers.  The current head and tail list elements have their

> +   previous and next pointers set to NULL, respectively.  */


You knows this kind of code seems awfully familiar.  I am sure that I have seen
it implemented in lots of different places... :-)


> +void

> +ctf_list_prepend (ctf_list_t * lp, void *new)


I think that using "new" here might be a problem if you try to compile this
source file with a C++ compiler.


> +const char *

> +ctf_strraw (ctf_file_t *fp, uint32_t name)

> +{

> +  ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID (name)];


My inner paranoia is screaming at code like this.  Unless you
are certain that these functions cannot be called with out of 
range parameters then I would strongly urge checking them before
using them.

Cheers
  Nick
Nick Alcock May 3, 2019, 7:24 p.m. | #2
On 2 May 2019, Nick Clifton said:

> Hi Nick,

>

>> +#include <gelf.h>

>

> This header is from the elfutils project right ?  Given that, and the fact

> that you are using the types from this header, why are you submitting this

> code to the binutils project ?  (Or maybe you are submitting it to both

> projects - I have not checked).

>

> In particular the BFD library has its own ELF reading and writing functions 

> and its own headers defining the layout of ELF structures.  Unfortunately 

> these headers do tend to conflict with the headers from the elfutils project, 

> whoch makes combining them problematical.


See my response to Joseph. This is basically because Solaris has
<gelf.h> easily available and used it promiscuously: we are only using a
few types that are always necessarily typedefs of types from <elf.h>,
but of course that's glibc-specific so I suppose we can't rely on that
either. But the binutils types seem... very far from ideal for my
purposes here, terribly bfd-specific.

Hence my suggestion (in an email that I hadn't written when you sent
this) that I could simply copy the necessary types from the installed
glibc headers into libctf. I don't know if that's too ugly to live.

>> +/* Simple doubly-linked list append routine.  This implementation assumes that

>> +   each list element contains an embedded ctf_list_t as the first member.

>> +   An additional ctf_list_t is used to store the head (l_next) and tail

>> +   (l_prev) pointers.  The current head and tail list elements have their

>> +   previous and next pointers set to NULL, respectively.  */

>

> You knows this kind of code seems awfully familiar.  I am sure that I have seen

> it implemented in lots of different places... :-)


Yes, but what else can we use? I tried using the stuff in <sys/queue.h>
and my eyes melted from all the CAPITAL LETTERS. :)

>> +void

>> +ctf_list_prepend (ctf_list_t * lp, void *new)

>

> I think that using "new" here might be a problem if you try to compile this

> source file with a C++ compiler.


True! I was only thinking in terms of using the headers with a C++
compiler... adjusted. However, there are a lot of other things we need
to fix up before libctf is C++-ready: implicit conversions to/from void
* are most of the problems, but we also use the %zi printf format in
several places...

>> +const char *

>> +ctf_strraw (ctf_file_t *fp, uint32_t name)

>> +{

>> +  ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID (name)];

>

> My inner paranoia is screaming at code like this.  Unless you

> are certain that these functions cannot be called with out of 

> range parameters then I would strongly urge checking them before

> using them.


Possibly a nicer fix than explicitly checking is to change CTF_NAME_STID
a bit to mask, from:

#define CTF_NAME_STID(name)		((name) >> 31)

to

#define CTF_NAME_STID(name)		(((name) >> 31) & 1)

That should mask out all but the bottom bit, ensuring that even if
someone manages to pass a 64-bit value with 30 1-bits to CTF_NAME_STID
(... which cannot happen in ctf_strraw() as presently constituted, but a
later change might break that assmuption), the result of CTF_NAME_STID()
is always either 0 or 1.  Thus it will always fit into ctf_str and never
overwrite anything.

... or is this just piling too-cleverness on top of too-cleverness?

(I've done that, provisionally, for v2.)

Patch

diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h
index 108c89d2c5..d4d71fbfdc 100644
--- a/libctf/ctf-impl.h
+++ b/libctf/ctf-impl.h
@@ -24,6 +24,13 @@ 
 #include <sys/errno.h>
 #include <ctf-api.h>
 #include <sys/types.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <limits.h>
+#include <ctype.h>
+#include <gelf.h>
 
 #ifdef	__cplusplus
 extern "C"
@@ -52,6 +59,25 @@  extern "C"
 
 #endif
 
+typedef struct ctf_list
+{
+  struct ctf_list *l_prev;	/* Previous pointer or tail pointer.  */
+  struct ctf_list *l_next;	/* Next pointer or head pointer.  */
+} ctf_list_t;
+
+#define	ctf_list_prev(elem)	((void *)(((ctf_list_t *)(elem))->l_prev))
+#define	ctf_list_next(elem)	((void *)(((ctf_list_t *)(elem))->l_next))
+
+extern void ctf_list_append (ctf_list_t *, void *);
+extern void ctf_list_prepend (ctf_list_t *, void *);
+extern void ctf_list_delete (ctf_list_t *, void *);
+
+extern const char *ctf_strraw (ctf_file_t *, uint32_t);
+extern const char *ctf_strptr (ctf_file_t *, uint32_t);
+
+extern ctf_file_t *ctf_set_open_errno (int *, int);
+extern long ctf_set_errno (ctf_file_t *, int);
+
 extern void *ctf_data_alloc (size_t);
 extern void ctf_data_free (void *, size_t);
 extern void ctf_data_protect (void *, size_t);
@@ -59,9 +85,15 @@  extern void ctf_data_protect (void *, size_t);
 extern void *ctf_alloc (size_t);
 extern void ctf_free (void *, size_t);
 
+extern char *ctf_strdup (const char *);
+extern char *ctf_str_append (char *, const char *);
+extern const char *ctf_strerror (int);
+
 _libctf_printflike_ (1, 2)
 extern void ctf_dprintf (const char *, ...);
 
+extern Elf64_Sym *ctf_sym_to_gelf (const Elf32_Sym *src, Elf64_Sym *dst);
+
 extern int _libctf_debug;	/* debugging messages enabled */
 
 #ifdef	__cplusplus
diff --git a/libctf/ctf-util.c b/libctf/ctf-util.c
new file mode 100644
index 0000000000..a3a8d05dd3
--- /dev/null
+++ b/libctf/ctf-util.c
@@ -0,0 +1,176 @@ 
+/* Miscellaneous utilities.
+   Copyright (C) 2005-2018 Free Software Foundation, Inc.
+
+   This file is part of libctf.
+
+   libctf 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 2, or (at your option) any later
+   version.
+
+   This program 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; see the file COPYING.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <ctf-impl.h>
+#include <string.h>
+
+/* Simple doubly-linked list append routine.  This implementation assumes that
+   each list element contains an embedded ctf_list_t as the first member.
+   An additional ctf_list_t is used to store the head (l_next) and tail
+   (l_prev) pointers.  The current head and tail list elements have their
+   previous and next pointers set to NULL, respectively.  */
+
+void
+ctf_list_append (ctf_list_t *lp, void *new)
+{
+  ctf_list_t *p = lp->l_prev;	/* p = tail list element.  */
+  ctf_list_t *q = new;		/* q = new list element.  */
+
+  lp->l_prev = q;
+  q->l_prev = p;
+  q->l_next = NULL;
+
+  if (p != NULL)
+    p->l_next = q;
+  else
+    lp->l_next = q;
+}
+
+/* Prepend the specified existing element to the given ctf_list_t.  The
+   existing pointer should be pointing at a struct with embedded ctf_list_t.  */
+
+void
+ctf_list_prepend (ctf_list_t * lp, void *new)
+{
+  ctf_list_t *p = new;		/* p = new list element.  */
+  ctf_list_t *q = lp->l_next;	/* q = head list element.  */
+
+  lp->l_next = p;
+  p->l_prev = NULL;
+  p->l_next = q;
+
+  if (q != NULL)
+    q->l_prev = p;
+  else
+    lp->l_prev = p;
+}
+
+/* Delete the specified existing element from the given ctf_list_t.  The
+   existing pointer should be pointing at a struct with embedded ctf_list_t.  */
+
+void
+ctf_list_delete (ctf_list_t *lp, void *existing)
+{
+  ctf_list_t *p = existing;
+
+  if (p->l_prev != NULL)
+    p->l_prev->l_next = p->l_next;
+  else
+    lp->l_next = p->l_next;
+
+  if (p->l_next != NULL)
+    p->l_next->l_prev = p->l_prev;
+  else
+    lp->l_prev = p->l_prev;
+}
+
+/* Convert a 32-bit ELF symbol into GElf (Elf64) and return a pointer to it.  */
+
+Elf64_Sym *
+ctf_sym_to_gelf (const Elf32_Sym *src, Elf64_Sym *dst)
+{
+  dst->st_name = src->st_name;
+  dst->st_value = src->st_value;
+  dst->st_size = src->st_size;
+  dst->st_info = src->st_info;
+  dst->st_other = src->st_other;
+  dst->st_shndx = src->st_shndx;
+
+  return dst;
+}
+
+/* Convert an encoded CTF string name into a pointer to a C string by looking
+  up the appropriate string table buffer and then adding the offset.  */
+
+const char *
+ctf_strraw (ctf_file_t *fp, uint32_t name)
+{
+  ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID (name)];
+
+  if (ctsp->cts_strs != NULL && CTF_NAME_OFFSET (name) < ctsp->cts_len)
+    return (ctsp->cts_strs + CTF_NAME_OFFSET (name));
+
+  /* String table not loaded or corrupt offset.  */
+  return NULL;
+}
+
+const char *
+ctf_strptr (ctf_file_t *fp, uint32_t name)
+{
+  const char *s = ctf_strraw (fp, name);
+  return (s != NULL ? s : "(?)");
+}
+
+/* Same as strdup(3C), but use ctf_alloc() to do the memory allocation. */
+
+char *
+ctf_strdup (const char *s1)
+{
+  char *s2 = ctf_alloc (strlen (s1) + 1);
+
+  if (s2 != NULL)
+    (void) strcpy (s2, s1);
+
+  return s2;
+}
+
+/* A string appender working on dynamic strings.  */
+
+char *
+ctf_str_append (char *s, const char *append)
+{
+  size_t s_len = 0;
+
+  if (append == NULL)
+    return s;
+
+  if (s != NULL)
+    s_len = strlen (s);
+
+  size_t append_len = strlen (append);
+
+  if ((s = realloc (s, s_len + append_len + 1)) == NULL)
+    return NULL;
+
+  memcpy (s + s_len, append, append_len);
+  s[s_len + append_len] = '\0';
+
+  return s;
+}
+
+/* Store the specified error code into errp if it is non-NULL, and then
+   return NULL for the benefit of the caller.  */
+
+ctf_file_t *
+ctf_set_open_errno (int *errp, int error)
+{
+  if (errp != NULL)
+    *errp = error;
+  return NULL;
+}
+
+/* Store the specified error code into the CTF container, and then return
+   CTF_ERR for the benefit of the caller. */
+
+long
+ctf_set_errno (ctf_file_t * fp, int err)
+{
+  fp->ctf_errno = err;
+  return CTF_ERR;
+}