Rewrite ELF parsing from scratch w/o dependency of elf(3) and gelf(3)
authorSofian Brabez <sbz@FreeBSD.org>
Tue, 19 May 2020 18:25:20 +0000 (20:25 +0200)
committerSofian Brabez <sbz@FreeBSD.org>
Tue, 19 May 2020 18:25:20 +0000 (20:25 +0200)
In order to make it portable on other Unix operating systems, use ELF
structure definitions and memory mapping to parse the ELF binary.

Makefile
README.md
elf.c [new file with mode: 0644]
elf.h [new file with mode: 0644]
elfdbg.1
elfdbg.c

index 1d4c2c68a1c6c49ee7540ab7f8753d099812a7e2..8d46f2f4f4e456fdec7c4db82c4ffc101f47ee1e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -5,9 +5,7 @@ BINDIR= ${PREFIX}/bin
 
 PROG=  elfdbg
 MAN=   elfdbg.1
-SRCS=  elfdbg.c
-
-LDADD= -lelf
+SRCS=  elfdbg.c elf.c
 
 test:
        @(cd tests && kyua test && kyua report)
index c317f29bc20e4709d44999c8a4cefc9addb0290e..78a5e2d86e1cf219a6e35fa5e2daede9646f141a 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1 +1,43 @@
 # elfdbg
