Skip to content

Commit

Permalink
Rewrite ELF parsing from scratch w/o dependency of elf(3) and gelf(3)
Browse files Browse the repository at this point in the history
In order to make it portable on other Unix operating systems, use ELF
structure definitions and memory mapping to parse the ELF binary.
  • Loading branch information
sbz committed May 19, 2020
1 parent e65a04f commit 1960d10
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 59 deletions.
4 changes: 1 addition & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
83 changes: 83 additions & 0 deletions elf.c
Original file line number Diff line number Diff line change
@@ -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;
}
19 changes: 19 additions & 0 deletions elf.h
Original file line number Diff line number Diff line change
@@ -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 *);
15 changes: 9 additions & 6 deletions elfdbg.1
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,20 @@
.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
Expand Down
67 changes: 17 additions & 50 deletions elfdbg.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,79 +23,46 @@
* (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);
}

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);
}

0 comments on commit 1960d10

Please sign in to comment.