+
+## About
+
+This project _elfdbg_ is a utility program to quickly identify if an ELF binary
+is built with [debug sections](https://en.wikipedia.org/wiki/Debug_symbol)
+
+Debug sections in ELF binary usually are generated using the `-g` flag with the
+compiler. The compiler adds the new `.debug_*` sections in the binary.
+
+The program is looking for the existence of sections with name starting with
+`.debug_` to determine if the binary has been built with debug information.
+
+The [ELF][ELF] format is a well-known standard. ELF TIS reference specification
+is available [here][spec] and as a FreeBSD [elf(5)][man] man page.
+
+## Build and install
+
+The Makefile use the standard BSDMakefile to build the program.
+
+```
+make && sudo make install
+```
+
+## Test
+
+The tests cases are implemented using the FreeBSD test suite framework with
+[kyua](https://github.com/jmmv/kyua) and [Kyuafile](./tests/Kyuafile).
+
+```
+make test
+```
+
+## History
+
+* _2015_ I wrote this using libelf elf(3) and gelf(3) API
+  [f4b470b](https://github.com/sbz/elfdbg/commit/f4b470b)
+* _2020_ I rewrote this without relying on libelf API
+  [96010ce](https://github.com/sbz/eldbg/commit/96010ce)
+
+[ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
+[spec]: http://refspecs.linuxbase.org/elf/elf.pdf
+[man]: https://www.freebsd.org/cgi/man.cgi?query=elf&sektion=5
diff --git a/elf.c b/elf.c
new file mode 100644 (file)
index 0000000..5ee633b
--- /dev/null
+++ b/elf.c
@@ -0,0 +1,83 @@
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "elf.h"
+
+
+Elf_Obj *
+elf_init(const char *filename)
+{
+    int fd;
+    struct stat sb;
+    Elf_Obj *e;
+
+    fd = open(filename, O_RDONLY);
+    e = (Elf_Obj *)malloc(sizeof(Elf_Obj));
+    e->fd = fd;
+    fstat(fd, &sb);
+    e->sb = sb;
+
+    e->mm = mmap(NULL, sb.st_size, PROT_READ,  MAP_SHARED, fd, 0);
+    e->ehdr = (Elf_Ehdr *) e->mm;
+    e->ehdr_size = e->ehdr->e_ehsize;
+
+    e->shdr = (Elf_Shdr *) (e->mm + e->ehdr->e_shoff);
+    e->shdr_size = e->ehdr->e_shnum;
+
+    e->strtab = NULL;
+    e->strtab_size = 0;
+
+    return e;
+}
+
+Elf_Shdr *
+elf_strtab(Elf_Obj *e)
+{
+    Elf_Shdr *ptr;
+
+    ptr = &e->shdr[e->ehdr->e_shstrndx];
+    e->strtab = malloc(ptr->sh_size);
+    e->strtab_size = ptr->sh_size;
+
+    memcpy(e->strtab, (e->mm + ptr->sh_offset), ptr->sh_size);
+
+    return ptr;
+}
+
+int
+elf_destroy(Elf_Obj *e)
+{
+    if (e == NULL)
+        return 1;
+
+    munmap(e->mm, e->sb.st_size);
+    close(e->fd);
+    if (e->strtab != NULL)
+        free(e->strtab);
+    free(e);
+
+    return 0;
+}
+
+int
+elf_debug(Elf_Obj *e) 
+{
+    int i, has_debug = 0;
+    char *debug_prefix = ".debug_";
+    char *section_name = NULL;
+
+    for (i=0; i < e->shdr_size; i++) {
+        section_name = &e->strtab[e->shdr[i].sh_name];
+        if (strnstr(section_name, debug_prefix, strlen(debug_prefix))) {
+            //printf("%s\n", section_name);
+            has_debug++;
+        }
+
+    }
+
+    return has_debug;
+}
diff --git a/elf.h b/elf.h
new file mode 100644 (file)
index 0000000..fa8c6b1
--- /dev/null
+++ b/elf.h
@@ -0,0 +1,19 @@
+#include <elf.h>
+#include <sys/stat.h>
+
+typedef struct Elf_Obj {
+    int         fd;
+    struct stat sb;
+    char        *mm;
+    char        *strtab;
+    size_t      strtab_size;
+    Elf_Ehdr    *ehdr;
+    size_t      ehdr_size;
+    Elf_Shdr    *shdr;
+    size_t      shdr_size;
+} Elf_Obj;
+
+Elf_Obj *elf_init(const char *filename);
+int elf_destroy(Elf_Obj *);
+Elf_Shdr *elf_strtab(Elf_Obj *);
+int elf_debug(Elf_Obj *);
index ab99e1f1985ba49f7b1fe7ac4ed04ad6e68c6c1f..5f5f98d1b20e38455d9e2a41fc023d028bf9bf05 100644 (file)
--- a/elfdbg.1
+++ b/elfdbg.1
 .Nd analyze if binary is compiled with debug sections
 .Sh SYNOPSIS
 .Nm
-.Ar file
+.Ar [-qv] file
 .Sh DESCRIPTION
 The
 .Nm
-utility reports if a binary is compiled with debug sections, commonly
-known as 
-.Ar -g 
+utility reports if a binary is compiled with debug sections commonly generated
+with the
+.Ar -g
 compiler toolchain flag.
+
+The program lookup for the sections name starting with
+.Ar .debug_
+in order to determine if he has debug information.
 .Sh SEE ALSO
-.Xr elf 3 , 
-.Xr gelf 3
+.Xr elf 5
 .Sh AUTHORS
 .An -nosplit
 The
index 4c219773e6ccefded5caa7323163e26ff25d5fc5..ca6686df3a30b6f482abbb1e062b2a6167d35467 100644 (file)
--- a/elfdbg.c
+++ b/elfdbg.c
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-#include <err.h>
-#include <fcntl.h>
-#include <gelf.h>
-#include <stdint.h>
-#include <string.h>
+#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sysexits.h>
 #include <unistd.h>
 
-extern char *__progname;
-
-static int
-elf_debug_sections(Elf *e)
-{
-       Elf_Scn *scn = NULL;
-       GElf_Shdr shdr;
-       size_t n, shstrndx, sz;
-       char *name;
-       int has_debug = 0;
-
-       if (elf_getshdrstrndx(e, &shstrndx) != 0)
-               errx(EX_SOFTWARE, "elf_getshdrstrndx() failed : %s . ",
-                   elf_errmsg(-1));
+#include "elf.h"
 
-       while ((scn = elf_nextscn(e, scn)) != NULL) {
-               gelf_getshdr(scn, &shdr);
-
-               name = elf_strptr(e, shstrndx, shdr.sh_name);
-               if (!strstr(name, "debug_"))
-                       continue;
-
-               has_debug++;
-       }
-
-       return (has_debug > 0);
-}
+extern char *__progname;
 
 static void
-usage(void) {
+usage(void)
+{
        fprintf(stderr, "Usage: %s file\n", __progname);
        exit(EX_USAGE);
 }
@@ -70,32 +44,25 @@ usage(void) {
 int
 main(int argc, char *argv[])
 {
-       const char *filename;
-       int fd, rc;
-       Elf *e;
-       int has_debug;
+       int rc, has_debug;
+       Elf_Obj *e = NULL;
+       Elf_Shdr *shstr = NULL;
 
        if (argc == 1)
                usage();
 
-       filename = argv[1];
+       /* load elf binary in memory */
+       e = elf_init(argv[1]);
 
-       if (elf_version(EV_CURRENT) == EV_NONE)
-               errx(EX_SOFTWARE, "ELF library initialization failed : %s ",
-                   elf_errmsg(-1));
+       /* load string stable */
+       shstr = elf_strtab(e);
 
-       if ((fd = open(filename, O_RDONLY, 0)) < 0)
-               err(EX_NOINPUT, "open %s failed ", filename);
-       if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
-               errx(EX_SOFTWARE, "elf_begin() failed : %s", elf_errmsg(-1));
-       if (elf_kind(e) != ELF_K_ELF)
-               errx(EX_DATAERR, "%s is not an ELF object", filename);
+       /* search for sections name with debug prefix */
+       has_debug = elf_debug(e);
 
-       has_debug = elf_debug_sections(e);
-       printf(has_debug ? "HAS DEBUG\n" : "NO DEBUG\n");
+       printf("%s\n", (has_debug > 0) ? "HAS DEBUG" : "NO DEBUG");
 
-       rc = close(fd);
-       rc = elf_end(e);
+       rc = elf_destroy(e);
 
        return (rc);
 }